From 565a739ff69a70a3e70259783c0e29e57a501940 Mon Sep 17 00:00:00 2001 From: Hugo Saporetti Junior Date: Mon, 23 Sep 2024 21:29:04 -0300 Subject: [PATCH] Adjust the Terminal.shell_exec due to changes on it --- dependencies.hspd | 2 +- docs/api/askai/main/askai.html | 6 +- docs/api/askai/main/askai/core.html | 6 +- docs/api/askai/main/askai/core/askai.html | 520 ++++--- docs/api/askai/main/askai/core/askai_cli.html | 733 +++++----- .../askai/main/askai/core/askai_configs.html | 549 +++++--- .../askai/main/askai/core/askai_events.html | 184 +-- .../askai/main/askai/core/askai_messages.html | 972 +++++++------ .../askai/main/askai/core/askai_prompt.html | 256 ++-- .../askai/main/askai/core/askai_settings.html | 854 +++++++----- docs/api/askai/main/askai/core/commander.html | 6 +- .../main/askai/core/commander/commander.html | 1046 ++++++++------ .../main/askai/core/commander/commands.html | 6 +- .../core/commander/commands/cache_cmd.html | 542 ++++---- .../core/commander/commands/camera_cmd.html | 276 ++-- .../core/commander/commands/general_cmd.html | 392 +++--- .../core/commander/commands/history_cmd.html | 58 +- .../core/commander/commands/settings_cmd.html | 223 +-- .../core/commander/commands/tts_stt_cmd.html | 635 +++++---- docs/api/askai/main/askai/core/component.html | 12 +- .../askai/core/component/audio_player.html | 495 ++++--- .../askai/core/component/cache_service.html | 746 +++++----- .../main/askai/core/component/camera.html | 1209 +++++++++-------- .../askai/core/component/geo_location.html | 396 +++--- .../askai/core/component/image_store.html | 1017 ++++++++------ .../core/component/internet_service.html | 899 ++++++------ .../main/askai/core/component/recorder.html | 1119 ++++++++------- .../main/askai/core/component/scheduler.html | 1196 +++++++++++----- .../main/askai/core/component/summarizer.html | 719 +++++----- .../askai/core/component/text_streamer.html | 615 +++++++++ docs/api/askai/main/askai/core/engine.html | 10 +- .../main/askai/core/engine/ai_engine.html | 803 ++++++----- .../main/askai/core/engine/ai_model.html | 62 +- .../engine/{ai_reply.html => ai_vision.html} | 182 ++- .../askai/core/engine/engine_factory.html | 128 +- .../askai/main/askai/core/engine/openai.html | 12 +- .../core/engine/openai/openai_configs.html | 213 ++- .../core/engine/openai/openai_engine.html | 1117 +++++++++------ .../core/engine/openai/openai_model.html | 320 ++--- .../core/engine/openai/openai_vision.html | 616 +++++++++ .../askai/core/engine/openai/temperature.html | 245 ++-- docs/api/askai/main/askai/core/enums.html | 12 +- .../main/askai/core/enums/acc_response.html | 568 +++++--- .../main/askai/core/enums/rag_response.html | 688 ---------- .../main/askai/core/enums/router_mode.html | 313 +++-- .../main/askai/core/enums/routing_model.html | 457 ++++--- .../vision.html => enums/verbosity.html} | 350 +++-- docs/api/askai/main/askai/core/features.html | 16 +- .../{validation.html => processors.html} | 30 +- .../{router => processors}/ai_processor.html | 70 +- .../{router/procs => processors}/qna.html | 14 +- .../{router/procs => processors}/qstring.html | 69 +- .../{router/procs => processors}/rag.html | 206 +-- .../features/processors/task_splitter.html | 898 ++++++++++++ .../askai/main/askai/core/features/rag.html | 254 ---- .../main/askai/core/features/rag/rag.html | 447 ------ .../main/askai/core/features/router.html | 22 +- .../core/features/router/model_selector.html | 195 +-- .../features/router/procs/task_splitter.html | 656 --------- .../task_accuracy.html} | 359 ++--- .../core/features/router/task_agent.html | 682 ++++------ .../core/features/router/task_splitter.html | 658 --------- .../core/features/router/task_toolkit.html | 1114 +++++++++------ .../core/features/{router => }/tools.html | 26 +- .../features/{router => }/tools/analysis.html | 125 +- .../features/{router => }/tools/browser.html | 16 +- .../features/{router => }/tools/general.html | 98 +- .../{router => }/tools/generation.html | 16 +- .../{router => }/tools/summarization.html | 12 +- .../features/{router => }/tools/terminal.html | 383 +++--- .../askai/core/features/tools/vision.html | 717 ++++++++++ .../askai/core/features/tools/webcam.html | 427 ++++++ docs/api/askai/main/askai/core/model.html | 20 +- .../main/askai/core/model/action_plan.html | 490 ++++--- .../askai/main/askai/core/model/ai_model.html | 409 ------ .../askai/main/askai/core/model/ai_reply.html | 549 +++++++- .../askai/main/askai/core/model/api_keys.html | 582 +++++--- .../procs.html => model/image_result.html} | 265 +++- .../main/askai/core/model/model_result.html | 39 +- .../main/askai/core/model/search_result.html | 60 +- .../main/askai/core/model/summary_result.html | 48 +- docs/api/askai/main/askai/core/support.html | 16 +- .../main/askai/core/support/chat_context.html | 1010 +++++++++----- .../askai/core/support/langchain_support.html | 212 ++- .../askai/core/support/object_mapper.html | 784 ----------- .../main/askai/core/support/platform.html | 94 +- .../main/askai/core/support/presets.html | 526 +++---- .../main/askai/core/support/rag_provider.html | 466 +++++++ .../askai/core/support/shared_instances.html | 828 ++++++----- .../askai/core/support/text_formatter.html | 698 ++++++---- .../main/askai/core/support/utilities.html | 900 ++++++------ docs/api/askai/main/askai/exception.html | 6 +- .../main/askai/exception/exceptions.html | 48 +- docs/api/askai/main/askai/language.html | 6 +- .../main/askai/language/ai_translator.html | 130 +- .../askai/main/askai/language/language.html | 544 ++++---- .../main/askai/language/translators.html | 18 +- .../askai/language/translators/argos.html | 525 ------- .../{ => translators}/argos_translator.html | 375 ++--- .../{deep.html => deepl_translator.html} | 258 ++-- .../{marian.html => marian_translator.html} | 252 ++-- docs/api/askai/main/askai/tui.html | 6 +- docs/api/askai/main/askai/tui/app_header.html | 31 +- docs/api/askai/main/askai/tui/app_icons.html | 6 +- .../askai/main/askai/tui/app_suggester.html | 2 +- .../api/askai/main/askai/tui/app_widgets.html | 45 +- docs/api/askai/main/askai/tui/askai_app.html | 1094 ++++++++------- docs/api/askai/search.js | 2 +- src/demo/__init__.py | 9 +- src/demo/components/__init__.py | 12 +- src/demo/features/__init__.py | 8 +- src/demo/features/rag/__init__.py | 6 +- src/demo/features/tools/__init__.py | 6 +- src/demo/others/__init__.py | 11 +- src/demo/others/tech_week_demo.py | 16 +- src/main/__init__.py | 6 +- src/main/askai/__init__.py | 9 +- src/main/askai/core/__init__.py | 30 +- src/main/askai/core/askai_messages.py | 4 +- src/main/askai/core/commander/__init__.py | 7 +- .../askai/core/commander/commands/__init__.py | 11 +- .../core/commander/commands/general_cmd.py | 4 +- src/main/askai/core/component/__init__.py | 21 +- src/main/askai/core/component/audio_player.py | 10 +- src/main/askai/core/engine/__init__.py | 10 +- src/main/askai/core/engine/openai/__init__.py | 10 +- src/main/askai/core/enums/__init__.py | 9 +- src/main/askai/core/features/__init__.py | 8 +- .../core/features/processors/__init__.py | 12 +- .../askai/core/features/router/__init__.py | 9 +- .../askai/core/features/tools/__init__.py | 15 +- .../askai/core/features/tools/terminal.py | 4 +- src/main/askai/core/model/__init__.py | 12 +- src/main/askai/core/support/__init__.py | 18 +- src/main/askai/core/support/chat_context.py | 2 +- src/main/askai/exception/__init__.py | 6 +- src/main/askai/language/__init__.py | 8 +- .../askai/language/translators/__init__.py | 8 +- src/main/askai/tui/__init__.py | 10 +- src/main/requirements.txt | 2 +- src/test/__init__.py | 7 +- src/test/core/__init__.py | 6 +- src/test/core/support/__init__.py | 6 +- 143 files changed, 22734 insertions(+), 18237 deletions(-) create mode 100644 docs/api/askai/main/askai/core/component/text_streamer.html rename docs/api/askai/main/askai/core/engine/{ai_reply.html => ai_vision.html} (70%) create mode 100644 docs/api/askai/main/askai/core/engine/openai/openai_vision.html delete mode 100644 docs/api/askai/main/askai/core/enums/rag_response.html rename docs/api/askai/main/askai/core/{features/router/tools/vision.html => enums/verbosity.html} (64%) rename docs/api/askai/main/askai/core/features/{validation.html => processors.html} (96%) rename docs/api/askai/main/askai/core/features/{router => processors}/ai_processor.html (91%) rename docs/api/askai/main/askai/core/features/{router/procs => processors}/qna.html (98%) rename docs/api/askai/main/askai/core/features/{router/procs => processors}/qstring.html (95%) rename docs/api/askai/main/askai/core/features/{router/procs => processors}/rag.html (95%) create mode 100644 docs/api/askai/main/askai/core/features/processors/task_splitter.html delete mode 100644 docs/api/askai/main/askai/core/features/rag.html delete mode 100644 docs/api/askai/main/askai/core/features/rag/rag.html delete mode 100644 docs/api/askai/main/askai/core/features/router/procs/task_splitter.html rename docs/api/askai/main/askai/core/features/{validation/accuracy.html => router/task_accuracy.html} (79%) delete mode 100644 docs/api/askai/main/askai/core/features/router/task_splitter.html rename docs/api/askai/main/askai/core/features/{router => }/tools.html (97%) rename docs/api/askai/main/askai/core/features/{router => }/tools/analysis.html (93%) rename docs/api/askai/main/askai/core/features/{router => }/tools/browser.html (98%) rename docs/api/askai/main/askai/core/features/{router => }/tools/general.html (92%) rename docs/api/askai/main/askai/core/features/{router => }/tools/generation.html (97%) rename docs/api/askai/main/askai/core/features/{router => }/tools/summarization.html (98%) rename docs/api/askai/main/askai/core/features/{router => }/tools/terminal.html (90%) create mode 100644 docs/api/askai/main/askai/core/features/tools/vision.html create mode 100644 docs/api/askai/main/askai/core/features/tools/webcam.html delete mode 100644 docs/api/askai/main/askai/core/model/ai_model.html rename docs/api/askai/main/askai/core/{features/router/procs.html => model/image_result.html} (65%) delete mode 100644 docs/api/askai/main/askai/core/support/object_mapper.html create mode 100644 docs/api/askai/main/askai/core/support/rag_provider.html delete mode 100644 docs/api/askai/main/askai/language/translators/argos.html rename docs/api/askai/main/askai/language/{ => translators}/argos_translator.html (75%) rename docs/api/askai/main/askai/language/translators/{deep.html => deepl_translator.html} (79%) rename docs/api/askai/main/askai/language/translators/{marian.html => marian_translator.html} (72%) diff --git a/dependencies.hspd b/dependencies.hspd index 260cc3b6..3e158cb3 100644 --- a/dependencies.hspd +++ b/dependencies.hspd @@ -9,7 +9,7 @@ /* HSPyLib projects */ package: hspylib, version: 1.12.49, mode: ge -package: hspylib-clitt, version: 0.9.133, mode: ge +package: hspylib-clitt, version: 0.9.135, mode: ge package: hspylib-setman, version: 0.10.35, mode: ge /* General */ diff --git a/docs/api/askai/main/askai.html b/docs/api/askai/main/askai.html index 59784462..0b12a3d3 100644 --- a/docs/api/askai/main/askai.html +++ b/docs/api/askai/main/askai.html @@ -3,7 +3,7 @@ - + main.askai API documentation @@ -53,7 +53,7 @@

 1# _*_ coding: utf-8 _*_
  2#
- 3# hspylib-askai v1.0.11
+ 3# hspylib-askai v1.0.13
  4#
  5# Package: main.askai
  6"""Package initialization."""
@@ -64,7 +64,7 @@ 

11 'language', 12 'tui' 13] -14__version__ = '1.0.11' +14__version__ = '1.0.13'

diff --git a/docs/api/askai/main/askai/core.html b/docs/api/askai/main/askai/core.html index e18f64f1..b38faf84 100644 --- a/docs/api/askai/main/askai/core.html +++ b/docs/api/askai/main/askai/core.html @@ -3,7 +3,7 @@ - + main.askai.core API documentation @@ -68,7 +68,7 @@

 1# _*_ coding: utf-8 _*_
  2#
- 3# hspylib-askai v1.0.11
+ 3# hspylib-askai v1.0.13
  4#
  5# Package: main.askai.core
  6"""Package initialization."""
@@ -89,7 +89,7 @@ 

21 'model', 22 'support' 23] -24__version__ = '1.0.11' +24__version__ = '1.0.13'

diff --git a/docs/api/askai/main/askai/core/askai.html b/docs/api/askai/main/askai/core/askai.html index 6a5015bb..7ac79dc8 100644 --- a/docs/api/askai/main/askai/core/askai.html +++ b/docs/api/askai/main/askai/core/askai.html @@ -3,7 +3,7 @@ - + main.askai.core.askai API documentation @@ -138,53 +138,53 @@

12 13 Copyright (c) 2024, HomeSetup 14""" - 15import logging as log - 16import os - 17import re - 18import sys - 19from enum import Enum - 20from pathlib import Path - 21from typing import List, TypeAlias, Optional - 22 - 23from click import UsageError - 24from hspylib.core.enums.charset import Charset - 25from hspylib.core.tools.commons import is_debugging, file_is_not_empty - 26from hspylib.core.zoned_datetime import now, DATE_FORMAT, TIME_FORMAT - 27from hspylib.modules.application.exit_status import ExitStatus - 28from hspylib.modules.eventbus.event import Event - 29from openai import RateLimitError - 30 - 31from askai.__classpath__ import classpath - 32from askai.core.askai_configs import configs - 33from askai.core.askai_events import events - 34from askai.core.askai_messages import msg - 35from askai.core.askai_settings import settings - 36from askai.core.commander.commander import ask_commander, RE_ASKAI_CMD - 37from askai.core.component.cache_service import CACHE_DIR, cache - 38from askai.core.engine.ai_engine import AIEngine - 39from askai.core.enums.router_mode import RouterMode - 40from askai.core.features.router.ai_processor import AIProcessor - 41from askai.core.support.chat_context import ChatContext - 42from askai.core.support.shared_instances import shared - 43from askai.core.support.utilities import read_stdin - 44from askai.exception.exceptions import ImpossibleQuery, MaxInteractionsReached, InaccurateResponse, \ - 45 IntelligibleAudioError, TerminatingQuery - 46from askai.tui.app_icons import AppIcons - 47 - 48QueryString: TypeAlias = str | List[str] | None + 15from askai.__classpath__ import classpath + 16from askai.core.askai_configs import configs + 17from askai.core.askai_events import events + 18from askai.core.askai_messages import msg + 19from askai.core.askai_settings import settings + 20from askai.core.commander.commander import ask_commander, RE_ASKAI_CMD + 21from askai.core.component.cache_service import cache, CACHE_DIR + 22from askai.core.engine.ai_engine import AIEngine + 23from askai.core.enums.router_mode import RouterMode + 24from askai.core.features.processors.ai_processor import AIProcessor + 25from askai.core.model.ai_reply import AIReply + 26from askai.core.support.chat_context import ChatContext + 27from askai.core.support.shared_instances import shared + 28from askai.core.support.utilities import read_stdin + 29from askai.exception.exceptions import (ImpossibleQuery, InaccurateResponse, IntelligibleAudioError, + 30 MaxInteractionsReached, TerminatingQuery) + 31from askai.tui.app_icons import AppIcons + 32from click import UsageError + 33from enum import Enum + 34from hspylib.core.enums.charset import Charset + 35from hspylib.core.tools.commons import file_is_not_empty, is_debugging + 36from hspylib.core.zoned_datetime import DATE_FORMAT, now, TIME_FORMAT + 37from hspylib.modules.application.exit_status import ExitStatus + 38from openai import RateLimitError + 39from pathlib import Path + 40from typing import List, Optional, TypeAlias + 41 + 42import logging as log + 43import os + 44import re + 45import sys + 46 + 47QueryString: TypeAlias = str | List[str] | None + 48 49 - 50 - 51class AskAi: - 52 """The AskAI core functionalities.""" - 53 - 54 SOURCE_DIR: Path = classpath.source_path() - 55 - 56 RESOURCE_DIR: Path = classpath.resource_path() - 57 - 58 SPLASH: str = classpath.get_resource("splash.txt").read_text(encoding=Charset.UTF_8.val) - 59 - 60 class RunModes(Enum): - 61 """AskAI run modes""" + 50class AskAi: + 51 """The AskAI core functionalities.""" + 52 + 53 SOURCE_DIR: Path = classpath.source_path() + 54 + 55 RESOURCE_DIR: Path = classpath.resource_path() + 56 + 57 SPLASH: str = classpath.get_resource("splash.txt").read_text(encoding=Charset.UTF_8.val) + 58 + 59 class RunModes(Enum): + 60 """AskAI run modes""" + 61 62 ASKAI_TUI = "ASKAI_TUI" # Interactive Terminal UI. 63 ASKAI_CLI = "ASKAI_CLI" # Interactive CLI. 64 ASKAI_CMD = "ASKAI_CMD" # Non interactive CLI (Command mode). @@ -216,9 +216,9 @@

90 self._session_id = now("%Y%m%d")[:8] 91 self._engine: AIEngine = shared.create_engine(engine_name, model_name) 92 self._context: ChatContext = shared.create_context(self._engine.ai_token_limit()) - 93 self._mode: RouterMode = RouterMode.default() - 94 self._console_path = Path(f"{CACHE_DIR}/askai-{self.session_id}.md") - 95 self._query_prompt: str | None = None + 93 self._console_path = Path(f"{CACHE_DIR}/askai-{self.session_id}.md") + 94 self._query_prompt: str | None = None + 95 self._mode: RouterMode = RouterMode.default() 96 97 if not self._console_path.exists(): 98 self._console_path.touch() @@ -264,85 +264,74 @@

138 ... 139 140 def ask_and_reply(self, question: str) -> tuple[bool, Optional[str]]: -141 """Ask the question to the AI, and provide the reply. -142 :param question: The question to ask to the AI engine. -143 """ -144 status: bool = True -145 output: str | None = None -146 processor: AIProcessor = self.mode.processor -147 assert isinstance(processor, AIProcessor) -148 -149 try: -150 if command := re.search(RE_ASKAI_CMD, question): -151 args: list[str] = list( -152 filter(lambda a: a and a != "None", re.split(r"\s", f"{command.group(1)} {command.group(2)}"))) -153 ask_commander(args, standalone_mode=False) -154 elif not (output := cache.read_reply(question)): -155 log.debug('Response not found for "%s" in cache. Querying from %s.', question, self.engine.nickname()) -156 events.reply.emit(message=msg.wait(), verbosity="debug") -157 output = processor.process(question, context=read_stdin(), query_prompt=self._query_prompt) -158 events.reply.emit(message=(output or msg.no_output("processor"))) -159 else: -160 log.debug("Reply found for '%s' in cache.", question) -161 events.reply.emit(message=output) -162 shared.context.push("HISTORY", question) -163 shared.context.push("HISTORY", output, "assistant") -164 except (NotImplementedError, ImpossibleQuery) as err: -165 events.reply_error.emit(message=str(err)) -166 except (MaxInteractionsReached, InaccurateResponse) as err: -167 events.reply_error.emit(message=msg.unprocessable(str(err))) -168 except UsageError as err: -169 events.reply_error.emit(message=msg.invalid_command(err)) -170 except IntelligibleAudioError as err: -171 events.reply_error.emit(message=msg.intelligible(err)) -172 except RateLimitError: -173 events.reply_error.emit(message=msg.quote_exceeded()) -174 status = False -175 except TerminatingQuery: +141 """Ask the specified question to the AI and provide the reply. +142 :param question: The question to ask the AI engine. +143 :return: A tuple containing a boolean indicating success or failure, and the AI's reply as an optional string. +144 """ +145 status: bool = True +146 output: str | None = None +147 processor: AIProcessor = self.mode.processor +148 assert isinstance(processor, AIProcessor) +149 +150 try: +151 if command := re.search(RE_ASKAI_CMD, question): +152 args: list[str] = list( +153 filter(lambda a: a and a != "None", re.split(r"\s", f"{command.group(1)} {command.group(2)}")) +154 ) +155 ask_commander(args, standalone_mode=False) +156 elif not (output := cache.read_reply(question)): +157 log.debug('Response not found for "%s" in cache. Querying from %s.', question, self.engine.nickname()) +158 events.reply.emit(reply=AIReply.detailed(msg.wait())) +159 output = processor.process(question, context=read_stdin(), query_prompt=self._query_prompt) +160 events.reply.emit(reply=AIReply.info(output or msg.no_output("processor"))) +161 else: +162 log.debug("Reply found for '%s' in cache.", question) +163 events.reply.emit(reply=AIReply.info(output)) +164 shared.context.push("HISTORY", question) +165 shared.context.push("HISTORY", output, "assistant") +166 except (NotImplementedError, ImpossibleQuery) as err: +167 events.reply.emit(reply=AIReply.error(err)) +168 except (MaxInteractionsReached, InaccurateResponse) as err: +169 events.reply.emit(reply=AIReply.error(msg.unprocessable(err))) +170 except UsageError as err: +171 events.reply.emit(reply=AIReply.error(msg.invalid_command(err))) +172 except IntelligibleAudioError as err: +173 events.reply.emit(reply=AIReply.error(msg.intelligible(err))) +174 except RateLimitError: +175 events.reply.emit(reply=AIReply.error(msg.quote_exceeded())) 176 status = False -177 finally: -178 if output: -179 shared.context.set("LAST_REPLY", output) -180 -181 return status, output +177 except TerminatingQuery: +178 status = False +179 finally: +180 if output: +181 shared.context.set("LAST_REPLY", output) 182 -183 def _create_console_file(self, overwrite: bool = True): -184 """Create the Markdown formatted console file. -185 :param overwrite: Whether to overwrite the file or not. -186 """ -187 is_new: bool = not file_is_not_empty(str(self.console_path)) or overwrite -188 with open(self.console_path, "w" if overwrite else "a", encoding=Charset.UTF_8.val) as f_console: -189 f_console.write( -190 f"{'---' + os.linesep * 2 if not is_new else ''}" -191 f"{'# ' + now(DATE_FORMAT) + os.linesep * 2 if is_new else ''}" -192 f"## {AppIcons.STARTED} {now(TIME_FORMAT)}\n\n" -193 ) -194 f_console.flush() -195 -196 def _reply(self, message: str) -> None: -197 """Reply to the user with the AI response. -198 :param message: The message to reply to the user. -199 """ -200 ... -201 -202 def _reply_error(self, message: str) -> None: -203 """Reply API or system errors. -204 :param message: The error message to be displayed. -205 """ -206 ... -207 -208 def _cb_mode_changed_event(self, ev: Event) -> None: -209 """Callback to handle mode changed events. -210 :param ev: The mode changed event. -211 """ -212 self._mode: RouterMode = RouterMode.of_name(ev.args.mode) -213 if not self._mode.is_default: -214 events.reply.emit( -215 f"{msg.enter_qna()} \n" -216 f"```\nContext:  {ev.args.sum_path},  {ev.args.glob} \n```\n" -217 f"`{msg.press_esc_enter()}` \n\n" -218 f"> {msg.qna_welcome()}" -219 ) +183 return status, output +184 +185 def _create_console_file(self, overwrite: bool = True) -> None: +186 """Create a Markdown-formatted console file. +187 :param overwrite: Whether to overwrite the existing file if it already exists (default is True). +188 """ +189 is_new: bool = not file_is_not_empty(str(self.console_path)) or overwrite +190 with open(self.console_path, "w" if overwrite else "a", encoding=Charset.UTF_8.val) as f_console: +191 f_console.write( +192 f"{'---' + os.linesep * 2 if not is_new else ''}" +193 f"{'# ' + now(DATE_FORMAT) + os.linesep * 2 if is_new else ''}" +194 f"## {AppIcons.STARTED} {now(TIME_FORMAT)}\n\n" +195 ) +196 f_console.flush() +197 +198 def _reply(self, reply: AIReply) -> None: +199 """Reply to the user with the AI-generated response. +200 :param reply: The reply message to send as a reply to the user. +201 """ +202 ... +203 +204 def _reply_error(self, reply: AIReply) -> None: +205 """Reply to the user with an AI-generated error message or system error. +206 :param reply: The error reply message to be displayed to the user. +207 """ +208 ... @@ -370,17 +359,18 @@

-
 52class AskAi:
- 53    """The AskAI core functionalities."""
- 54
- 55    SOURCE_DIR: Path = classpath.source_path()
- 56
- 57    RESOURCE_DIR: Path = classpath.resource_path()
- 58
- 59    SPLASH: str = classpath.get_resource("splash.txt").read_text(encoding=Charset.UTF_8.val)
- 60
- 61    class RunModes(Enum):
- 62        """AskAI run modes"""
+            
 51class AskAi:
+ 52    """The AskAI core functionalities."""
+ 53
+ 54    SOURCE_DIR: Path = classpath.source_path()
+ 55
+ 56    RESOURCE_DIR: Path = classpath.resource_path()
+ 57
+ 58    SPLASH: str = classpath.get_resource("splash.txt").read_text(encoding=Charset.UTF_8.val)
+ 59
+ 60    class RunModes(Enum):
+ 61        """AskAI run modes"""
+ 62
  63        ASKAI_TUI = "ASKAI_TUI"  # Interactive Terminal UI.
  64        ASKAI_CLI = "ASKAI_CLI"  # Interactive CLI.
  65        ASKAI_CMD = "ASKAI_CMD"  # Non interactive CLI (Command mode).
@@ -412,9 +402,9 @@ 

91 self._session_id = now("%Y%m%d")[:8] 92 self._engine: AIEngine = shared.create_engine(engine_name, model_name) 93 self._context: ChatContext = shared.create_context(self._engine.ai_token_limit()) - 94 self._mode: RouterMode = RouterMode.default() - 95 self._console_path = Path(f"{CACHE_DIR}/askai-{self.session_id}.md") - 96 self._query_prompt: str | None = None + 94 self._console_path = Path(f"{CACHE_DIR}/askai-{self.session_id}.md") + 95 self._query_prompt: str | None = None + 96 self._mode: RouterMode = RouterMode.default() 97 98 if not self._console_path.exists(): 99 self._console_path.touch() @@ -460,85 +450,74 @@

139 ... 140 141 def ask_and_reply(self, question: str) -> tuple[bool, Optional[str]]: -142 """Ask the question to the AI, and provide the reply. -143 :param question: The question to ask to the AI engine. -144 """ -145 status: bool = True -146 output: str | None = None -147 processor: AIProcessor = self.mode.processor -148 assert isinstance(processor, AIProcessor) -149 -150 try: -151 if command := re.search(RE_ASKAI_CMD, question): -152 args: list[str] = list( -153 filter(lambda a: a and a != "None", re.split(r"\s", f"{command.group(1)} {command.group(2)}"))) -154 ask_commander(args, standalone_mode=False) -155 elif not (output := cache.read_reply(question)): -156 log.debug('Response not found for "%s" in cache. Querying from %s.', question, self.engine.nickname()) -157 events.reply.emit(message=msg.wait(), verbosity="debug") -158 output = processor.process(question, context=read_stdin(), query_prompt=self._query_prompt) -159 events.reply.emit(message=(output or msg.no_output("processor"))) -160 else: -161 log.debug("Reply found for '%s' in cache.", question) -162 events.reply.emit(message=output) -163 shared.context.push("HISTORY", question) -164 shared.context.push("HISTORY", output, "assistant") -165 except (NotImplementedError, ImpossibleQuery) as err: -166 events.reply_error.emit(message=str(err)) -167 except (MaxInteractionsReached, InaccurateResponse) as err: -168 events.reply_error.emit(message=msg.unprocessable(str(err))) -169 except UsageError as err: -170 events.reply_error.emit(message=msg.invalid_command(err)) -171 except IntelligibleAudioError as err: -172 events.reply_error.emit(message=msg.intelligible(err)) -173 except RateLimitError: -174 events.reply_error.emit(message=msg.quote_exceeded()) -175 status = False -176 except TerminatingQuery: +142 """Ask the specified question to the AI and provide the reply. +143 :param question: The question to ask the AI engine. +144 :return: A tuple containing a boolean indicating success or failure, and the AI's reply as an optional string. +145 """ +146 status: bool = True +147 output: str | None = None +148 processor: AIProcessor = self.mode.processor +149 assert isinstance(processor, AIProcessor) +150 +151 try: +152 if command := re.search(RE_ASKAI_CMD, question): +153 args: list[str] = list( +154 filter(lambda a: a and a != "None", re.split(r"\s", f"{command.group(1)} {command.group(2)}")) +155 ) +156 ask_commander(args, standalone_mode=False) +157 elif not (output := cache.read_reply(question)): +158 log.debug('Response not found for "%s" in cache. Querying from %s.', question, self.engine.nickname()) +159 events.reply.emit(reply=AIReply.detailed(msg.wait())) +160 output = processor.process(question, context=read_stdin(), query_prompt=self._query_prompt) +161 events.reply.emit(reply=AIReply.info(output or msg.no_output("processor"))) +162 else: +163 log.debug("Reply found for '%s' in cache.", question) +164 events.reply.emit(reply=AIReply.info(output)) +165 shared.context.push("HISTORY", question) +166 shared.context.push("HISTORY", output, "assistant") +167 except (NotImplementedError, ImpossibleQuery) as err: +168 events.reply.emit(reply=AIReply.error(err)) +169 except (MaxInteractionsReached, InaccurateResponse) as err: +170 events.reply.emit(reply=AIReply.error(msg.unprocessable(err))) +171 except UsageError as err: +172 events.reply.emit(reply=AIReply.error(msg.invalid_command(err))) +173 except IntelligibleAudioError as err: +174 events.reply.emit(reply=AIReply.error(msg.intelligible(err))) +175 except RateLimitError: +176 events.reply.emit(reply=AIReply.error(msg.quote_exceeded())) 177 status = False -178 finally: -179 if output: -180 shared.context.set("LAST_REPLY", output) -181 -182 return status, output +178 except TerminatingQuery: +179 status = False +180 finally: +181 if output: +182 shared.context.set("LAST_REPLY", output) 183 -184 def _create_console_file(self, overwrite: bool = True): -185 """Create the Markdown formatted console file. -186 :param overwrite: Whether to overwrite the file or not. -187 """ -188 is_new: bool = not file_is_not_empty(str(self.console_path)) or overwrite -189 with open(self.console_path, "w" if overwrite else "a", encoding=Charset.UTF_8.val) as f_console: -190 f_console.write( -191 f"{'---' + os.linesep * 2 if not is_new else ''}" -192 f"{'# ' + now(DATE_FORMAT) + os.linesep * 2 if is_new else ''}" -193 f"## {AppIcons.STARTED} {now(TIME_FORMAT)}\n\n" -194 ) -195 f_console.flush() -196 -197 def _reply(self, message: str) -> None: -198 """Reply to the user with the AI response. -199 :param message: The message to reply to the user. -200 """ -201 ... -202 -203 def _reply_error(self, message: str) -> None: -204 """Reply API or system errors. -205 :param message: The error message to be displayed. -206 """ -207 ... -208 -209 def _cb_mode_changed_event(self, ev: Event) -> None: -210 """Callback to handle mode changed events. -211 :param ev: The mode changed event. -212 """ -213 self._mode: RouterMode = RouterMode.of_name(ev.args.mode) -214 if not self._mode.is_default: -215 events.reply.emit( -216 f"{msg.enter_qna()} \n" -217 f"```\nContext:  {ev.args.sum_path},  {ev.args.glob} \n```\n" -218 f"`{msg.press_esc_enter()}` \n\n" -219 f"> {msg.qna_welcome()}" -220 ) +184 return status, output +185 +186 def _create_console_file(self, overwrite: bool = True) -> None: +187 """Create a Markdown-formatted console file. +188 :param overwrite: Whether to overwrite the existing file if it already exists (default is True). +189 """ +190 is_new: bool = not file_is_not_empty(str(self.console_path)) or overwrite +191 with open(self.console_path, "w" if overwrite else "a", encoding=Charset.UTF_8.val) as f_console: +192 f_console.write( +193 f"{'---' + os.linesep * 2 if not is_new else ''}" +194 f"{'# ' + now(DATE_FORMAT) + os.linesep * 2 if is_new else ''}" +195 f"## {AppIcons.STARTED} {now(TIME_FORMAT)}\n\n" +196 ) +197 f_console.flush() +198 +199 def _reply(self, reply: AIReply) -> None: +200 """Reply to the user with the AI-generated response. +201 :param reply: The reply message to send as a reply to the user. +202 """ +203 ... +204 +205 def _reply_error(self, reply: AIReply) -> None: +206 """Reply to the user with an AI-generated error message or system error. +207 :param reply: The error reply message to be displayed to the user. +208 """ +209 ...

@@ -578,9 +557,9 @@

91 self._session_id = now("%Y%m%d")[:8] 92 self._engine: AIEngine = shared.create_engine(engine_name, model_name) 93 self._context: ChatContext = shared.create_context(self._engine.ai_token_limit()) -94 self._mode: RouterMode = RouterMode.default() -95 self._console_path = Path(f"{CACHE_DIR}/askai-{self.session_id}.md") -96 self._query_prompt: str | None = None +94 self._console_path = Path(f"{CACHE_DIR}/askai-{self.session_id}.md") +95 self._query_prompt: str | None = None +96 self._mode: RouterMode = RouterMode.default() 97 98 if not self._console_path.exists(): 99 self._console_path.touch() @@ -618,7 +597,7 @@

SPLASH: str = - " ################\n ### ###\n ## ##### ### ##\n # ####### # ######## ###\n ### # #### # ###### ## #\n ### # ## ( ) #### ## ### #\n ## ( ) # ## ########## # #\n _ _ _\n | | ___ __ _ __| (_)_ __ __ _\n | | / _ \\ / _` |/ _` | | '_ \\ / _` |\n | |__| (_) | (_| | (_| | | | | | (_| |_ _ _\n |_____\\___/ \\__,_|\\__,_|_|_| |_|\\__, (_|_|_)\n |___/\n\n # # ## ## ### ################ ####\n( ) ( ) ### ### ############## #\n ### # ### ############ #\n # ### ######## #\n # #### #\n ( ) #####\n" + " ################\n ### ###\n ## ##### ### ##\n # ####### # ######## ###\n ### # #### # ###### ## #\n ### # ## ( ) #### ## ### #\n ## ( ) # ## ########## # #\n _ _ _\n | | ___ __ _ __| (_)_ __ __ _\n | | / _ \\ / _` |/ _` | | '_ \\ / _` |\n | |__| (_) | (_| | (_| | | | | | (_| |_ _ _\n |_____\\___/ \\__,_|\\__,_|_|_| |_|\\__, (_|_|_)\n |___/\n\n # # ## ## ### ################ ####\n( ) ( ) ### ### ############## #\n ### # ### ############ #\n # ### ######## #\n # #### #\n ( ) #####\n\n"
@@ -794,57 +773,65 @@

141    def ask_and_reply(self, question: str) -> tuple[bool, Optional[str]]:
-142        """Ask the question to the AI, and provide the reply.
-143        :param question: The question to ask to the AI engine.
-144        """
-145        status: bool = True
-146        output: str | None = None
-147        processor: AIProcessor = self.mode.processor
-148        assert isinstance(processor, AIProcessor)
-149
-150        try:
-151            if command := re.search(RE_ASKAI_CMD, question):
-152                args: list[str] = list(
-153                    filter(lambda a: a and a != "None", re.split(r"\s", f"{command.group(1)} {command.group(2)}")))
-154                ask_commander(args, standalone_mode=False)
-155            elif not (output := cache.read_reply(question)):
-156                log.debug('Response not found for "%s" in cache. Querying from %s.', question, self.engine.nickname())
-157                events.reply.emit(message=msg.wait(), verbosity="debug")
-158                output = processor.process(question, context=read_stdin(), query_prompt=self._query_prompt)
-159                events.reply.emit(message=(output or msg.no_output("processor")))
-160            else:
-161                log.debug("Reply found for '%s' in cache.", question)
-162                events.reply.emit(message=output)
-163                shared.context.push("HISTORY", question)
-164                shared.context.push("HISTORY", output, "assistant")
-165        except (NotImplementedError, ImpossibleQuery) as err:
-166            events.reply_error.emit(message=str(err))
-167        except (MaxInteractionsReached, InaccurateResponse) as err:
-168            events.reply_error.emit(message=msg.unprocessable(str(err)))
-169        except UsageError as err:
-170            events.reply_error.emit(message=msg.invalid_command(err))
-171        except IntelligibleAudioError as err:
-172            events.reply_error.emit(message=msg.intelligible(err))
-173        except RateLimitError:
-174            events.reply_error.emit(message=msg.quote_exceeded())
-175            status = False
-176        except TerminatingQuery:
+142        """Ask the specified question to the AI and provide the reply.
+143        :param question: The question to ask the AI engine.
+144        :return: A tuple containing a boolean indicating success or failure, and the AI's reply as an optional string.
+145        """
+146        status: bool = True
+147        output: str | None = None
+148        processor: AIProcessor = self.mode.processor
+149        assert isinstance(processor, AIProcessor)
+150
+151        try:
+152            if command := re.search(RE_ASKAI_CMD, question):
+153                args: list[str] = list(
+154                    filter(lambda a: a and a != "None", re.split(r"\s", f"{command.group(1)} {command.group(2)}"))
+155                )
+156                ask_commander(args, standalone_mode=False)
+157            elif not (output := cache.read_reply(question)):
+158                log.debug('Response not found for "%s" in cache. Querying from %s.', question, self.engine.nickname())
+159                events.reply.emit(reply=AIReply.detailed(msg.wait()))
+160                output = processor.process(question, context=read_stdin(), query_prompt=self._query_prompt)
+161                events.reply.emit(reply=AIReply.info(output or msg.no_output("processor")))
+162            else:
+163                log.debug("Reply found for '%s' in cache.", question)
+164                events.reply.emit(reply=AIReply.info(output))
+165                shared.context.push("HISTORY", question)
+166                shared.context.push("HISTORY", output, "assistant")
+167        except (NotImplementedError, ImpossibleQuery) as err:
+168            events.reply.emit(reply=AIReply.error(err))
+169        except (MaxInteractionsReached, InaccurateResponse) as err:
+170            events.reply.emit(reply=AIReply.error(msg.unprocessable(err)))
+171        except UsageError as err:
+172            events.reply.emit(reply=AIReply.error(msg.invalid_command(err)))
+173        except IntelligibleAudioError as err:
+174            events.reply.emit(reply=AIReply.error(msg.intelligible(err)))
+175        except RateLimitError:
+176            events.reply.emit(reply=AIReply.error(msg.quote_exceeded()))
 177            status = False
-178        finally:
-179            if output:
-180                shared.context.set("LAST_REPLY", output)
-181
-182        return status, output
+178        except TerminatingQuery:
+179            status = False
+180        finally:
+181            if output:
+182                shared.context.set("LAST_REPLY", output)
+183
+184        return status, output
 
-

Ask the question to the AI, and provide the reply.

+

Ask the specified question to the AI and provide the reply.

Parameters
    -
  • question: The question to ask to the AI engine.
  • +
  • question: The question to ask the AI engine.
+ +
Returns
+ +
+

A tuple containing a boolean indicating success or failure, and the AI's reply as an optional string.

+
@@ -861,8 +848,9 @@
Parameters
-
61    class RunModes(Enum):
-62        """AskAI run modes"""
+            
60    class RunModes(Enum):
+61        """AskAI run modes"""
+62
 63        ASKAI_TUI = "ASKAI_TUI"  # Interactive Terminal UI.
 64        ASKAI_CLI = "ASKAI_CLI"  # Interactive CLI.
 65        ASKAI_CMD = "ASKAI_CMD"  # Non interactive CLI (Command mode).
diff --git a/docs/api/askai/main/askai/core/askai_cli.html b/docs/api/askai/main/askai/core/askai_cli.html
index cda0eef4..14824b23 100644
--- a/docs/api/askai/main/askai/core/askai_cli.html
+++ b/docs/api/askai/main/askai/core/askai_cli.html
@@ -3,7 +3,7 @@
 
     
     
-    
+    
     main.askai.core.askai_cli API documentation
 
     
@@ -90,167 +90,209 @@ 

12 13 Copyright (c) 2024, HomeSetup 14""" - 15 - 16import logging as log - 17import os - 18from functools import partial - 19from threading import Thread - 20from typing import List, TypeAlias - 21 - 22import nltk - 23import pause - 24from clitt.core.term.cursor import cursor - 25from clitt.core.term.screen import screen - 26from clitt.core.tui.line_input.keyboard_input import KeyboardInput - 27from hspylib.core.tools.commons import sysout - 28from hspylib.modules.eventbus.event import Event - 29 - 30from askai.core.askai import AskAi - 31from askai.core.askai_configs import configs - 32from askai.core.askai_events import * - 33from askai.core.askai_messages import msg - 34from askai.core.commander.commander import commands - 35from askai.core.component.audio_player import player - 36from askai.core.component.cache_service import cache, CACHE_DIR - 37from askai.core.component.recorder import recorder - 38from askai.core.component.scheduler import scheduler - 39from askai.core.support.shared_instances import shared - 40from askai.core.support.utilities import display_text - 41 - 42QueryString: TypeAlias = str | List[str] | None - 43 + 15from askai.core.askai import AskAi + 16from askai.core.askai_configs import configs + 17from askai.core.askai_events import * + 18from askai.core.askai_messages import msg + 19from askai.core.commander.commander import commands + 20from askai.core.component.audio_player import player + 21from askai.core.component.cache_service import cache, CACHE_DIR + 22from askai.core.component.recorder import recorder + 23from askai.core.component.scheduler import scheduler + 24from askai.core.enums.router_mode import RouterMode + 25from askai.core.model.ai_reply import AIReply + 26from askai.core.support.shared_instances import shared + 27from askai.core.support.text_formatter import text_formatter + 28from askai.core.support.utilities import display_text + 29from clitt.core.term.cursor import cursor + 30from clitt.core.term.screen import screen + 31from clitt.core.tui.line_input.keyboard_input import KeyboardInput + 32from hspylib.modules.eventbus.event import Event + 33from pathlib import Path + 34from rich.progress import Progress + 35from threading import Thread + 36from typing import List, Optional, TypeAlias + 37 + 38import logging as log + 39import nltk + 40import os + 41import pause + 42 + 43QueryString: TypeAlias = str | List[str] | None 44 - 45class AskAiCli(AskAi): - 46 """The AskAI CLI application.""" - 47 - 48 def __init__( - 49 self, - 50 interactive: bool, - 51 speak: bool, - 52 debug: bool, - 53 cacheable: bool, - 54 tempo: int, - 55 query_prompt: str, - 56 engine_name: str, - 57 model_name: str, - 58 query_string: QueryString, - 59 ): - 60 - 61 os.environ["ASKAI_APP"] = (self.RunModes.ASKAI_CLI if interactive else self.RunModes.ASKAI_CMD).value - 62 super().__init__(interactive, speak, debug, cacheable, tempo, engine_name, model_name) - 63 self._ready: bool = False - 64 self._query_prompt = query_prompt - 65 self._query_string: QueryString = query_string if isinstance(query_string, str) else " ".join(query_string) - 66 self._startup() - 67 - 68 def run(self) -> None: - 69 """Run the application.""" - 70 while question := (self._query_string or self._input()): - 71 status, output = self.ask_and_reply(question) - 72 if not status: - 73 question = None - 74 break - 75 elif output: - 76 cache.save_reply(question, output) - 77 cache.save_input_history() - 78 with open(self._console_path, "a+") as f_console: - 79 f_console.write(f"{shared.username_md}{question}\n\n") - 80 f_console.write(f"{shared.nickname_md}{output}\n\n") - 81 f_console.flush() - 82 if not configs.is_interactive: - 83 break - 84 if question == "": - 85 self._reply(msg.goodbye()) - 86 sysout("%NC%") - 87 - 88 def _reply(self, message: str) -> None: - 89 """Reply to the user with the AI response. - 90 :param message: The message to reply to the user. - 91 """ - 92 if message and (text := msg.translate(message)): - 93 log.debug(message) - 94 if configs.is_speak: - 95 self.engine.text_to_speech(text, f"{shared.nickname}") - 96 else: - 97 display_text(text, f"{shared.nickname}") - 98 - 99 def _reply_error(self, message: str) -> None: -100 """Reply API or system errors. -101 :param message: The error message to be displayed. -102 """ -103 if message and (text := msg.translate(message)): -104 log.error(message) -105 if configs.is_speak: -106 self.engine.text_to_speech(f"Error: {text}", f"{shared.nickname}") -107 else: -108 display_text(f"%RED%Error: {text}%NC%", f"{shared.nickname}") -109 -110 def _input(self) -> Optional[str]: -111 """Read the user input from stdin.""" -112 return shared.input_text(f"{shared.username}", f"Message {self.engine.nickname()}") -113 -114 def _cb_reply_event(self, ev: Event, error: bool = False) -> None: -115 """Callback to handle reply events. -116 :param ev: The reply event. -117 :param error: Whether the event is an error not not. + 45 + 46class AskAiCli(AskAi): + 47 """The AskAI CLI application.""" + 48 + 49 def __init__( + 50 self, + 51 interactive: bool, + 52 speak: bool, + 53 debug: bool, + 54 cacheable: bool, + 55 tempo: int, + 56 query_prompt: str, + 57 engine_name: str, + 58 model_name: str, + 59 query_string: QueryString, + 60 ): + 61 + 62 configs.is_interactive = interactive if not query_prompt else False + 63 super().__init__(interactive, speak, debug, cacheable, tempo, engine_name, model_name) + 64 os.environ["ASKAI_APP"] = (self.RunModes.ASKAI_CLI if interactive else self.RunModes.ASKAI_CMD).value + 65 self._ready: bool = False + 66 self._progress = Progress() + 67 self._query_prompt = query_prompt + 68 self._query_string: QueryString = query_string if isinstance(query_string, str) else " ".join(query_string) + 69 self._startup() + 70 + 71 def run(self) -> None: + 72 """Run the application.""" + 73 while question := (self._query_string or self._input()): + 74 status, output = self.ask_and_reply(question) + 75 if not status: + 76 question = None + 77 break + 78 elif output: + 79 cache.save_reply(question, output) + 80 cache.save_input_history() + 81 with open(self._console_path, "a+") as f_console: + 82 f_console.write(f"{shared.username_md}{question}\n\n") + 83 f_console.write(f"{shared.nickname_md}{output}\n\n") + 84 f_console.flush() + 85 if not configs.is_interactive: + 86 break + 87 if question == "": + 88 self._reply(AIReply.info(msg.goodbye())) + 89 display_text("", markdown=False) + 90 + 91 def _reply(self, reply: AIReply) -> None: + 92 """Reply to the user with the AI-generated response. + 93 :param reply: The reply message to send as a reply to the user. + 94 """ + 95 if reply and (text := msg.translate(reply.message)): + 96 log.debug(reply.message) + 97 if configs.is_speak and reply.is_speakable: + 98 self.engine.text_to_speech(text, f"{shared.nickname}") + 99 elif not configs.is_interactive: +100 display_text(text_formatter.strip_format(text), f"{shared.nickname}", markdown=False) +101 else: +102 display_text(text, f"{shared.nickname}") +103 +104 def _reply_error(self, reply: AIReply) -> None: +105 """Reply to the user with an AI-generated error message or system error. +106 :param reply: The error reply message to be displayed to the user. +107 """ +108 if reply and (text := msg.translate(reply.message)): +109 log.error(reply.message) +110 if configs.is_speak and reply.is_speakable: +111 self.engine.text_to_speech(f"Error: {text}", f"{shared.nickname}") +112 else: +113 display_text(f"%RED%Error: {text}%NC%", f"{shared.nickname}") +114 +115 def _input(self) -> Optional[str]: +116 """Read the user input from stdin. +117 :return: The user's input as a string, or None if no input is provided. 118 """ -119 if message := ev.args.message: -120 if error: -121 self._reply_error(message) -122 else: -123 if ev.args.verbosity.casefold() == "normal" or configs.is_debug: -124 if ev.args.erase_last: -125 cursor.erase_line() -126 self._reply(message) -127 -128 def _cb_mic_listening_event(self, ev: Event) -> None: -129 """Callback to handle microphone listening events. -130 :param ev: The microphone listening event. -131 """ -132 if ev.args.listening: -133 self._reply(msg.listening()) +119 return shared.input_text(f"{shared.username}", f"Message {self.engine.nickname()}") +120 +121 def _cb_reply_event(self, ev: Event) -> None: +122 """Callback to handle reply events. +123 :param ev: The event object representing the reply event. +124 """ +125 reply: AIReply +126 if reply := ev.args.reply: +127 if reply.is_error: +128 self._reply_error(reply) +129 else: +130 if ev.args.reply.verbosity.match(configs.verbosity) or configs.is_debug: +131 if ev.args.erase_last: +132 cursor.erase_line() +133 self._reply(reply) 134 -135 def _cb_device_changed_event(self, ev: Event) -> None: -136 """Callback to handle audio input device changed events. -137 :param ev: The device changed event. +135 def _cb_mode_changed_event(self, ev: Event) -> None: +136 """Callback to handle mode change events. +137 :param ev: The event object representing the mode change. 138 """ -139 cursor.erase_line() -140 self._reply(msg.device_switch(str(ev.args.device))) -141 -142 def _splash(self) -> None: -143 """Display the AskAI splash screen.""" -144 splash_interval = 250 -145 screen.clear() -146 sysout(f"%GREEN%{self.SPLASH}%NC%") -147 while not self._ready: -148 pause.milliseconds(splash_interval) -149 screen.clear() -150 -151 def _startup(self) -> None: -152 """Initialize the application.""" -153 askai_bus = AskAiEvents.bus(ASKAI_BUS_NAME) -154 askai_bus.subscribe(REPLY_EVENT, self._cb_reply_event) -155 askai_bus.subscribe(REPLY_ERROR_EVENT, partial(self._cb_reply_event, error=True)) -156 if configs.is_interactive: -157 splash_thread: Thread = Thread(daemon=True, target=self._splash) -158 splash_thread.start() -159 nltk.download("averaged_perceptron_tagger", quiet=True, download_dir=CACHE_DIR) -160 cache.cache_enable = configs.is_cache -161 KeyboardInput.preload_history(cache.load_input_history(commands())) -162 scheduler.start() -163 recorder.setup() -164 player.start_delay() -165 self._ready = True -166 splash_thread.join() -167 display_text(self, markdown=False) -168 self._reply(msg.welcome(os.getenv("USER", "you"))) -169 elif configs.is_speak: -170 recorder.setup() -171 player.start_delay() -172 askai_bus.subscribe(MIC_LISTENING_EVENT, self._cb_mic_listening_event) -173 askai_bus.subscribe(DEVICE_CHANGED_EVENT, self._cb_device_changed_event) -174 askai_bus.subscribe(MODE_CHANGED_EVENT, self._cb_mode_changed_event) -175 log.info("AskAI is ready to use!") +139 self._mode: RouterMode = RouterMode.of_name(ev.args.mode) +140 if not self._mode.is_default: +141 sum_msg: str = ( +142 f"{msg.enter_qna()} \n" +143 f"```\nContext:  {ev.args.sum_path},  {ev.args.glob} \n```\n" +144 f"`{msg.press_esc_enter()}` \n\n" +145 f"> {msg.qna_welcome()}" +146 ) +147 events.reply.emit(reply=AIReply.info(sum_msg)) +148 +149 def _cb_mic_listening_event(self, ev: Event) -> None: +150 """Callback to handle microphone listening events. +151 :param ev: The event object representing the microphone listening event. +152 """ +153 if ev.args.listening: +154 self._reply(AIReply.info(msg.listening())) +155 +156 def _cb_device_changed_event(self, ev: Event) -> None: +157 """Callback to handle audio input device change events. +158 :param ev: The event object representing the device change. +159 """ +160 cursor.erase_line() +161 self._reply(AIReply.info(msg.device_switch(str(ev.args.device)))) +162 +163 def _splash(self, interval: int = 250) -> None: +164 """Display the AskAI splash screen until the system is fully started and ready. This method shows the splash +165 screen during the startup process and hides it once the system is ready. +166 :param interval: The interval in milliseconds for polling the startup status (default is 250 ms). +167 """ +168 screen.clear() +169 display_text(f"%GREEN%{self.SPLASH}%NC%", markdown=False) +170 while not self._ready: +171 pause.milliseconds(interval) +172 screen.clear() +173 +174 def _startup(self) -> None: +175 """Initialize the application components.""" +176 # List of tasks for progress tracking +177 tasks = [ +178 "Downloading nltk data", +179 "Loading input history", +180 "Starting scheduler", +181 "Setting up recorder", +182 "Starting player delay", +183 "Finalizing startup", +184 ] +185 # Start and manage the progress bar +186 askai_bus = AskAiEvents.bus(ASKAI_BUS_NAME) +187 askai_bus.subscribe(REPLY_EVENT, self._cb_reply_event) +188 if configs.is_interactive: +189 splash_thread: Thread = Thread(daemon=True, target=self._splash) +190 splash_thread.start() +191 task = self._progress.add_task("[green]Starting up...", total=len(tasks)) +192 with self._progress: +193 os.chdir(Path.home()) +194 self._progress.update(task, advance=1, description="[green]Downloading nltk data") +195 nltk.download("averaged_perceptron_tagger", quiet=True, download_dir=CACHE_DIR) +196 cache.cache_enable = configs.is_cache +197 self._progress.update(task, advance=1, description="[green]Loading input history") +198 KeyboardInput.preload_history(cache.load_input_history(commands())) +199 self._progress.update(task, advance=1, description="[green]Starting scheduler") +200 scheduler.start() +201 self._progress.update(task, advance=1, description="[green]Setting up recorder") +202 recorder.setup() +203 self._progress.update(task, advance=1, description="[green]Starting player delay") +204 player.start_delay() +205 self._progress.update(task, advance=1, description="[green]Finalizing startup") +206 pause.seconds(1) +207 self._ready = True +208 splash_thread.join() +209 askai_bus.subscribe(MIC_LISTENING_EVENT, self._cb_mic_listening_event) +210 askai_bus.subscribe(DEVICE_CHANGED_EVENT, self._cb_device_changed_event) +211 askai_bus.subscribe(MODE_CHANGED_EVENT, self._cb_mode_changed_event) +212 display_text(str(self), markdown=False) +213 self._reply(AIReply.info(msg.welcome(os.getenv("USER", "you")))) +214 elif configs.is_speak: +215 recorder.setup() +216 player.start_delay() +217 log.info("AskAI is ready to use!")

@@ -278,137 +320,178 @@

-
 46class AskAiCli(AskAi):
- 47    """The AskAI CLI application."""
- 48
- 49    def __init__(
- 50        self,
- 51        interactive: bool,
- 52        speak: bool,
- 53        debug: bool,
- 54        cacheable: bool,
- 55        tempo: int,
- 56        query_prompt: str,
- 57        engine_name: str,
- 58        model_name: str,
- 59        query_string: QueryString,
- 60    ):
- 61
- 62        os.environ["ASKAI_APP"] = (self.RunModes.ASKAI_CLI if interactive else self.RunModes.ASKAI_CMD).value
- 63        super().__init__(interactive, speak, debug, cacheable, tempo, engine_name, model_name)
- 64        self._ready: bool = False
- 65        self._query_prompt = query_prompt
- 66        self._query_string: QueryString = query_string if isinstance(query_string, str) else " ".join(query_string)
- 67        self._startup()
- 68
- 69    def run(self) -> None:
- 70        """Run the application."""
- 71        while question := (self._query_string or self._input()):
- 72            status, output = self.ask_and_reply(question)
- 73            if not status:
- 74                question = None
- 75                break
- 76            elif output:
- 77                cache.save_reply(question, output)
- 78                cache.save_input_history()
- 79                with open(self._console_path, "a+") as f_console:
- 80                    f_console.write(f"{shared.username_md}{question}\n\n")
- 81                    f_console.write(f"{shared.nickname_md}{output}\n\n")
- 82                    f_console.flush()
- 83            if not configs.is_interactive:
- 84                break
- 85        if question == "":
- 86            self._reply(msg.goodbye())
- 87        sysout("%NC%")
- 88
- 89    def _reply(self, message: str) -> None:
- 90        """Reply to the user with the AI response.
- 91        :param message: The message to reply to the user.
- 92        """
- 93        if message and (text := msg.translate(message)):
- 94            log.debug(message)
- 95            if configs.is_speak:
- 96                self.engine.text_to_speech(text, f"{shared.nickname}")
- 97            else:
- 98                display_text(text, f"{shared.nickname}")
- 99
-100    def _reply_error(self, message: str) -> None:
-101        """Reply API or system errors.
-102        :param message: The error message to be displayed.
-103        """
-104        if message and (text := msg.translate(message)):
-105            log.error(message)
-106            if configs.is_speak:
-107                self.engine.text_to_speech(f"Error: {text}", f"{shared.nickname}")
-108            else:
-109                display_text(f"%RED%Error: {text}%NC%", f"{shared.nickname}")
-110
-111    def _input(self) -> Optional[str]:
-112        """Read the user input from stdin."""
-113        return shared.input_text(f"{shared.username}", f"Message {self.engine.nickname()}")
-114
-115    def _cb_reply_event(self, ev: Event, error: bool = False) -> None:
-116        """Callback to handle reply events.
-117        :param ev: The reply event.
-118        :param error: Whether the event is an error not not.
+            
 47class AskAiCli(AskAi):
+ 48    """The AskAI CLI application."""
+ 49
+ 50    def __init__(
+ 51        self,
+ 52        interactive: bool,
+ 53        speak: bool,
+ 54        debug: bool,
+ 55        cacheable: bool,
+ 56        tempo: int,
+ 57        query_prompt: str,
+ 58        engine_name: str,
+ 59        model_name: str,
+ 60        query_string: QueryString,
+ 61    ):
+ 62
+ 63        configs.is_interactive = interactive if not query_prompt else False
+ 64        super().__init__(interactive, speak, debug, cacheable, tempo, engine_name, model_name)
+ 65        os.environ["ASKAI_APP"] = (self.RunModes.ASKAI_CLI if interactive else self.RunModes.ASKAI_CMD).value
+ 66        self._ready: bool = False
+ 67        self._progress = Progress()
+ 68        self._query_prompt = query_prompt
+ 69        self._query_string: QueryString = query_string if isinstance(query_string, str) else " ".join(query_string)
+ 70        self._startup()
+ 71
+ 72    def run(self) -> None:
+ 73        """Run the application."""
+ 74        while question := (self._query_string or self._input()):
+ 75            status, output = self.ask_and_reply(question)
+ 76            if not status:
+ 77                question = None
+ 78                break
+ 79            elif output:
+ 80                cache.save_reply(question, output)
+ 81                cache.save_input_history()
+ 82                with open(self._console_path, "a+") as f_console:
+ 83                    f_console.write(f"{shared.username_md}{question}\n\n")
+ 84                    f_console.write(f"{shared.nickname_md}{output}\n\n")
+ 85                    f_console.flush()
+ 86            if not configs.is_interactive:
+ 87                break
+ 88        if question == "":
+ 89            self._reply(AIReply.info(msg.goodbye()))
+ 90        display_text("", markdown=False)
+ 91
+ 92    def _reply(self, reply: AIReply) -> None:
+ 93        """Reply to the user with the AI-generated response.
+ 94        :param reply: The reply message to send as a reply to the user.
+ 95        """
+ 96        if reply and (text := msg.translate(reply.message)):
+ 97            log.debug(reply.message)
+ 98            if configs.is_speak and reply.is_speakable:
+ 99                self.engine.text_to_speech(text, f"{shared.nickname}")
+100            elif not configs.is_interactive:
+101                display_text(text_formatter.strip_format(text), f"{shared.nickname}", markdown=False)
+102            else:
+103                display_text(text, f"{shared.nickname}")
+104
+105    def _reply_error(self, reply: AIReply) -> None:
+106        """Reply to the user with an AI-generated error message or system error.
+107        :param reply: The error reply message to be displayed to the user.
+108        """
+109        if reply and (text := msg.translate(reply.message)):
+110            log.error(reply.message)
+111            if configs.is_speak and reply.is_speakable:
+112                self.engine.text_to_speech(f"Error: {text}", f"{shared.nickname}")
+113            else:
+114                display_text(f"%RED%Error: {text}%NC%", f"{shared.nickname}")
+115
+116    def _input(self) -> Optional[str]:
+117        """Read the user input from stdin.
+118        :return: The user's input as a string, or None if no input is provided.
 119        """
-120        if message := ev.args.message:
-121            if error:
-122                self._reply_error(message)
-123            else:
-124                if ev.args.verbosity.casefold() == "normal" or configs.is_debug:
-125                    if ev.args.erase_last:
-126                        cursor.erase_line()
-127                    self._reply(message)
-128
-129    def _cb_mic_listening_event(self, ev: Event) -> None:
-130        """Callback to handle microphone listening events.
-131        :param ev: The microphone listening event.
-132        """
-133        if ev.args.listening:
-134            self._reply(msg.listening())
+120        return shared.input_text(f"{shared.username}", f"Message {self.engine.nickname()}")
+121
+122    def _cb_reply_event(self, ev: Event) -> None:
+123        """Callback to handle reply events.
+124        :param ev: The event object representing the reply event.
+125        """
+126        reply: AIReply
+127        if reply := ev.args.reply:
+128            if reply.is_error:
+129                self._reply_error(reply)
+130            else:
+131                if ev.args.reply.verbosity.match(configs.verbosity) or configs.is_debug:
+132                    if ev.args.erase_last:
+133                        cursor.erase_line()
+134                    self._reply(reply)
 135
-136    def _cb_device_changed_event(self, ev: Event) -> None:
-137        """Callback to handle audio input device changed events.
-138        :param ev: The device changed event.
+136    def _cb_mode_changed_event(self, ev: Event) -> None:
+137        """Callback to handle mode change events.
+138        :param ev: The event object representing the mode change.
 139        """
-140        cursor.erase_line()
-141        self._reply(msg.device_switch(str(ev.args.device)))
-142
-143    def _splash(self) -> None:
-144        """Display the AskAI splash screen."""
-145        splash_interval = 250
-146        screen.clear()
-147        sysout(f"%GREEN%{self.SPLASH}%NC%")
-148        while not self._ready:
-149            pause.milliseconds(splash_interval)
-150        screen.clear()
-151
-152    def _startup(self) -> None:
-153        """Initialize the application."""
-154        askai_bus = AskAiEvents.bus(ASKAI_BUS_NAME)
-155        askai_bus.subscribe(REPLY_EVENT, self._cb_reply_event)
-156        askai_bus.subscribe(REPLY_ERROR_EVENT, partial(self._cb_reply_event, error=True))
-157        if configs.is_interactive:
-158            splash_thread: Thread = Thread(daemon=True, target=self._splash)
-159            splash_thread.start()
-160            nltk.download("averaged_perceptron_tagger", quiet=True, download_dir=CACHE_DIR)
-161            cache.cache_enable = configs.is_cache
-162            KeyboardInput.preload_history(cache.load_input_history(commands()))
-163            scheduler.start()
-164            recorder.setup()
-165            player.start_delay()
-166            self._ready = True
-167            splash_thread.join()
-168            display_text(self, markdown=False)
-169            self._reply(msg.welcome(os.getenv("USER", "you")))
-170        elif configs.is_speak:
-171            recorder.setup()
-172            player.start_delay()
-173        askai_bus.subscribe(MIC_LISTENING_EVENT, self._cb_mic_listening_event)
-174        askai_bus.subscribe(DEVICE_CHANGED_EVENT, self._cb_device_changed_event)
-175        askai_bus.subscribe(MODE_CHANGED_EVENT, self._cb_mode_changed_event)
-176        log.info("AskAI is ready to use!")
+140        self._mode: RouterMode = RouterMode.of_name(ev.args.mode)
+141        if not self._mode.is_default:
+142            sum_msg: str = (
+143                f"{msg.enter_qna()} \n"
+144                f"```\nContext:  {ev.args.sum_path},   {ev.args.glob} \n```\n"
+145                f"`{msg.press_esc_enter()}` \n\n"
+146                f"> {msg.qna_welcome()}"
+147            )
+148            events.reply.emit(reply=AIReply.info(sum_msg))
+149
+150    def _cb_mic_listening_event(self, ev: Event) -> None:
+151        """Callback to handle microphone listening events.
+152        :param ev: The event object representing the microphone listening event.
+153        """
+154        if ev.args.listening:
+155            self._reply(AIReply.info(msg.listening()))
+156
+157    def _cb_device_changed_event(self, ev: Event) -> None:
+158        """Callback to handle audio input device change events.
+159        :param ev: The event object representing the device change.
+160        """
+161        cursor.erase_line()
+162        self._reply(AIReply.info(msg.device_switch(str(ev.args.device))))
+163
+164    def _splash(self, interval: int = 250) -> None:
+165        """Display the AskAI splash screen until the system is fully started and ready. This method shows the splash
+166        screen during the startup process and hides it once the system is ready.
+167        :param interval: The interval in milliseconds for polling the startup status (default is 250 ms).
+168        """
+169        screen.clear()
+170        display_text(f"%GREEN%{self.SPLASH}%NC%", markdown=False)
+171        while not self._ready:
+172            pause.milliseconds(interval)
+173        screen.clear()
+174
+175    def _startup(self) -> None:
+176        """Initialize the application components."""
+177        # List of tasks for progress tracking
+178        tasks = [
+179            "Downloading nltk data",
+180            "Loading input history",
+181            "Starting scheduler",
+182            "Setting up recorder",
+183            "Starting player delay",
+184            "Finalizing startup",
+185        ]
+186        # Start and manage the progress bar
+187        askai_bus = AskAiEvents.bus(ASKAI_BUS_NAME)
+188        askai_bus.subscribe(REPLY_EVENT, self._cb_reply_event)
+189        if configs.is_interactive:
+190            splash_thread: Thread = Thread(daemon=True, target=self._splash)
+191            splash_thread.start()
+192            task = self._progress.add_task("[green]Starting up...", total=len(tasks))
+193            with self._progress:
+194                os.chdir(Path.home())
+195                self._progress.update(task, advance=1, description="[green]Downloading nltk data")
+196                nltk.download("averaged_perceptron_tagger", quiet=True, download_dir=CACHE_DIR)
+197                cache.cache_enable = configs.is_cache
+198                self._progress.update(task, advance=1, description="[green]Loading input history")
+199                KeyboardInput.preload_history(cache.load_input_history(commands()))
+200                self._progress.update(task, advance=1, description="[green]Starting scheduler")
+201                scheduler.start()
+202                self._progress.update(task, advance=1, description="[green]Setting up recorder")
+203                recorder.setup()
+204                self._progress.update(task, advance=1, description="[green]Starting player delay")
+205                player.start_delay()
+206                self._progress.update(task, advance=1, description="[green]Finalizing startup")
+207                pause.seconds(1)
+208            self._ready = True
+209            splash_thread.join()
+210            askai_bus.subscribe(MIC_LISTENING_EVENT, self._cb_mic_listening_event)
+211            askai_bus.subscribe(DEVICE_CHANGED_EVENT, self._cb_device_changed_event)
+212            askai_bus.subscribe(MODE_CHANGED_EVENT, self._cb_mode_changed_event)
+213            display_text(str(self), markdown=False)
+214            self._reply(AIReply.info(msg.welcome(os.getenv("USER", "you"))))
+215        elif configs.is_speak:
+216            recorder.setup()
+217            player.start_delay()
+218        log.info("AskAI is ready to use!")
 
@@ -426,25 +509,27 @@

-
49    def __init__(
-50        self,
-51        interactive: bool,
-52        speak: bool,
-53        debug: bool,
-54        cacheable: bool,
-55        tempo: int,
-56        query_prompt: str,
-57        engine_name: str,
-58        model_name: str,
-59        query_string: QueryString,
-60    ):
-61
-62        os.environ["ASKAI_APP"] = (self.RunModes.ASKAI_CLI if interactive else self.RunModes.ASKAI_CMD).value
-63        super().__init__(interactive, speak, debug, cacheable, tempo, engine_name, model_name)
-64        self._ready: bool = False
-65        self._query_prompt = query_prompt
-66        self._query_string: QueryString = query_string if isinstance(query_string, str) else " ".join(query_string)
-67        self._startup()
+            
50    def __init__(
+51        self,
+52        interactive: bool,
+53        speak: bool,
+54        debug: bool,
+55        cacheable: bool,
+56        tempo: int,
+57        query_prompt: str,
+58        engine_name: str,
+59        model_name: str,
+60        query_string: QueryString,
+61    ):
+62
+63        configs.is_interactive = interactive if not query_prompt else False
+64        super().__init__(interactive, speak, debug, cacheable, tempo, engine_name, model_name)
+65        os.environ["ASKAI_APP"] = (self.RunModes.ASKAI_CLI if interactive else self.RunModes.ASKAI_CMD).value
+66        self._ready: bool = False
+67        self._progress = Progress()
+68        self._query_prompt = query_prompt
+69        self._query_string: QueryString = query_string if isinstance(query_string, str) else " ".join(query_string)
+70        self._startup()
 
@@ -462,25 +547,25 @@

-
69    def run(self) -> None:
-70        """Run the application."""
-71        while question := (self._query_string or self._input()):
-72            status, output = self.ask_and_reply(question)
-73            if not status:
-74                question = None
-75                break
-76            elif output:
-77                cache.save_reply(question, output)
-78                cache.save_input_history()
-79                with open(self._console_path, "a+") as f_console:
-80                    f_console.write(f"{shared.username_md}{question}\n\n")
-81                    f_console.write(f"{shared.nickname_md}{output}\n\n")
-82                    f_console.flush()
-83            if not configs.is_interactive:
-84                break
-85        if question == "":
-86            self._reply(msg.goodbye())
-87        sysout("%NC%")
+            
72    def run(self) -> None:
+73        """Run the application."""
+74        while question := (self._query_string or self._input()):
+75            status, output = self.ask_and_reply(question)
+76            if not status:
+77                question = None
+78                break
+79            elif output:
+80                cache.save_reply(question, output)
+81                cache.save_input_history()
+82                with open(self._console_path, "a+") as f_console:
+83                    f_console.write(f"{shared.username_md}{question}\n\n")
+84                    f_console.write(f"{shared.nickname_md}{output}\n\n")
+85                    f_console.flush()
+86            if not configs.is_interactive:
+87                break
+88        if question == "":
+89            self._reply(AIReply.info(msg.goodbye()))
+90        display_text("", markdown=False)
 
diff --git a/docs/api/askai/main/askai/core/askai_configs.html b/docs/api/askai/main/askai/core/askai_configs.html index 1894bf70..39556bb4 100644 --- a/docs/api/askai/main/askai/core/askai_configs.html +++ b/docs/api/askai/main/askai/core/askai_configs.html @@ -3,7 +3,7 @@ - + main.askai.core.askai_configs API documentation @@ -66,12 +66,21 @@

API Documentation

  • ttl
  • +
  • + verbosity +
  • chunk_size
  • chunk_overlap
  • +
  • + rag_retrival_amount +
  • +
  • + is_rag +
  • language
  • @@ -126,6 +135,9 @@

    API Documentation

  • remove_device
  • +
  • + clear_devices +
  • @@ -174,16 +186,16 @@

    12 13 Copyright (c) 2024, HomeSetup 14""" - 15import locale - 16import os - 17from shutil import which - 18 + 15from askai.__classpath__ import classpath + 16from askai.core.askai_settings import settings + 17from askai.core.enums.verbosity import Verbosity + 18from askai.language.language import Language 19from hspylib.core.enums.charset import Charset 20from hspylib.core.metaclass.singleton import Singleton - 21 - 22from askai.__classpath__ import classpath - 23from askai.core.askai_settings import settings - 24from askai.language.language import Language + 21from shutil import which + 22 + 23import locale + 24import os 25 26 27class AskAiConfigs(metaclass=Singleton): @@ -227,7 +239,7 @@

    65 66 @is_speak.setter 67 def is_speak(self, value: bool) -> None: - 68 settings.put("askai.speak.enabled", which("ffplay") and not self.is_debug and value) + 68 settings.put("askai.speak.enabled", which("ffplay") and value) 69 70 @property 71 def is_debug(self) -> bool: @@ -262,96 +274,117 @@

    100 settings.put("askai.cache.ttl.minutes", value) 101 102 @property -103 def chunk_size(self) -> int: -104 return settings.get_int("askai.text.splitter.chunk.size") +103 def verbosity(self) -> Verbosity: +104 return Verbosity.of_value(settings.get_int("askai.verbosity.level")) 105 106 @property -107 def chunk_overlap(self) -> int: -108 return settings.get_int("askai.text.splitter.chunk.overlap") +107 def chunk_size(self) -> int: +108 return settings.get_int("askai.text.splitter.chunk.size") 109 110 @property -111 def language(self) -> Language: -112 # Lookup order: Settings -> Locale -> Environment -113 return Language.of_locale( -114 settings.get("askai.preferred.language") -115 or locale.getlocale(locale.LC_ALL) -116 or os.getenv("LC_ALL", os.getenv("LC_TYPE", os.getenv("LANG"))) -117 or Language.EN_US.idiom -118 ) -119 -120 @property -121 def encoding(self) -> Charset: -122 return self.language.encoding -123 -124 @property -125 def max_iteractions(self) -> int: -126 return settings.get_int("askai.max.router.iteractions") -127 -128 @property -129 def max_short_memory_size(self) -> int: -130 return settings.get_int("askai.max.short.memory.size") +111 def chunk_overlap(self) -> int: +112 return settings.get_int("askai.text.splitter.chunk.overlap") +113 +114 @property +115 def rag_retrival_amount(self) -> int: +116 return settings.get_int("askai.rag.retrival.amount") +117 +118 @property +119 def is_rag(self) -> bool: +120 return settings.get_int("askai.rag.enabled") +121 +122 @property +123 def language(self) -> Language: +124 """Lookup order: Settings -> Locale -> Environment.""" +125 return Language.of_locale( +126 settings.get("askai.preferred.language") +127 or locale.getlocale(locale.LC_ALL) +128 or os.getenv("LC_ALL", os.getenv("LC_TYPE", os.getenv("LANG"))) +129 or Language.EN_US.idiom +130 ) 131 132 @property -133 def max_router_retries(self) -> int: -134 return settings.get_int("askai.max.agent.retries") +133 def encoding(self) -> Charset: +134 return self.language.encoding 135 136 @property -137 def max_agent_execution_time_seconds(self) -> int: -138 return settings.get_int("askai.max.agent.execution.time.seconds") +137 def max_iteractions(self) -> int: +138 return settings.get_int("askai.max.router.iteractions") 139 140 @property -141 def face_detect_alg(self) -> str: -142 return settings.get("askai.camera.face-detect.alg") +141 def max_short_memory_size(self) -> int: +142 return settings.get_int("askai.max.short.memory.size") 143 144 @property -145 def scale_factor(self) -> float: -146 return settings.get_float("askai.camera.scale.factor") +145 def max_router_retries(self) -> int: +146 return settings.get_int("askai.max.agent.retries") 147 148 @property -149 def min_neighbors(self) -> int: -150 return settings.get_int("askai.camera.min.neighbors") +149 def max_agent_execution_time_seconds(self) -> int: +150 return settings.get_int("askai.max.agent.execution.time.seconds") 151 152 @property -153 def min_max_size(self) -> tuple[int, ...]: -154 return tuple(map(int, settings.get_list("askai.camera.min-max.size"))) +153 def face_detect_alg(self) -> str: +154 return settings.get("askai.camera.face-detect.alg") 155 156 @property -157 def max_id_distance(self) -> float: -158 return settings.get_float("askai.camera.identity.max.distance") +157 def scale_factor(self) -> float: +158 return settings.get_float("askai.camera.scale.factor") 159 160 @property -161 def recorder_phrase_limit_millis(self) -> int: -162 return settings.get_int("askai.recorder.phrase.limit.millis") +161 def min_neighbors(self) -> int: +162 return settings.get_int("askai.camera.min.neighbors") 163 164 @property -165 def recorder_silence_timeout_millis(self) -> int: -166 return settings.get_int("askai.recorder.silence.timeout.millis") +165 def min_max_size(self) -> tuple[int, ...]: +166 return tuple(map(int, settings.get_list("askai.camera.min-max.size"))) 167 168 @property -169 def recorder_noise_detection_duration_millis(self) -> int: -170 return settings.get_int("askai.recorder.noise.detection.duration.millis") +169 def max_id_distance(self) -> float: +170 return settings.get_float("askai.camera.identity.max.distance") 171 172 @property -173 def recorder_input_device_auto_swap(self) -> bool: -174 return settings.get_bool("askai.recorder.input.device.auto.swap") +173 def recorder_phrase_limit_millis(self) -> int: +174 return settings.get_int("askai.recorder.phrase.limit.millis") 175 176 @property -177 def recorder_devices(self) -> set[str]: -178 return self._recorder_devices +177 def recorder_silence_timeout_millis(self) -> int: +178 return settings.get_int("askai.recorder.silence.timeout.millis") 179 -180 def add_device(self, device_name: str) -> None: -181 """Add a new device to the configuration list.""" -182 if device_name not in self._recorder_devices: -183 self._recorder_devices.add(device_name) -184 settings.put("askai.recorder.devices", ", ".join(self._recorder_devices)) -185 -186 def remove_device(self, device_name: str) -> None: -187 """Remove a new device from the configuration list.""" -188 if device_name in self._recorder_devices: -189 self._recorder_devices.remove(device_name) -190 settings.put("askai.recorder.devices", ", ".join(self._recorder_devices)) +180 @property +181 def recorder_noise_detection_duration_millis(self) -> int: +182 return settings.get_int("askai.recorder.noise.detection.duration.millis") +183 +184 @property +185 def recorder_input_device_auto_swap(self) -> bool: +186 return settings.get_bool("askai.recorder.input.device.auto.swap") +187 +188 @property +189 def recorder_devices(self) -> set[str]: +190 return self._recorder_devices 191 -192assert (configs := AskAiConfigs().INSTANCE) is not None +192 def add_device(self, device_name: str) -> None: +193 """Add a new device to the configuration list. +194 :param device_name: The device name to be added. +195 """ +196 if device_name not in self._recorder_devices: +197 self._recorder_devices.add(device_name) +198 settings.put("askai.recorder.devices", ", ".join(self._recorder_devices)) +199 +200 def remove_device(self, device_name: str) -> None: +201 """Remove a new device from the configuration list. +202 :param device_name: The device name to be removed. +203 """ +204 if device_name in self._recorder_devices: +205 self._recorder_devices.remove(device_name) +206 settings.put("askai.recorder.devices", ", ".join(self._recorder_devices)) +207 +208 def clear_devices(self) -> None: +209 """Remove all devices from the configuration list.""" +210 self._recorder_devices.clear() +211 +212 +213assert (configs := AskAiConfigs().INSTANCE) is not None

    @@ -408,7 +441,7 @@

    66 67 @is_speak.setter 68 def is_speak(self, value: bool) -> None: - 69 settings.put("askai.speak.enabled", which("ffplay") and not self.is_debug and value) + 69 settings.put("askai.speak.enabled", which("ffplay") and value) 70 71 @property 72 def is_debug(self) -> bool: @@ -443,94 +476,114 @@

    101 settings.put("askai.cache.ttl.minutes", value) 102 103 @property -104 def chunk_size(self) -> int: -105 return settings.get_int("askai.text.splitter.chunk.size") +104 def verbosity(self) -> Verbosity: +105 return Verbosity.of_value(settings.get_int("askai.verbosity.level")) 106 107 @property -108 def chunk_overlap(self) -> int: -109 return settings.get_int("askai.text.splitter.chunk.overlap") +108 def chunk_size(self) -> int: +109 return settings.get_int("askai.text.splitter.chunk.size") 110 111 @property -112 def language(self) -> Language: -113 # Lookup order: Settings -> Locale -> Environment -114 return Language.of_locale( -115 settings.get("askai.preferred.language") -116 or locale.getlocale(locale.LC_ALL) -117 or os.getenv("LC_ALL", os.getenv("LC_TYPE", os.getenv("LANG"))) -118 or Language.EN_US.idiom -119 ) -120 -121 @property -122 def encoding(self) -> Charset: -123 return self.language.encoding -124 -125 @property -126 def max_iteractions(self) -> int: -127 return settings.get_int("askai.max.router.iteractions") -128 -129 @property -130 def max_short_memory_size(self) -> int: -131 return settings.get_int("askai.max.short.memory.size") +112 def chunk_overlap(self) -> int: +113 return settings.get_int("askai.text.splitter.chunk.overlap") +114 +115 @property +116 def rag_retrival_amount(self) -> int: +117 return settings.get_int("askai.rag.retrival.amount") +118 +119 @property +120 def is_rag(self) -> bool: +121 return settings.get_int("askai.rag.enabled") +122 +123 @property +124 def language(self) -> Language: +125 """Lookup order: Settings -> Locale -> Environment.""" +126 return Language.of_locale( +127 settings.get("askai.preferred.language") +128 or locale.getlocale(locale.LC_ALL) +129 or os.getenv("LC_ALL", os.getenv("LC_TYPE", os.getenv("LANG"))) +130 or Language.EN_US.idiom +131 ) 132 133 @property -134 def max_router_retries(self) -> int: -135 return settings.get_int("askai.max.agent.retries") +134 def encoding(self) -> Charset: +135 return self.language.encoding 136 137 @property -138 def max_agent_execution_time_seconds(self) -> int: -139 return settings.get_int("askai.max.agent.execution.time.seconds") +138 def max_iteractions(self) -> int: +139 return settings.get_int("askai.max.router.iteractions") 140 141 @property -142 def face_detect_alg(self) -> str: -143 return settings.get("askai.camera.face-detect.alg") +142 def max_short_memory_size(self) -> int: +143 return settings.get_int("askai.max.short.memory.size") 144 145 @property -146 def scale_factor(self) -> float: -147 return settings.get_float("askai.camera.scale.factor") +146 def max_router_retries(self) -> int: +147 return settings.get_int("askai.max.agent.retries") 148 149 @property -150 def min_neighbors(self) -> int: -151 return settings.get_int("askai.camera.min.neighbors") +150 def max_agent_execution_time_seconds(self) -> int: +151 return settings.get_int("askai.max.agent.execution.time.seconds") 152 153 @property -154 def min_max_size(self) -> tuple[int, ...]: -155 return tuple(map(int, settings.get_list("askai.camera.min-max.size"))) +154 def face_detect_alg(self) -> str: +155 return settings.get("askai.camera.face-detect.alg") 156 157 @property -158 def max_id_distance(self) -> float: -159 return settings.get_float("askai.camera.identity.max.distance") +158 def scale_factor(self) -> float: +159 return settings.get_float("askai.camera.scale.factor") 160 161 @property -162 def recorder_phrase_limit_millis(self) -> int: -163 return settings.get_int("askai.recorder.phrase.limit.millis") +162 def min_neighbors(self) -> int: +163 return settings.get_int("askai.camera.min.neighbors") 164 165 @property -166 def recorder_silence_timeout_millis(self) -> int: -167 return settings.get_int("askai.recorder.silence.timeout.millis") +166 def min_max_size(self) -> tuple[int, ...]: +167 return tuple(map(int, settings.get_list("askai.camera.min-max.size"))) 168 169 @property -170 def recorder_noise_detection_duration_millis(self) -> int: -171 return settings.get_int("askai.recorder.noise.detection.duration.millis") +170 def max_id_distance(self) -> float: +171 return settings.get_float("askai.camera.identity.max.distance") 172 173 @property -174 def recorder_input_device_auto_swap(self) -> bool: -175 return settings.get_bool("askai.recorder.input.device.auto.swap") +174 def recorder_phrase_limit_millis(self) -> int: +175 return settings.get_int("askai.recorder.phrase.limit.millis") 176 177 @property -178 def recorder_devices(self) -> set[str]: -179 return self._recorder_devices +178 def recorder_silence_timeout_millis(self) -> int: +179 return settings.get_int("askai.recorder.silence.timeout.millis") 180 -181 def add_device(self, device_name: str) -> None: -182 """Add a new device to the configuration list.""" -183 if device_name not in self._recorder_devices: -184 self._recorder_devices.add(device_name) -185 settings.put("askai.recorder.devices", ", ".join(self._recorder_devices)) -186 -187 def remove_device(self, device_name: str) -> None: -188 """Remove a new device from the configuration list.""" -189 if device_name in self._recorder_devices: -190 self._recorder_devices.remove(device_name) -191 settings.put("askai.recorder.devices", ", ".join(self._recorder_devices)) +181 @property +182 def recorder_noise_detection_duration_millis(self) -> int: +183 return settings.get_int("askai.recorder.noise.detection.duration.millis") +184 +185 @property +186 def recorder_input_device_auto_swap(self) -> bool: +187 return settings.get_bool("askai.recorder.input.device.auto.swap") +188 +189 @property +190 def recorder_devices(self) -> set[str]: +191 return self._recorder_devices +192 +193 def add_device(self, device_name: str) -> None: +194 """Add a new device to the configuration list. +195 :param device_name: The device name to be added. +196 """ +197 if device_name not in self._recorder_devices: +198 self._recorder_devices.add(device_name) +199 settings.put("askai.recorder.devices", ", ".join(self._recorder_devices)) +200 +201 def remove_device(self, device_name: str) -> None: +202 """Remove a new device from the configuration list. +203 :param device_name: The device name to be removed. +204 """ +205 if device_name in self._recorder_devices: +206 self._recorder_devices.remove(device_name) +207 settings.put("askai.recorder.devices", ", ".join(self._recorder_devices)) +208 +209 def clear_devices(self) -> None: +210 """Remove all devices from the configuration list.""" +211 self._recorder_devices.clear() @@ -735,6 +788,24 @@

    + +
    + +
    + verbosity: askai.core.enums.verbosity.Verbosity + + + +
    + +
    103    @property
    +104    def verbosity(self) -> Verbosity:
    +105        return Verbosity.of_value(settings.get_int("askai.verbosity.level"))
    +
    + + + +
    @@ -745,9 +816,9 @@

    -
    103    @property
    -104    def chunk_size(self) -> int:
    -105        return settings.get_int("askai.text.splitter.chunk.size")
    +            
    107    @property
    +108    def chunk_size(self) -> int:
    +109        return settings.get_int("askai.text.splitter.chunk.size")
     
    @@ -763,9 +834,45 @@

    -
    107    @property
    -108    def chunk_overlap(self) -> int:
    -109        return settings.get_int("askai.text.splitter.chunk.overlap")
    +            
    111    @property
    +112    def chunk_overlap(self) -> int:
    +113        return settings.get_int("askai.text.splitter.chunk.overlap")
    +
    + + + + +
    +
    + +
    + rag_retrival_amount: int + + + +
    + +
    115    @property
    +116    def rag_retrival_amount(self) -> int:
    +117        return settings.get_int("askai.rag.retrival.amount")
    +
    + + + + +
    +
    + +
    + is_rag: bool + + + +
    + +
    119    @property
    +120    def is_rag(self) -> bool:
    +121        return settings.get_int("askai.rag.enabled")
     
    @@ -781,19 +888,21 @@

    -
    111    @property
    -112    def language(self) -> Language:
    -113        # Lookup order: Settings -> Locale -> Environment
    -114        return Language.of_locale(
    -115            settings.get("askai.preferred.language")
    -116            or locale.getlocale(locale.LC_ALL)
    -117            or os.getenv("LC_ALL", os.getenv("LC_TYPE", os.getenv("LANG")))
    -118            or Language.EN_US.idiom
    -119        )
    +            
    123    @property
    +124    def language(self) -> Language:
    +125        """Lookup order: Settings -> Locale -> Environment."""
    +126        return Language.of_locale(
    +127            settings.get("askai.preferred.language")
    +128            or locale.getlocale(locale.LC_ALL)
    +129            or os.getenv("LC_ALL", os.getenv("LC_TYPE", os.getenv("LANG")))
    +130            or Language.EN_US.idiom
    +131        )
     
    - +

    Lookup order: Settings -> Locale -> Environment.

    +
    +
    @@ -805,9 +914,9 @@

    -
    121    @property
    -122    def encoding(self) -> Charset:
    -123        return self.language.encoding
    +            
    133    @property
    +134    def encoding(self) -> Charset:
    +135        return self.language.encoding
     
    @@ -823,9 +932,9 @@

    -
    125    @property
    -126    def max_iteractions(self) -> int:
    -127        return settings.get_int("askai.max.router.iteractions")
    +            
    137    @property
    +138    def max_iteractions(self) -> int:
    +139        return settings.get_int("askai.max.router.iteractions")
     
    @@ -841,9 +950,9 @@

    -
    129    @property
    -130    def max_short_memory_size(self) -> int:
    -131        return settings.get_int("askai.max.short.memory.size")
    +            
    141    @property
    +142    def max_short_memory_size(self) -> int:
    +143        return settings.get_int("askai.max.short.memory.size")
     
    @@ -859,9 +968,9 @@

    -
    133    @property
    -134    def max_router_retries(self) -> int:
    -135        return settings.get_int("askai.max.agent.retries")
    +            
    145    @property
    +146    def max_router_retries(self) -> int:
    +147        return settings.get_int("askai.max.agent.retries")
     
    @@ -877,9 +986,9 @@

    -
    137    @property
    -138    def max_agent_execution_time_seconds(self) -> int:
    -139        return settings.get_int("askai.max.agent.execution.time.seconds")
    +            
    149    @property
    +150    def max_agent_execution_time_seconds(self) -> int:
    +151        return settings.get_int("askai.max.agent.execution.time.seconds")
     
    @@ -895,9 +1004,9 @@

    -
    141    @property
    -142    def face_detect_alg(self) -> str:
    -143        return settings.get("askai.camera.face-detect.alg")
    +            
    153    @property
    +154    def face_detect_alg(self) -> str:
    +155        return settings.get("askai.camera.face-detect.alg")
     
    @@ -913,9 +1022,9 @@

    -
    145    @property
    -146    def scale_factor(self) -> float:
    -147        return settings.get_float("askai.camera.scale.factor")
    +            
    157    @property
    +158    def scale_factor(self) -> float:
    +159        return settings.get_float("askai.camera.scale.factor")
     
    @@ -931,9 +1040,9 @@

    -
    149    @property
    -150    def min_neighbors(self) -> int:
    -151        return settings.get_int("askai.camera.min.neighbors")
    +            
    161    @property
    +162    def min_neighbors(self) -> int:
    +163        return settings.get_int("askai.camera.min.neighbors")
     
    @@ -949,9 +1058,9 @@

    -
    153    @property
    -154    def min_max_size(self) -> tuple[int, ...]:
    -155        return tuple(map(int, settings.get_list("askai.camera.min-max.size")))
    +            
    165    @property
    +166    def min_max_size(self) -> tuple[int, ...]:
    +167        return tuple(map(int, settings.get_list("askai.camera.min-max.size")))
     
    @@ -967,9 +1076,9 @@

    -
    157    @property
    -158    def max_id_distance(self) -> float:
    -159        return settings.get_float("askai.camera.identity.max.distance")
    +            
    169    @property
    +170    def max_id_distance(self) -> float:
    +171        return settings.get_float("askai.camera.identity.max.distance")
     
    @@ -985,9 +1094,9 @@

    -
    161    @property
    -162    def recorder_phrase_limit_millis(self) -> int:
    -163        return settings.get_int("askai.recorder.phrase.limit.millis")
    +            
    173    @property
    +174    def recorder_phrase_limit_millis(self) -> int:
    +175        return settings.get_int("askai.recorder.phrase.limit.millis")
     
    @@ -1003,9 +1112,9 @@

    -
    165    @property
    -166    def recorder_silence_timeout_millis(self) -> int:
    -167        return settings.get_int("askai.recorder.silence.timeout.millis")
    +            
    177    @property
    +178    def recorder_silence_timeout_millis(self) -> int:
    +179        return settings.get_int("askai.recorder.silence.timeout.millis")
     
    @@ -1021,9 +1130,9 @@

    -
    169    @property
    -170    def recorder_noise_detection_duration_millis(self) -> int:
    -171        return settings.get_int("askai.recorder.noise.detection.duration.millis")
    +            
    181    @property
    +182    def recorder_noise_detection_duration_millis(self) -> int:
    +183        return settings.get_int("askai.recorder.noise.detection.duration.millis")
     
    @@ -1039,9 +1148,9 @@

    -
    173    @property
    -174    def recorder_input_device_auto_swap(self) -> bool:
    -175        return settings.get_bool("askai.recorder.input.device.auto.swap")
    +            
    185    @property
    +186    def recorder_input_device_auto_swap(self) -> bool:
    +187        return settings.get_bool("askai.recorder.input.device.auto.swap")
     
    @@ -1057,9 +1166,9 @@

    -
    177    @property
    -178    def recorder_devices(self) -> set[str]:
    -179        return self._recorder_devices
    +            
    189    @property
    +190    def recorder_devices(self) -> set[str]:
    +191        return self._recorder_devices
     
    @@ -1077,15 +1186,23 @@

    -
    181    def add_device(self, device_name: str) -> None:
    -182        """Add a new device to the configuration list."""
    -183        if device_name not in self._recorder_devices:
    -184            self._recorder_devices.add(device_name)
    -185            settings.put("askai.recorder.devices", ", ".join(self._recorder_devices))
    +            
    193    def add_device(self, device_name: str) -> None:
    +194        """Add a new device to the configuration list.
    +195        :param device_name: The device name to be added.
    +196        """
    +197        if device_name not in self._recorder_devices:
    +198            self._recorder_devices.add(device_name)
    +199            settings.put("askai.recorder.devices", ", ".join(self._recorder_devices))
     

    Add a new device to the configuration list.

    + +
    Parameters
    + +
      +
    • device_name: The device name to be added.
    • +
    @@ -1101,15 +1218,45 @@

    -
    187    def remove_device(self, device_name: str) -> None:
    -188        """Remove a new device from the configuration list."""
    -189        if device_name in self._recorder_devices:
    -190            self._recorder_devices.remove(device_name)
    -191            settings.put("askai.recorder.devices", ", ".join(self._recorder_devices))
    +            
    201    def remove_device(self, device_name: str) -> None:
    +202        """Remove a new device from the configuration list.
    +203        :param device_name: The device name to be removed.
    +204        """
    +205        if device_name in self._recorder_devices:
    +206            self._recorder_devices.remove(device_name)
    +207            settings.put("askai.recorder.devices", ", ".join(self._recorder_devices))
     

    Remove a new device from the configuration list.

    + +
    Parameters
    + +
      +
    • device_name: The device name to be removed.
    • +
    +
    + + +
    +
    + +
    + + def + clear_devices(self) -> None: + + + +
    + +
    209    def clear_devices(self) -> None:
    +210        """Remove all devices from the configuration list."""
    +211        self._recorder_devices.clear()
    +
    + + +

    Remove all devices from the configuration list.

    diff --git a/docs/api/askai/main/askai/core/askai_events.html b/docs/api/askai/main/askai/core/askai_events.html index 8ff394d4..d8fde4a5 100644 --- a/docs/api/askai/main/askai/core/askai_events.html +++ b/docs/api/askai/main/askai/core/askai_events.html @@ -3,7 +3,7 @@ - + main.askai.core.askai_events API documentation @@ -36,9 +36,6 @@

    API Documentation

  • REPLY_EVENT
  • -
  • - REPLY_ERROR_EVENT -
  • MIC_LISTENING_EVENT
  • @@ -113,51 +110,53 @@

    14""" 15 16from hspylib.core.enums.enumeration import Enumeration -17from hspylib.core.namespace import Namespace -18from hspylib.modules.eventbus.fluid import FluidEvent, FluidEventBus -19from typing import Optional +17from hspylib.core.exception.exceptions import InvalidArgumentError +18from hspylib.core.namespace import Namespace +19from hspylib.modules.eventbus.fluid import FluidEvent, FluidEventBus 20 21ASKAI_BUS_NAME: str = "askai-reply-bus" 22 23REPLY_EVENT: str = "askai-reply-event" 24 -25REPLY_ERROR_EVENT: str = "askai-reply-error-event" +25MIC_LISTENING_EVENT: str = "askai-mic-listening-event" 26 -27MIC_LISTENING_EVENT: str = "askai-mic-listening-event" +27DEVICE_CHANGED_EVENT: str = "askai-input-device-changed-event" 28 -29DEVICE_CHANGED_EVENT: str = "askai-input-device-changed-event" +29MODE_CHANGED_EVENT: str = "askai-routing-mode-changed-event" 30 -31MODE_CHANGED_EVENT: str = "askai-routing-mode-changed-event" -32 -33 -34class AskAiEvents(Enumeration): -35 """Facility class to provide easy access to AskAI events.""" -36 -37 # fmt: off -38 ASKAI_BUS = FluidEventBus( -39 ASKAI_BUS_NAME, -40 reply=FluidEvent(REPLY_EVENT, verbosity='normal', erase_last=False), -41 reply_error=FluidEvent(REPLY_ERROR_EVENT), -42 listening=FluidEvent(MIC_LISTENING_EVENT, listening=True), -43 device_changed=FluidEvent(DEVICE_CHANGED_EVENT, device=None), -44 mode_changed=FluidEvent(MODE_CHANGED_EVENT, mode=None, sum_path=None, glob=None), -45 ) -46 # fmt: on -47 -48 @staticmethod -49 def bus(bus_name: str) -> Optional[FluidEventBus]: -50 """Return an eventbus instance for the given name.""" -51 return next((b.bus for b in AskAiEvents.values() if b.name == bus_name), None) -52 -53 def __init__(self, bus: FluidEventBus): -54 self._bus = bus -55 -56 @property -57 def events(self) -> Namespace: -58 return self.value.events -59 -60 -61events: Namespace = AskAiEvents.ASKAI_BUS.events +31 +32class AskAiEvents(Enumeration): +33 """Facility class to provide easy access to AskAI events.""" +34 +35 # fmt: off +36 ASKAI_BUS = FluidEventBus( +37 ASKAI_BUS_NAME, +38 reply=FluidEvent(REPLY_EVENT, erase_last=False), +39 listening=FluidEvent(MIC_LISTENING_EVENT, listening=True), +40 device_changed=FluidEvent(DEVICE_CHANGED_EVENT, device=None), +41 mode_changed=FluidEvent(MODE_CHANGED_EVENT, mode=None, sum_path=None, glob=None), +42 ) +43 # fmt: on +44 +45 @staticmethod +46 def bus(bus_name: str) -> FluidEventBus: +47 """Return an event bus instance for the given name. +48 :param bus_name: The name of the event bus to retrieve. +49 :return: An instance of FluidEventBus if found; otherwise, None. +50 """ +51 if not (ret_bus := next((b.bus for b in AskAiEvents.values() if b.name == bus_name), None)): +52 raise InvalidArgumentError(f"bus '{bus_name}' does not exist!") +53 return ret_bus +54 +55 def __init__(self, bus: FluidEventBus): +56 self._bus = bus +57 +58 @property +59 def events(self) -> Namespace: +60 return self.value.events +61 +62 +63events: Namespace = AskAiEvents.ASKAI_BUS.events

    @@ -185,18 +184,6 @@

    - -
    -
    - REPLY_ERROR_EVENT: str = -'askai-reply-error-event' - - -
    - - - -
    @@ -245,31 +232,35 @@

    -
    35class AskAiEvents(Enumeration):
    -36    """Facility class to provide easy access to AskAI events."""
    -37
    -38    # fmt: off
    -39    ASKAI_BUS = FluidEventBus(
    -40        ASKAI_BUS_NAME,
    -41        reply=FluidEvent(REPLY_EVENT, verbosity='normal', erase_last=False),
    -42        reply_error=FluidEvent(REPLY_ERROR_EVENT),
    -43        listening=FluidEvent(MIC_LISTENING_EVENT, listening=True),
    -44        device_changed=FluidEvent(DEVICE_CHANGED_EVENT, device=None),
    -45        mode_changed=FluidEvent(MODE_CHANGED_EVENT, mode=None, sum_path=None, glob=None),
    -46    )
    -47    # fmt: on
    -48
    -49    @staticmethod
    -50    def bus(bus_name: str) -> Optional[FluidEventBus]:
    -51        """Return an eventbus instance for the given name."""
    -52        return next((b.bus for b in AskAiEvents.values() if b.name == bus_name), None)
    -53
    -54    def __init__(self, bus: FluidEventBus):
    -55        self._bus = bus
    -56
    -57    @property
    -58    def events(self) -> Namespace:
    -59        return self.value.events
    +            
    33class AskAiEvents(Enumeration):
    +34    """Facility class to provide easy access to AskAI events."""
    +35
    +36    # fmt: off
    +37    ASKAI_BUS = FluidEventBus(
    +38        ASKAI_BUS_NAME,
    +39        reply=FluidEvent(REPLY_EVENT, erase_last=False),
    +40        listening=FluidEvent(MIC_LISTENING_EVENT, listening=True),
    +41        device_changed=FluidEvent(DEVICE_CHANGED_EVENT, device=None),
    +42        mode_changed=FluidEvent(MODE_CHANGED_EVENT, mode=None, sum_path=None, glob=None),
    +43    )
    +44    # fmt: on
    +45
    +46    @staticmethod
    +47    def bus(bus_name: str) -> FluidEventBus:
    +48        """Return an event bus instance for the given name.
    +49        :param bus_name: The name of the event bus to retrieve.
    +50        :return: An instance of FluidEventBus if found; otherwise, None.
    +51        """
    +52        if not (ret_bus := next((b.bus for b in AskAiEvents.values() if b.name == bus_name), None)):
    +53            raise InvalidArgumentError(f"bus '{bus_name}' does not exist!")
    +54        return ret_bus
    +55
    +56    def __init__(self, bus: FluidEventBus):
    +57        self._bus = bus
    +58
    +59    @property
    +60    def events(self) -> Namespace:
    +61        return self.value.events
     
    @@ -295,20 +286,37 @@

    @staticmethod
    def - bus(bus_name: str) -> Optional[hspylib.modules.eventbus.fluid.FluidEventBus]: + bus(bus_name: str) -> hspylib.modules.eventbus.fluid.FluidEventBus:

    -
    49    @staticmethod
    -50    def bus(bus_name: str) -> Optional[FluidEventBus]:
    -51        """Return an eventbus instance for the given name."""
    -52        return next((b.bus for b in AskAiEvents.values() if b.name == bus_name), None)
    +            
    46    @staticmethod
    +47    def bus(bus_name: str) -> FluidEventBus:
    +48        """Return an event bus instance for the given name.
    +49        :param bus_name: The name of the event bus to retrieve.
    +50        :return: An instance of FluidEventBus if found; otherwise, None.
    +51        """
    +52        if not (ret_bus := next((b.bus for b in AskAiEvents.values() if b.name == bus_name), None)):
    +53            raise InvalidArgumentError(f"bus '{bus_name}' does not exist!")
    +54        return ret_bus
     
    -

    Return an eventbus instance for the given name.

    +

    Return an event bus instance for the given name.

    + +
    Parameters
    + +
      +
    • bus_name: The name of the event bus to retrieve.
    • +
    + +
    Returns
    + +
    +

    An instance of FluidEventBus if found; otherwise, None.

    +
    @@ -322,9 +330,9 @@

    -
    57    @property
    -58    def events(self) -> Namespace:
    -59        return self.value.events
    +            
    59    @property
    +60    def events(self) -> Namespace:
    +61        return self.value.events
     
    @@ -355,7 +363,7 @@
    Inherited Members
    events: hspylib.core.namespace.Namespace = - FluidEventBus::askai-reply-bus(reply=FluidEvent-askai-reply-event::(name, verbosity, erase_last, cb_event_handler, emit, subscribe), reply_error=FluidEvent-askai-reply-error-event::(name, cb_event_handler, emit, subscribe), listening=FluidEvent-askai-mic-listening-event::(name, listening, cb_event_handler, emit, subscribe), device_changed=FluidEvent-askai-input-device-changed-event::(name, device, cb_event_handler, emit, subscribe), mode_changed=FluidEvent-askai-routing-mode-changed-event::(name, mode, sum_path, glob, cb_event_handler, emit, subscribe)) + FluidEventBus::askai-reply-bus(reply=FluidEvent-askai-reply-event::(name, erase_last, cb_event_handler, emit, subscribe), listening=FluidEvent-askai-mic-listening-event::(name, listening, cb_event_handler, emit, subscribe), device_changed=FluidEvent-askai-input-device-changed-event::(name, device, cb_event_handler, emit, subscribe), mode_changed=FluidEvent-askai-routing-mode-changed-event::(name, mode, sum_path, glob, cb_event_handler, emit, subscribe))
    diff --git a/docs/api/askai/main/askai/core/askai_messages.html b/docs/api/askai/main/askai/core/askai_messages.html index edc53b6f..bdd825fc 100644 --- a/docs/api/askai/main/askai/core/askai_messages.html +++ b/docs/api/askai/main/askai/core/askai_messages.html @@ -3,7 +3,7 @@ - + main.askai.core.askai_messages API documentation @@ -111,6 +111,9 @@

    API Documentation

  • photo_captured
  • +
  • + screenshot_saved +
  • executing
  • @@ -138,9 +141,15 @@

    API Documentation

  • final_query
  • +
  • + refine_answer +
  • no_caption
  • +
  • + no_good_result +
  • no_output
  • @@ -195,6 +204,9 @@

    API Documentation

  • quote_exceeded
  • +
  • + interruption_requested +
  • @@ -243,15 +255,15 @@

    12 13 Copyright (c) 2024, HomeSetup 14""" - 15import re - 16from functools import cached_property, lru_cache - 17 - 18from hspylib.core.metaclass.singleton import Singleton - 19 - 20from askai.core.askai_configs import configs - 21from askai.language.ai_translator import AITranslator - 22from askai.language.language import Language - 23from askai.language.translators.marian import MarianTranslator + 15from askai.core.askai_configs import configs + 16from askai.language.ai_translator import AITranslator + 17from askai.language.language import Language + 18from askai.language.translators.deepl_translator import DeepLTranslator + 19from functools import cached_property, lru_cache + 20from hspylib.core.metaclass.singleton import Singleton + 21from typing import AnyStr + 22 + 23import re 24 25 26class AskAiMessages(metaclass=Singleton): @@ -259,7 +271,7 @@

    28 29 INSTANCE: "AskAiMessages" 30 - 31 TRANSLATOR: AITranslator = MarianTranslator + 31 TRANSLATOR: AITranslator = DeepLTranslator 32 33 @staticmethod 34 @lru_cache @@ -282,175 +294,193 @@

    51 return AskAiMessages.get_translator(Language.EN_US, configs.language) 52 53 @lru_cache(maxsize=256) - 54 def translate(self, text: str) -> str: - 55 """Translate text using the configured language.""" - 56 # Avoid translating debug messages. - 57 if re.match(r'^~~\[DEBUG]~~.*', text, flags=re.IGNORECASE | re.MULTILINE): - 58 return text - 59 return self.translator.translate(text) - 60 - 61 # Informational - 62 - 63 def welcome(self, username: str) -> str: - 64 return f"Welcome back {username}, How can I assist you today ?" + 54 def translate(self, text: AnyStr) -> str: + 55 """Translate text using the configured language. + 56 :param text: The text to be translated. + 57 :return: The translated text. + 58 """ + 59 # Avoid translating debug messages. + 60 if re.match(r"^~~\[DEBUG]~~.*", str(text), flags=re.IGNORECASE | re.MULTILINE): + 61 return text + 62 return self.translator.translate(str(text)) + 63 + 64 # Informational 65 - 66 def wait(self) -> str: - 67 return "I'm thinking…" + 66 def welcome(self, username: str) -> str: + 67 return f"Welcome back {username}, How can I assist you today ?" 68 - 69 def welcome_back(self) -> str: - 70 return "How may I further assist you ?" + 69 def wait(self) -> str: + 70 return "I'm thinking…" 71 - 72 def listening(self) -> str: - 73 return "I'm listening…" + 72 def welcome_back(self) -> str: + 73 return "How may I further assist you ?" 74 - 75 def transcribing(self) -> str: - 76 return "I'm processing your voice…" + 75 def listening(self) -> str: + 76 return "I'm listening…" 77 - 78 def goodbye(self) -> str: - 79 return "Goodbye, have a nice day !" + 78 def transcribing(self) -> str: + 79 return "I'm processing your voice…" 80 - 81 def smile(self, countdown: int) -> str: - 82 return f"\nSmile {str(countdown)} " + 81 def goodbye(self) -> str: + 82 return "Goodbye, have a nice day !" 83 - 84 def cmd_success(self, command_line: str) -> str: - 85 return f"OK, command `{command_line}` succeeded" + 84 def smile(self, countdown: int) -> str: + 85 return f"\nSmile {str(countdown)} " 86 - 87 def searching(self) -> str: - 88 return f"Searching on the internet…" + 87 def cmd_success(self, command_line: str) -> str: + 88 return f"OK, command `{command_line}` succeeded" 89 - 90 def scrapping(self) -> str: - 91 return f"Scrapping web site…" + 90 def searching(self) -> str: + 91 return f"Searching on the internet…" 92 - 93 def summarizing(self, path: str) -> str: - 94 return f"Summarizing docs at:'{path}'…" + 93 def scrapping(self) -> str: + 94 return f"Scrapping web site…" 95 - 96 def summary_succeeded(self, path: str, glob: str) -> str: - 97 return f"Summarization of docs at: **{path}/{glob}** succeeded !" + 96 def summarizing(self, path: str) -> str: + 97 return f"Summarizing docs at:'{path}'…" 98 - 99 def enter_qna(self) -> str: -100 return "You have *entered* the **Summarization Q & A**" + 99 def summary_succeeded(self, path: str, glob: str) -> str: +100 return f"Summarization of docs at: **{path}/{glob}** succeeded !" 101 -102 def leave_qna(self) -> str: -103 return "You have *left* the **Summarization Q & A**" +102 def enter_qna(self) -> str: +103 return "You have *entered* the **Summarization Q & A**" 104 -105 def leave_rag(self) -> str: -106 return "You have *left* the **RAG Mode**" +105 def leave_qna(self) -> str: +106 return "You have *left* the **Summarization Q & A**" 107 -108 def qna_welcome(self) -> str: -109 return " What specific information are you seeking about this content ?" +108 def leave_rag(self) -> str: +109 return "You have *left* the **RAG Mode**" 110 -111 def press_esc_enter(self) -> str: -112 return "Type [exit] to exit Q & A mode" +111 def qna_welcome(self) -> str: +112 return " What specific information are you seeking about this content ?" 113 -114 def device_switch(self, device_info: str) -> str: -115 return f"\nSwitching to Audio Input device: `{device_info}`\n" +114 def press_esc_enter(self) -> str: +115 return "Type [exit] to exit Q & A mode" 116 -117 def photo_captured(self, photo: str) -> str: -118 return f"~~[DEBUG]~~ WebCam photo captured: `{photo}`" +117 def device_switch(self, device_info: str) -> str: +118 return f"\nSwitching to Audio Input device: `{device_info}`\n" 119 -120 def executing(self, command_line: str) -> str: -121 return f"~~[DEBUG]~~ Executing: `{command_line}`…" -122 -123 # Debug messages +120 # Debug messages +121 +122 def photo_captured(self, photo: str) -> str: +123 return f"~~[DEBUG]~~ WebCam photo captured: `{photo}`" 124 -125 def analysis(self, result: str) -> str: -126 return f"~~[DEBUG]~~ Analysis result => {result}" +125 def screenshot_saved(self, screenshot: str) -> str: +126 return f"~~[DEBUG]~~ Screenshot saved: `{screenshot}`" 127 -128 def assert_acc(self, status: str, details: str) -> str: -129 match status.casefold(): -130 case 'red': -131 cl = '~~' -132 case 'yellow': -133 cl = '`' -134 case 'green': -135 cl = '*' -136 case _: -137 cl = '' -138 return f"~~[DEBUG]~~ Accuracy result => {cl}{status}:{cl} {details}" -139 -140 def action_plan(self, plan_text: str) -> str: -141 return f"~~[DEBUG]~~ Action plan > {plan_text}" -142 -143 def x_reference(self, pathname: str) -> str: -144 return f"~~[DEBUG]~~ Resolving X-References: `{pathname}`…" -145 -146 def describe_image(self, image_path: str) -> str: -147 return f"~~[DEBUG]~~ Describing image: `{image_path}`…" -148 -149 def model_select(self, model: str) -> str: -150 return f"~~[DEBUG]~~ Using routing model: `{model}`" -151 -152 def task(self, task: str) -> str: -153 return f"~~[DEBUG]~~ > `Task:` {task}" -154 -155 def final_query(self, query: str) -> str: -156 return f"~~[DEBUG]~~ > Final query: `{query}`" -157 -158 def no_caption(self) -> str: -159 return "No caption available" -160 -161 # Warnings and alerts +128 def executing(self, command_line: str) -> str: +129 return f"~~[DEBUG]~~ Executing: `{command_line}`…" +130 +131 def analysis(self, result: str) -> str: +132 return f"~~[DEBUG]~~ Analysis result => {result}" +133 +134 def assert_acc(self, status: str, details: str) -> str: +135 match status.casefold(): +136 case "red": +137 cl = "%RED%" +138 case "yellow": +139 cl = "%YELLOW%" +140 case "green": +141 cl = "%GREEN%" +142 case "blue": +143 cl = "%BLUE%" +144 case _: +145 cl = "" +146 return f"~~[DEBUG]~~ Accuracy result => {cl}{status}:%NC% {details}" +147 +148 def action_plan(self, plan_text: str) -> str: +149 return f"~~[DEBUG]~~ Action plan > {plan_text}" +150 +151 def x_reference(self, pathname: str) -> str: +152 return f"~~[DEBUG]~~ Resolving X-References: `{pathname}`…" +153 +154 def describe_image(self, image_path: str) -> str: +155 return f"~~[DEBUG]~~ Describing image: `{image_path}`…" +156 +157 def model_select(self, model: str) -> str: +158 return f"~~[DEBUG]~~ Using routing model: `{model}`" +159 +160 def task(self, task: str) -> str: +161 return f"~~[DEBUG]~~ > `Task:` {task}" 162 -163 def no_output(self, source: str) -> str: -164 return f"The {source} didn't produce an output !" +163 def final_query(self, query: str) -> str: +164 return f"~~[DEBUG]~~ > Final query: `{query}`" 165 -166 def access_grant(self) -> str: -167 return "Do you approve executing this command on you terminal (~~yes/[no]~~)?" +166 def refine_answer(self, answer: str) -> str: +167 return f"~~[DEBUG]~~ > Refining answer: `{answer}`" 168 -169 # Failures -170 -171 def no_query_string(self) -> str: -172 return "No query string was provided in non-interactive mode !" -173 -174 def invalid_response(self, response_text: str) -> str: -175 return f"Invalid query response/type => '{response_text}' !" +169 def no_caption(self) -> str: +170 return "No caption available" +171 +172 def no_good_result(self) -> str: +173 return "The search did not bring any good result" +174 +175 # Warnings and alerts 176 -177 def invalid_command(self, response_text: str) -> str: -178 return f"Invalid **AskAI** command => '{response_text}' !" +177 def no_output(self, source: str) -> str: +178 return f"The {source} didn't produce an output !" 179 -180 def cmd_no_exist(self, command: str) -> str: -181 return f"Command: `{command}' does not exist !" +180 def access_grant(self) -> str: +181 return "Do you approve executing this command on you terminal (~~yes/[no]~~)?" 182 -183 def cmd_failed(self, cmd_line: str) -> str: -184 return f"Command: `{cmd_line}' failed to execute !" -185 -186 def camera_not_open(self) -> str: -187 return "Camera is not open, or unauthorized!" -188 -189 def missing_package(self, err: ImportError) -> str: -190 return f"Unable to summarize => {str(err)}' !" -191 -192 def summary_not_possible(self, err: BaseException = None) -> str: -193 return f"Summarization was not possible {'=> ' + str(err) if err else ''}!" -194 -195 def intelligible(self, reason: str) -> str: -196 return f"Your speech was not intelligible => '{reason}' !" -197 -198 def impossible(self, reason: str) -> str: -199 return f"Impossible to fulfill your request => `{reason}` !" -200 -201 def timeout(self, reason: str) -> str: -202 return f"Time out while {reason} !" -203 -204 def llm_error(self, error: str) -> str: -205 return f"**LLM** failed to reply: {error} !" -206 -207 def fail_to_search(self, error: str) -> str: -208 return f"'Internet Search' failed: {error} !" -209 -210 def too_many_actions(self) -> str: -211 return "Failed to complete the request => 'Max chained actions reached' !" -212 -213 def unprocessable(self, reason: str) -> str: -214 return f"Sorry, {reason}" -215 -216 def quote_exceeded(self) -> str: -217 return( -218 f"Oops! Looks like you have reached your quota limit. You can add credits at: " -219 f"https://platform.openai.com/settings/organization/billing/overview") +183 # Failures +184 +185 def no_query_string(self) -> str: +186 return "No query string was provided in non-interactive mode !" +187 +188 def invalid_response(self, response_text: AnyStr) -> str: +189 return f"Invalid query response/type => '{response_text}' !" +190 +191 def invalid_command(self, response_text: AnyStr) -> str: +192 return f"Invalid **AskAI** command => '{response_text}' !" +193 +194 def cmd_no_exist(self, command: str) -> str: +195 return f"Command: `{command}' does not exist !" +196 +197 def cmd_failed(self, cmd_line: str, error_msg: str) -> str: +198 return f"Command: `{cmd_line}' failed to execute -> {error_msg}!" +199 +200 def camera_not_open(self) -> str: +201 return "Camera is not open, or unauthorized!" +202 +203 def missing_package(self, err: ImportError) -> str: +204 return f"Unable to summarize => {str(err)}' !" +205 +206 def summary_not_possible(self, err: BaseException = None) -> str: +207 return f"Summarization was not possible {'=> ' + str(err) if err else ''}!" +208 +209 def intelligible(self, reason: AnyStr) -> str: +210 return f"Your speech was not intelligible => '{reason}' !" +211 +212 def impossible(self, reason: AnyStr) -> str: +213 return f"Impossible to fulfill your request => `{reason}` !" +214 +215 def timeout(self, reason: AnyStr) -> str: +216 return f"Time out while {reason} !" +217 +218 def llm_error(self, error: AnyStr) -> str: +219 return f"**LLM** failed to reply: {error} !" 220 -221 -222assert (msg := AskAiMessages().INSTANCE) is not None +221 def fail_to_search(self, error: AnyStr) -> str: +222 return f"'Internet Search' failed: {error} !" +223 +224 def too_many_actions(self) -> AnyStr: +225 return "Failed to complete the request => 'Max chained actions reached' !" +226 +227 def unprocessable(self, reason: AnyStr) -> str: +228 return f"Sorry, {reason}" +229 +230 def quote_exceeded(self) -> str: +231 return ( +232 f"Oops! Looks like you have reached your quota limit. You can add credits at: " +233 f"https://platform.openai.com/settings/organization/billing/overview" +234 ) +235 +236 def interruption_requested(self, reason: str) -> str: +237 return f"AI has interrupted the execution => {reason}" +238 +239 +240assert (msg := AskAiMessages().INSTANCE) is not None

    @@ -471,7 +501,7 @@

    29 30 INSTANCE: "AskAiMessages" 31 - 32 TRANSLATOR: AITranslator = MarianTranslator + 32 TRANSLATOR: AITranslator = DeepLTranslator 33 34 @staticmethod 35 @lru_cache @@ -494,172 +524,190 @@

    52 return AskAiMessages.get_translator(Language.EN_US, configs.language) 53 54 @lru_cache(maxsize=256) - 55 def translate(self, text: str) -> str: - 56 """Translate text using the configured language.""" - 57 # Avoid translating debug messages. - 58 if re.match(r'^~~\[DEBUG]~~.*', text, flags=re.IGNORECASE | re.MULTILINE): - 59 return text - 60 return self.translator.translate(text) - 61 - 62 # Informational - 63 - 64 def welcome(self, username: str) -> str: - 65 return f"Welcome back {username}, How can I assist you today ?" + 55 def translate(self, text: AnyStr) -> str: + 56 """Translate text using the configured language. + 57 :param text: The text to be translated. + 58 :return: The translated text. + 59 """ + 60 # Avoid translating debug messages. + 61 if re.match(r"^~~\[DEBUG]~~.*", str(text), flags=re.IGNORECASE | re.MULTILINE): + 62 return text + 63 return self.translator.translate(str(text)) + 64 + 65 # Informational 66 - 67 def wait(self) -> str: - 68 return "I'm thinking…" + 67 def welcome(self, username: str) -> str: + 68 return f"Welcome back {username}, How can I assist you today ?" 69 - 70 def welcome_back(self) -> str: - 71 return "How may I further assist you ?" + 70 def wait(self) -> str: + 71 return "I'm thinking…" 72 - 73 def listening(self) -> str: - 74 return "I'm listening…" + 73 def welcome_back(self) -> str: + 74 return "How may I further assist you ?" 75 - 76 def transcribing(self) -> str: - 77 return "I'm processing your voice…" + 76 def listening(self) -> str: + 77 return "I'm listening…" 78 - 79 def goodbye(self) -> str: - 80 return "Goodbye, have a nice day !" + 79 def transcribing(self) -> str: + 80 return "I'm processing your voice…" 81 - 82 def smile(self, countdown: int) -> str: - 83 return f"\nSmile {str(countdown)} " + 82 def goodbye(self) -> str: + 83 return "Goodbye, have a nice day !" 84 - 85 def cmd_success(self, command_line: str) -> str: - 86 return f"OK, command `{command_line}` succeeded" + 85 def smile(self, countdown: int) -> str: + 86 return f"\nSmile {str(countdown)} " 87 - 88 def searching(self) -> str: - 89 return f"Searching on the internet…" + 88 def cmd_success(self, command_line: str) -> str: + 89 return f"OK, command `{command_line}` succeeded" 90 - 91 def scrapping(self) -> str: - 92 return f"Scrapping web site…" + 91 def searching(self) -> str: + 92 return f"Searching on the internet…" 93 - 94 def summarizing(self, path: str) -> str: - 95 return f"Summarizing docs at:'{path}'…" + 94 def scrapping(self) -> str: + 95 return f"Scrapping web site…" 96 - 97 def summary_succeeded(self, path: str, glob: str) -> str: - 98 return f"Summarization of docs at: **{path}/{glob}** succeeded !" + 97 def summarizing(self, path: str) -> str: + 98 return f"Summarizing docs at:'{path}'…" 99 -100 def enter_qna(self) -> str: -101 return "You have *entered* the **Summarization Q & A**" +100 def summary_succeeded(self, path: str, glob: str) -> str: +101 return f"Summarization of docs at: **{path}/{glob}** succeeded !" 102 -103 def leave_qna(self) -> str: -104 return "You have *left* the **Summarization Q & A**" +103 def enter_qna(self) -> str: +104 return "You have *entered* the **Summarization Q & A**" 105 -106 def leave_rag(self) -> str: -107 return "You have *left* the **RAG Mode**" +106 def leave_qna(self) -> str: +107 return "You have *left* the **Summarization Q & A**" 108 -109 def qna_welcome(self) -> str: -110 return " What specific information are you seeking about this content ?" +109 def leave_rag(self) -> str: +110 return "You have *left* the **RAG Mode**" 111 -112 def press_esc_enter(self) -> str: -113 return "Type [exit] to exit Q & A mode" +112 def qna_welcome(self) -> str: +113 return " What specific information are you seeking about this content ?" 114 -115 def device_switch(self, device_info: str) -> str: -116 return f"\nSwitching to Audio Input device: `{device_info}`\n" +115 def press_esc_enter(self) -> str: +116 return "Type [exit] to exit Q & A mode" 117 -118 def photo_captured(self, photo: str) -> str: -119 return f"~~[DEBUG]~~ WebCam photo captured: `{photo}`" +118 def device_switch(self, device_info: str) -> str: +119 return f"\nSwitching to Audio Input device: `{device_info}`\n" 120 -121 def executing(self, command_line: str) -> str: -122 return f"~~[DEBUG]~~ Executing: `{command_line}`…" -123 -124 # Debug messages +121 # Debug messages +122 +123 def photo_captured(self, photo: str) -> str: +124 return f"~~[DEBUG]~~ WebCam photo captured: `{photo}`" 125 -126 def analysis(self, result: str) -> str: -127 return f"~~[DEBUG]~~ Analysis result => {result}" +126 def screenshot_saved(self, screenshot: str) -> str: +127 return f"~~[DEBUG]~~ Screenshot saved: `{screenshot}`" 128 -129 def assert_acc(self, status: str, details: str) -> str: -130 match status.casefold(): -131 case 'red': -132 cl = '~~' -133 case 'yellow': -134 cl = '`' -135 case 'green': -136 cl = '*' -137 case _: -138 cl = '' -139 return f"~~[DEBUG]~~ Accuracy result => {cl}{status}:{cl} {details}" -140 -141 def action_plan(self, plan_text: str) -> str: -142 return f"~~[DEBUG]~~ Action plan > {plan_text}" -143 -144 def x_reference(self, pathname: str) -> str: -145 return f"~~[DEBUG]~~ Resolving X-References: `{pathname}`…" -146 -147 def describe_image(self, image_path: str) -> str: -148 return f"~~[DEBUG]~~ Describing image: `{image_path}`…" -149 -150 def model_select(self, model: str) -> str: -151 return f"~~[DEBUG]~~ Using routing model: `{model}`" -152 -153 def task(self, task: str) -> str: -154 return f"~~[DEBUG]~~ > `Task:` {task}" -155 -156 def final_query(self, query: str) -> str: -157 return f"~~[DEBUG]~~ > Final query: `{query}`" -158 -159 def no_caption(self) -> str: -160 return "No caption available" -161 -162 # Warnings and alerts +129 def executing(self, command_line: str) -> str: +130 return f"~~[DEBUG]~~ Executing: `{command_line}`…" +131 +132 def analysis(self, result: str) -> str: +133 return f"~~[DEBUG]~~ Analysis result => {result}" +134 +135 def assert_acc(self, status: str, details: str) -> str: +136 match status.casefold(): +137 case "red": +138 cl = "%RED%" +139 case "yellow": +140 cl = "%YELLOW%" +141 case "green": +142 cl = "%GREEN%" +143 case "blue": +144 cl = "%BLUE%" +145 case _: +146 cl = "" +147 return f"~~[DEBUG]~~ Accuracy result => {cl}{status}:%NC% {details}" +148 +149 def action_plan(self, plan_text: str) -> str: +150 return f"~~[DEBUG]~~ Action plan > {plan_text}" +151 +152 def x_reference(self, pathname: str) -> str: +153 return f"~~[DEBUG]~~ Resolving X-References: `{pathname}`…" +154 +155 def describe_image(self, image_path: str) -> str: +156 return f"~~[DEBUG]~~ Describing image: `{image_path}`…" +157 +158 def model_select(self, model: str) -> str: +159 return f"~~[DEBUG]~~ Using routing model: `{model}`" +160 +161 def task(self, task: str) -> str: +162 return f"~~[DEBUG]~~ > `Task:` {task}" 163 -164 def no_output(self, source: str) -> str: -165 return f"The {source} didn't produce an output !" +164 def final_query(self, query: str) -> str: +165 return f"~~[DEBUG]~~ > Final query: `{query}`" 166 -167 def access_grant(self) -> str: -168 return "Do you approve executing this command on you terminal (~~yes/[no]~~)?" +167 def refine_answer(self, answer: str) -> str: +168 return f"~~[DEBUG]~~ > Refining answer: `{answer}`" 169 -170 # Failures -171 -172 def no_query_string(self) -> str: -173 return "No query string was provided in non-interactive mode !" -174 -175 def invalid_response(self, response_text: str) -> str: -176 return f"Invalid query response/type => '{response_text}' !" +170 def no_caption(self) -> str: +171 return "No caption available" +172 +173 def no_good_result(self) -> str: +174 return "The search did not bring any good result" +175 +176 # Warnings and alerts 177 -178 def invalid_command(self, response_text: str) -> str: -179 return f"Invalid **AskAI** command => '{response_text}' !" +178 def no_output(self, source: str) -> str: +179 return f"The {source} didn't produce an output !" 180 -181 def cmd_no_exist(self, command: str) -> str: -182 return f"Command: `{command}' does not exist !" +181 def access_grant(self) -> str: +182 return "Do you approve executing this command on you terminal (~~yes/[no]~~)?" 183 -184 def cmd_failed(self, cmd_line: str) -> str: -185 return f"Command: `{cmd_line}' failed to execute !" -186 -187 def camera_not_open(self) -> str: -188 return "Camera is not open, or unauthorized!" -189 -190 def missing_package(self, err: ImportError) -> str: -191 return f"Unable to summarize => {str(err)}' !" -192 -193 def summary_not_possible(self, err: BaseException = None) -> str: -194 return f"Summarization was not possible {'=> ' + str(err) if err else ''}!" -195 -196 def intelligible(self, reason: str) -> str: -197 return f"Your speech was not intelligible => '{reason}' !" -198 -199 def impossible(self, reason: str) -> str: -200 return f"Impossible to fulfill your request => `{reason}` !" -201 -202 def timeout(self, reason: str) -> str: -203 return f"Time out while {reason} !" -204 -205 def llm_error(self, error: str) -> str: -206 return f"**LLM** failed to reply: {error} !" -207 -208 def fail_to_search(self, error: str) -> str: -209 return f"'Internet Search' failed: {error} !" -210 -211 def too_many_actions(self) -> str: -212 return "Failed to complete the request => 'Max chained actions reached' !" -213 -214 def unprocessable(self, reason: str) -> str: -215 return f"Sorry, {reason}" -216 -217 def quote_exceeded(self) -> str: -218 return( -219 f"Oops! Looks like you have reached your quota limit. You can add credits at: " -220 f"https://platform.openai.com/settings/organization/billing/overview") +184 # Failures +185 +186 def no_query_string(self) -> str: +187 return "No query string was provided in non-interactive mode !" +188 +189 def invalid_response(self, response_text: AnyStr) -> str: +190 return f"Invalid query response/type => '{response_text}' !" +191 +192 def invalid_command(self, response_text: AnyStr) -> str: +193 return f"Invalid **AskAI** command => '{response_text}' !" +194 +195 def cmd_no_exist(self, command: str) -> str: +196 return f"Command: `{command}' does not exist !" +197 +198 def cmd_failed(self, cmd_line: str, error_msg: str) -> str: +199 return f"Command: `{cmd_line}' failed to execute -> {error_msg}!" +200 +201 def camera_not_open(self) -> str: +202 return "Camera is not open, or unauthorized!" +203 +204 def missing_package(self, err: ImportError) -> str: +205 return f"Unable to summarize => {str(err)}' !" +206 +207 def summary_not_possible(self, err: BaseException = None) -> str: +208 return f"Summarization was not possible {'=> ' + str(err) if err else ''}!" +209 +210 def intelligible(self, reason: AnyStr) -> str: +211 return f"Your speech was not intelligible => '{reason}' !" +212 +213 def impossible(self, reason: AnyStr) -> str: +214 return f"Impossible to fulfill your request => `{reason}` !" +215 +216 def timeout(self, reason: AnyStr) -> str: +217 return f"Time out while {reason} !" +218 +219 def llm_error(self, error: AnyStr) -> str: +220 return f"**LLM** failed to reply: {error} !" +221 +222 def fail_to_search(self, error: AnyStr) -> str: +223 return f"'Internet Search' failed: {error} !" +224 +225 def too_many_actions(self) -> AnyStr: +226 return "Failed to complete the request => 'Max chained actions reached' !" +227 +228 def unprocessable(self, reason: AnyStr) -> str: +229 return f"Sorry, {reason}" +230 +231 def quote_exceeded(self) -> str: +232 return ( +233 f"Oops! Looks like you have reached your quota limit. You can add credits at: " +234 f"https://platform.openai.com/settings/organization/billing/overview" +235 ) +236 +237 def interruption_requested(self, reason: str) -> str: +238 return f"AI has interrupted the execution => {reason}"

    @@ -712,7 +760,7 @@

    TRANSLATOR: askai.language.ai_translator.AITranslator = -<class 'askai.language.translators.marian.MarianTranslator'> +<class 'askai.language.translators.deepl_translator.DeepLTranslator'>
    @@ -786,23 +834,38 @@

    @lru_cache(maxsize=256)
    def - translate(self, text: str) -> str: + translate(self, text: ~AnyStr) -> str:

    54    @lru_cache(maxsize=256)
    -55    def translate(self, text: str) -> str:
    -56        """Translate text using the configured language."""
    -57        # Avoid translating debug messages.
    -58        if re.match(r'^~~\[DEBUG]~~.*', text, flags=re.IGNORECASE | re.MULTILINE):
    -59            return text
    -60        return self.translator.translate(text)
    +55    def translate(self, text: AnyStr) -> str:
    +56        """Translate text using the configured language.
    +57        :param text: The text to be translated.
    +58        :return: The translated text.
    +59        """
    +60        # Avoid translating debug messages.
    +61        if re.match(r"^~~\[DEBUG]~~.*", str(text), flags=re.IGNORECASE | re.MULTILINE):
    +62            return text
    +63        return self.translator.translate(str(text))
     

    Translate text using the configured language.

    + +
    Parameters
    + +
      +
    • text: The text to be translated.
    • +
    + +
    Returns
    + +
    +

    The translated text.

    +
    @@ -818,8 +881,8 @@

    -
    64    def welcome(self, username: str) -> str:
    -65        return f"Welcome back {username}, How can I assist you today ?"
    +            
    67    def welcome(self, username: str) -> str:
    +68        return f"Welcome back {username}, How can I assist you today ?"
     
    @@ -837,8 +900,8 @@

    -
    67    def wait(self) -> str:
    -68        return "I'm thinking…"
    +            
    70    def wait(self) -> str:
    +71        return "I'm thinking…"
     
    @@ -856,8 +919,8 @@

    -
    70    def welcome_back(self) -> str:
    -71        return "How may I further assist you ?"
    +            
    73    def welcome_back(self) -> str:
    +74        return "How may I further assist you ?"
     
    @@ -875,8 +938,8 @@

    -
    73    def listening(self) -> str:
    -74        return "I'm listening…"
    +            
    76    def listening(self) -> str:
    +77        return "I'm listening…"
     
    @@ -894,8 +957,8 @@

    -
    76    def transcribing(self) -> str:
    -77        return "I'm processing your voice…"
    +            
    79    def transcribing(self) -> str:
    +80        return "I'm processing your voice…"
     
    @@ -913,8 +976,8 @@

    -
    79    def goodbye(self) -> str:
    -80        return "Goodbye, have a nice day !"
    +            
    82    def goodbye(self) -> str:
    +83        return "Goodbye, have a nice day !"
     
    @@ -932,8 +995,8 @@

    -
    82    def smile(self, countdown: int) -> str:
    -83        return f"\nSmile {str(countdown)} "
    +            
    85    def smile(self, countdown: int) -> str:
    +86        return f"\nSmile {str(countdown)} "
     
    @@ -951,8 +1014,8 @@

    -
    85    def cmd_success(self, command_line: str) -> str:
    -86        return f"OK, command `{command_line}` succeeded"
    +            
    88    def cmd_success(self, command_line: str) -> str:
    +89        return f"OK, command `{command_line}` succeeded"
     
    @@ -970,8 +1033,8 @@

    -
    88    def searching(self) -> str:
    -89        return f"Searching on the internet…"
    +            
    91    def searching(self) -> str:
    +92        return f"Searching on the internet…"
     
    @@ -989,8 +1052,8 @@

    -
    91    def scrapping(self) -> str:
    -92        return f"Scrapping web site…"
    +            
    94    def scrapping(self) -> str:
    +95        return f"Scrapping web site…"
     
    @@ -1008,8 +1071,8 @@

    -
    94    def summarizing(self, path: str) -> str:
    -95        return f"Summarizing docs at:'{path}'…"
    +            
    97    def summarizing(self, path: str) -> str:
    +98        return f"Summarizing docs at:'{path}'…"
     
    @@ -1027,8 +1090,8 @@

    -
    97    def summary_succeeded(self, path: str, glob: str) -> str:
    -98        return f"Summarization of docs at: **{path}/{glob}** succeeded !"
    +            
    100    def summary_succeeded(self, path: str, glob: str) -> str:
    +101        return f"Summarization of docs at: **{path}/{glob}** succeeded !"
     
    @@ -1046,8 +1109,8 @@

    -
    100    def enter_qna(self) -> str:
    -101        return "You have *entered* the **Summarization Q & A**"
    +            
    103    def enter_qna(self) -> str:
    +104        return "You have *entered* the **Summarization Q & A**"
     
    @@ -1065,8 +1128,8 @@

    -
    103    def leave_qna(self) -> str:
    -104        return "You have *left* the **Summarization Q & A**"
    +            
    106    def leave_qna(self) -> str:
    +107        return "You have *left* the **Summarization Q & A**"
     
    @@ -1084,8 +1147,8 @@

    -
    106    def leave_rag(self) -> str:
    -107        return "You have *left* the **RAG Mode**"
    +            
    109    def leave_rag(self) -> str:
    +110        return "You have *left* the **RAG Mode**"
     
    @@ -1103,8 +1166,8 @@

    -
    109    def qna_welcome(self) -> str:
    -110        return "  What specific information are you seeking about this content ?"
    +            
    112    def qna_welcome(self) -> str:
    +113        return "  What specific information are you seeking about this content ?"
     
    @@ -1122,8 +1185,8 @@

    -
    112    def press_esc_enter(self) -> str:
    -113        return "Type [exit] to exit Q & A mode"
    +            
    115    def press_esc_enter(self) -> str:
    +116        return "Type [exit] to exit Q & A mode"
     
    @@ -1141,8 +1204,8 @@

    -
    115    def device_switch(self, device_info: str) -> str:
    -116        return f"\nSwitching to Audio Input device: `{device_info}`\n"
    +            
    118    def device_switch(self, device_info: str) -> str:
    +119        return f"\nSwitching to Audio Input device: `{device_info}`\n"
     
    @@ -1160,8 +1223,27 @@

    -
    118    def photo_captured(self, photo: str) -> str:
    -119        return f"~~[DEBUG]~~ WebCam photo captured: `{photo}`"
    +            
    123    def photo_captured(self, photo: str) -> str:
    +124        return f"~~[DEBUG]~~ WebCam photo captured: `{photo}`"
    +
    + + + + +
    +
    + +
    + + def + screenshot_saved(self, screenshot: str) -> str: + + + +
    + +
    126    def screenshot_saved(self, screenshot: str) -> str:
    +127        return f"~~[DEBUG]~~ Screenshot saved: `{screenshot}`"
     
    @@ -1179,8 +1261,8 @@

    -
    121    def executing(self, command_line: str) -> str:
    -122        return f"~~[DEBUG]~~ Executing: `{command_line}`…"
    +            
    129    def executing(self, command_line: str) -> str:
    +130        return f"~~[DEBUG]~~ Executing: `{command_line}`…"
     
    @@ -1198,8 +1280,8 @@

    -
    126    def analysis(self, result: str) -> str:
    -127        return f"~~[DEBUG]~~ Analysis result => {result}"
    +            
    132    def analysis(self, result: str) -> str:
    +133        return f"~~[DEBUG]~~ Analysis result => {result}"
     
    @@ -1217,17 +1299,19 @@

    -
    129    def assert_acc(self, status: str, details: str) -> str:
    -130        match status.casefold():
    -131            case 'red':
    -132                cl = '~~'
    -133            case 'yellow':
    -134                cl = '`'
    -135            case 'green':
    -136                cl = '*'
    -137            case _:
    -138                cl = ''
    -139        return f"~~[DEBUG]~~ Accuracy result => {cl}{status}:{cl} {details}"
    +            
    135    def assert_acc(self, status: str, details: str) -> str:
    +136        match status.casefold():
    +137            case "red":
    +138                cl = "%RED%"
    +139            case "yellow":
    +140                cl = "%YELLOW%"
    +141            case "green":
    +142                cl = "%GREEN%"
    +143            case "blue":
    +144                cl = "%BLUE%"
    +145            case _:
    +146                cl = ""
    +147        return f"~~[DEBUG]~~ Accuracy result => {cl}{status}:%NC% {details}"
     
    @@ -1245,8 +1329,8 @@

    -
    141    def action_plan(self, plan_text: str) -> str:
    -142        return f"~~[DEBUG]~~ Action plan > {plan_text}"
    +            
    149    def action_plan(self, plan_text: str) -> str:
    +150        return f"~~[DEBUG]~~ Action plan > {plan_text}"
     
    @@ -1264,8 +1348,8 @@

    -
    144    def x_reference(self, pathname: str) -> str:
    -145        return f"~~[DEBUG]~~ Resolving X-References: `{pathname}`…"
    +            
    152    def x_reference(self, pathname: str) -> str:
    +153        return f"~~[DEBUG]~~ Resolving X-References: `{pathname}`…"
     
    @@ -1283,8 +1367,8 @@

    -
    147    def describe_image(self, image_path: str) -> str:
    -148        return f"~~[DEBUG]~~ Describing image: `{image_path}`…"
    +            
    155    def describe_image(self, image_path: str) -> str:
    +156        return f"~~[DEBUG]~~ Describing image: `{image_path}`…"
     
    @@ -1302,8 +1386,8 @@

    -
    150    def model_select(self, model: str) -> str:
    -151        return f"~~[DEBUG]~~ Using routing model: `{model}`"
    +            
    158    def model_select(self, model: str) -> str:
    +159        return f"~~[DEBUG]~~ Using routing model: `{model}`"
     
    @@ -1321,8 +1405,8 @@

    -
    153    def task(self, task: str) -> str:
    -154        return f"~~[DEBUG]~~ > `Task:` {task}"
    +            
    161    def task(self, task: str) -> str:
    +162        return f"~~[DEBUG]~~ > `Task:` {task}"
     
    @@ -1340,8 +1424,27 @@

    -
    156    def final_query(self, query: str) -> str:
    -157        return f"~~[DEBUG]~~ > Final query: `{query}`"
    +            
    164    def final_query(self, query: str) -> str:
    +165        return f"~~[DEBUG]~~ > Final query: `{query}`"
    +
    + + + + +
    +
    + +
    + + def + refine_answer(self, answer: str) -> str: + + + +
    + +
    167    def refine_answer(self, answer: str) -> str:
    +168        return f"~~[DEBUG]~~ > Refining answer: `{answer}`"
     
    @@ -1359,8 +1462,27 @@

    -
    159    def no_caption(self) -> str:
    -160        return "No caption available"
    +            
    170    def no_caption(self) -> str:
    +171        return "No caption available"
    +
    + + + + +
    +
    + +
    + + def + no_good_result(self) -> str: + + + +
    + +
    173    def no_good_result(self) -> str:
    +174        return "The search did not bring any good result"
     
    @@ -1378,8 +1500,8 @@

    -
    164    def no_output(self, source: str) -> str:
    -165        return f"The {source} didn't produce an output !"
    +            
    178    def no_output(self, source: str) -> str:
    +179        return f"The {source} didn't produce an output !"
     
    @@ -1397,8 +1519,8 @@

    -
    167    def access_grant(self) -> str:
    -168        return "Do you approve executing this command on you terminal (~~yes/[no]~~)?"
    +            
    181    def access_grant(self) -> str:
    +182        return "Do you approve executing this command on you terminal (~~yes/[no]~~)?"
     
    @@ -1416,8 +1538,8 @@

    -
    172    def no_query_string(self) -> str:
    -173        return "No query string was provided in non-interactive mode !"
    +            
    186    def no_query_string(self) -> str:
    +187        return "No query string was provided in non-interactive mode !"
     
    @@ -1429,14 +1551,14 @@

    def - invalid_response(self, response_text: str) -> str: + invalid_response(self, response_text: ~AnyStr) -> str:
    -
    175    def invalid_response(self, response_text: str) -> str:
    -176        return f"Invalid query response/type => '{response_text}' !"
    +            
    189    def invalid_response(self, response_text: AnyStr) -> str:
    +190        return f"Invalid query response/type => '{response_text}' !"
     
    @@ -1448,14 +1570,14 @@

    def - invalid_command(self, response_text: str) -> str: + invalid_command(self, response_text: ~AnyStr) -> str:
    -
    178    def invalid_command(self, response_text: str) -> str:
    -179        return f"Invalid **AskAI** command => '{response_text}' !"
    +            
    192    def invalid_command(self, response_text: AnyStr) -> str:
    +193        return f"Invalid **AskAI** command => '{response_text}' !"
     
    @@ -1473,8 +1595,8 @@

    -
    181    def cmd_no_exist(self, command: str) -> str:
    -182        return f"Command: `{command}' does not exist !"
    +            
    195    def cmd_no_exist(self, command: str) -> str:
    +196        return f"Command: `{command}' does not exist !"
     
    @@ -1486,14 +1608,14 @@

    def - cmd_failed(self, cmd_line: str) -> str: + cmd_failed(self, cmd_line: str, error_msg: str) -> str:
    -
    184    def cmd_failed(self, cmd_line: str) -> str:
    -185        return f"Command: `{cmd_line}' failed to execute !"
    +            
    198    def cmd_failed(self, cmd_line: str, error_msg: str) -> str:
    +199        return f"Command: `{cmd_line}' failed to execute -> {error_msg}!"
     
    @@ -1511,8 +1633,8 @@

    -
    187    def camera_not_open(self) -> str:
    -188        return "Camera is not open, or unauthorized!"
    +            
    201    def camera_not_open(self) -> str:
    +202        return "Camera is not open, or unauthorized!"
     
    @@ -1530,8 +1652,8 @@

    -
    190    def missing_package(self, err: ImportError) -> str:
    -191        return f"Unable to summarize => {str(err)}' !"
    +            
    204    def missing_package(self, err: ImportError) -> str:
    +205        return f"Unable to summarize => {str(err)}' !"
     
    @@ -1549,8 +1671,8 @@

    -
    193    def summary_not_possible(self, err: BaseException = None) -> str:
    -194        return f"Summarization was not possible {'=> ' + str(err) if err else ''}!"
    +            
    207    def summary_not_possible(self, err: BaseException = None) -> str:
    +208        return f"Summarization was not possible {'=> ' + str(err) if err else ''}!"
     
    @@ -1562,14 +1684,14 @@

    def - intelligible(self, reason: str) -> str: + intelligible(self, reason: ~AnyStr) -> str:
    -
    196    def intelligible(self, reason: str) -> str:
    -197        return f"Your speech was not intelligible => '{reason}' !"
    +            
    210    def intelligible(self, reason: AnyStr) -> str:
    +211        return f"Your speech was not intelligible => '{reason}' !"
     
    @@ -1581,14 +1703,14 @@

    def - impossible(self, reason: str) -> str: + impossible(self, reason: ~AnyStr) -> str:
    -
    199    def impossible(self, reason: str) -> str:
    -200        return f"Impossible to fulfill your request => `{reason}` !"
    +            
    213    def impossible(self, reason: AnyStr) -> str:
    +214        return f"Impossible to fulfill your request => `{reason}` !"
     
    @@ -1600,14 +1722,14 @@

    def - timeout(self, reason: str) -> str: + timeout(self, reason: ~AnyStr) -> str:
    -
    202    def timeout(self, reason: str) -> str:
    -203        return f"Time out while {reason} !"
    +            
    216    def timeout(self, reason: AnyStr) -> str:
    +217        return f"Time out while {reason} !"
     
    @@ -1619,14 +1741,14 @@

    def - llm_error(self, error: str) -> str: + llm_error(self, error: ~AnyStr) -> str:
    -
    205    def llm_error(self, error: str) -> str:
    -206        return f"**LLM** failed to reply: {error} !"
    +            
    219    def llm_error(self, error: AnyStr) -> str:
    +220        return f"**LLM** failed to reply: {error} !"
     
    @@ -1638,14 +1760,14 @@

    def - fail_to_search(self, error: str) -> str: + fail_to_search(self, error: ~AnyStr) -> str:
    -
    208    def fail_to_search(self, error: str) -> str:
    -209        return f"'Internet Search' failed: {error} !"
    +            
    222    def fail_to_search(self, error: AnyStr) -> str:
    +223        return f"'Internet Search' failed: {error} !"
     
    @@ -1657,14 +1779,14 @@

    def - too_many_actions(self) -> str: + too_many_actions(self) -> ~AnyStr:
    -
    211    def too_many_actions(self) -> str:
    -212        return "Failed to complete the request => 'Max chained actions reached' !"
    +            
    225    def too_many_actions(self) -> AnyStr:
    +226        return "Failed to complete the request => 'Max chained actions reached' !"
     
    @@ -1676,14 +1798,14 @@

    def - unprocessable(self, reason: str) -> str: + unprocessable(self, reason: ~AnyStr) -> str:
    -
    214    def unprocessable(self, reason: str) -> str:
    -215        return f"Sorry, {reason}"
    +            
    228    def unprocessable(self, reason: AnyStr) -> str:
    +229        return f"Sorry, {reason}"
     
    @@ -1701,10 +1823,30 @@

    -
    217    def quote_exceeded(self) -> str:
    -218        return(
    -219            f"Oops! Looks like you have reached your quota limit. You can add credits at: "
    -220            f"https://platform.openai.com/settings/organization/billing/overview")
    +            
    231    def quote_exceeded(self) -> str:
    +232        return (
    +233            f"Oops! Looks like you have reached your quota limit. You can add credits at: "
    +234            f"https://platform.openai.com/settings/organization/billing/overview"
    +235        )
    +
    + + + + +
    +
    + +
    + + def + interruption_requested(self, reason: str) -> str: + + + +
    + +
    237    def interruption_requested(self, reason: str) -> str:
    +238        return f"AI has interrupted the execution => {reason}"
     
    diff --git a/docs/api/askai/main/askai/core/askai_prompt.html b/docs/api/askai/main/askai/core/askai_prompt.html index a14a0a67..603fc37c 100644 --- a/docs/api/askai/main/askai/core/askai_prompt.html +++ b/docs/api/askai/main/askai/core/askai_prompt.html @@ -3,7 +3,7 @@ - + main.askai.core.askai_prompt API documentation @@ -108,56 +108,61 @@

    12 13 Copyright (c) 2024, HomeSetup 14""" -15from functools import lru_cache -16 -17from hspylib.core.metaclass.singleton import Singleton -18 -19from askai.__classpath__ import classpath -20from askai.core.askai_configs import configs -21from askai.core.support.platform import get_os, get_shell, get_user, SupportedPlatforms, SupportedShells -22from askai.core.support.utilities import read_resource -23 -24 -25class AskAiPrompt(metaclass=Singleton): -26 """Provide the prompts used by the AskAi.""" +15from askai.__classpath__ import classpath +16from askai.core.askai_configs import configs +17from askai.core.support.platform import get_os, get_shell, get_user, SupportedPlatforms, SupportedShells +18from askai.core.support.utilities import read_resource +19from functools import lru_cache +20from hspylib.core.metaclass.singleton import Singleton +21 +22 +23class AskAiPrompt(metaclass=Singleton): +24 """Provide the prompts used by the AskAi.""" +25 +26 INSTANCE: "AskAiPrompt" 27 -28 INSTANCE: "AskAiPrompt" -29 -30 # AI Prompts directory. -31 PROMPT_DIR = str(classpath.resource_path()) + "/prompts" -32 -33 def __init__(self): -34 self._shell: SupportedShells = get_shell() -35 self._os_type: SupportedPlatforms = get_os() -36 self._user: str = get_user() -37 -38 @property -39 def os_type(self) -> SupportedPlatforms: -40 return self._os_type -41 -42 @property -43 def shell(self) -> SupportedShells: -44 return self._shell -45 -46 @property -47 def user(self) -> str: -48 return self._user -49 -50 @property -51 def idiom(self) -> str: -52 return f"{configs.language.name} ({configs.language.country})" -53 -54 @lru_cache -55 def read_prompt(self, template_file: str, prompt_dir: str = None) -> str: -56 """Read a processor prompt template and set its persona.""" -57 return read_resource(prompt_dir or self.PROMPT_DIR, template_file) -58 -59 def append_path(self, path: str) -> str: -60 """Return the PROMPT_DIR with the extra path appended.""" -61 return f"{self.PROMPT_DIR}/{path}" -62 -63 -64assert (prompt := AskAiPrompt().INSTANCE) is not None +28 # AI Prompts directory. +29 PROMPT_DIR = str(classpath.resource_path()) + "/prompts" +30 +31 def __init__(self): +32 self._shell: SupportedShells = get_shell() +33 self._os_type: SupportedPlatforms = get_os() +34 self._user: str = get_user() +35 +36 @property +37 def os_type(self) -> SupportedPlatforms: +38 return self._os_type +39 +40 @property +41 def shell(self) -> SupportedShells: +42 return self._shell +43 +44 @property +45 def user(self) -> str: +46 return self._user +47 +48 @property +49 def idiom(self) -> str: +50 return f"{configs.language.name} ({configs.language.country})" +51 +52 @lru_cache +53 def read_prompt(self, template_file: str, prompt_dir: str = None) -> str: +54 """Read a processor prompt template. +55 :param template_file: The name of the template file to read. +56 :param prompt_dir: Optional directory where the template file is located. +57 :return: The content of the prompt template as a string. +58 """ +59 return read_resource(prompt_dir or self.PROMPT_DIR, template_file) +60 +61 def append_path(self, path: str) -> str: +62 """Return the PROMPT_DIR with the extra path appended. +63 :param path: The path to append to PROMPT_DIR. +64 :return: The concatenated path. +65 """ +66 return f"{self.PROMPT_DIR}/{path}" +67 +68 +69assert (prompt := AskAiPrompt().INSTANCE) is not None

    @@ -173,43 +178,50 @@

    -
    26class AskAiPrompt(metaclass=Singleton):
    -27    """Provide the prompts used by the AskAi."""
    +            
    24class AskAiPrompt(metaclass=Singleton):
    +25    """Provide the prompts used by the AskAi."""
    +26
    +27    INSTANCE: "AskAiPrompt"
     28
    -29    INSTANCE: "AskAiPrompt"
    -30
    -31    # AI Prompts directory.
    -32    PROMPT_DIR = str(classpath.resource_path()) + "/prompts"
    -33
    -34    def __init__(self):
    -35        self._shell: SupportedShells = get_shell()
    -36        self._os_type: SupportedPlatforms = get_os()
    -37        self._user: str = get_user()
    -38
    -39    @property
    -40    def os_type(self) -> SupportedPlatforms:
    -41        return self._os_type
    -42
    -43    @property
    -44    def shell(self) -> SupportedShells:
    -45        return self._shell
    -46
    -47    @property
    -48    def user(self) -> str:
    -49        return self._user
    -50
    -51    @property
    -52    def idiom(self) -> str:
    -53        return f"{configs.language.name} ({configs.language.country})"
    -54
    -55    @lru_cache
    -56    def read_prompt(self, template_file: str, prompt_dir: str = None) -> str:
    -57        """Read a processor prompt template and set its persona."""
    -58        return read_resource(prompt_dir or self.PROMPT_DIR, template_file)
    -59
    -60    def append_path(self, path: str) -> str:
    -61        """Return the PROMPT_DIR with the extra path appended."""
    -62        return f"{self.PROMPT_DIR}/{path}"
    +29    # AI Prompts directory.
    +30    PROMPT_DIR = str(classpath.resource_path()) + "/prompts"
    +31
    +32    def __init__(self):
    +33        self._shell: SupportedShells = get_shell()
    +34        self._os_type: SupportedPlatforms = get_os()
    +35        self._user: str = get_user()
    +36
    +37    @property
    +38    def os_type(self) -> SupportedPlatforms:
    +39        return self._os_type
    +40
    +41    @property
    +42    def shell(self) -> SupportedShells:
    +43        return self._shell
    +44
    +45    @property
    +46    def user(self) -> str:
    +47        return self._user
    +48
    +49    @property
    +50    def idiom(self) -> str:
    +51        return f"{configs.language.name} ({configs.language.country})"
    +52
    +53    @lru_cache
    +54    def read_prompt(self, template_file: str, prompt_dir: str = None) -> str:
    +55        """Read a processor prompt template.
    +56        :param template_file: The name of the template file to read.
    +57        :param prompt_dir: Optional directory where the template file is located.
    +58        :return: The content of the prompt template as a string.
    +59        """
    +60        return read_resource(prompt_dir or self.PROMPT_DIR, template_file)
    +61
    +62    def append_path(self, path: str) -> str:
    +63        """Return the PROMPT_DIR with the extra path appended.
    +64        :param path: The path to append to PROMPT_DIR.
    +65        :return: The concatenated path.
    +66        """
    +67        return f"{self.PROMPT_DIR}/{path}"
     
    @@ -280,9 +292,9 @@

    -
    39    @property
    -40    def os_type(self) -> SupportedPlatforms:
    -41        return self._os_type
    +            
    37    @property
    +38    def os_type(self) -> SupportedPlatforms:
    +39        return self._os_type
     
    @@ -298,9 +310,9 @@

    -
    43    @property
    -44    def shell(self) -> SupportedShells:
    -45        return self._shell
    +            
    41    @property
    +42    def shell(self) -> SupportedShells:
    +43        return self._shell
     
    @@ -316,9 +328,9 @@

    -
    47    @property
    -48    def user(self) -> str:
    -49        return self._user
    +            
    45    @property
    +46    def user(self) -> str:
    +47        return self._user
     
    @@ -334,9 +346,9 @@

    -
    51    @property
    -52    def idiom(self) -> str:
    -53        return f"{configs.language.name} ({configs.language.country})"
    +            
    49    @property
    +50    def idiom(self) -> str:
    +51        return f"{configs.language.name} ({configs.language.country})"
     
    @@ -355,14 +367,31 @@

    -
    55    @lru_cache
    -56    def read_prompt(self, template_file: str, prompt_dir: str = None) -> str:
    -57        """Read a processor prompt template and set its persona."""
    -58        return read_resource(prompt_dir or self.PROMPT_DIR, template_file)
    +            
    53    @lru_cache
    +54    def read_prompt(self, template_file: str, prompt_dir: str = None) -> str:
    +55        """Read a processor prompt template.
    +56        :param template_file: The name of the template file to read.
    +57        :param prompt_dir: Optional directory where the template file is located.
    +58        :return: The content of the prompt template as a string.
    +59        """
    +60        return read_resource(prompt_dir or self.PROMPT_DIR, template_file)
     
    -

    Read a processor prompt template and set its persona.

    +

    Read a processor prompt template.

    + +
    Parameters
    + +
      +
    • template_file: The name of the template file to read.
    • +
    • prompt_dir: Optional directory where the template file is located.
    • +
    + +
    Returns
    + +
    +

    The content of the prompt template as a string.

    +
    @@ -378,13 +407,28 @@

    -
    60    def append_path(self, path: str) -> str:
    -61        """Return the PROMPT_DIR with the extra path appended."""
    -62        return f"{self.PROMPT_DIR}/{path}"
    +            
    62    def append_path(self, path: str) -> str:
    +63        """Return the PROMPT_DIR with the extra path appended.
    +64        :param path: The path to append to PROMPT_DIR.
    +65        :return: The concatenated path.
    +66        """
    +67        return f"{self.PROMPT_DIR}/{path}"
     

    Return the PROMPT_DIR with the extra path appended.

    + +
    Parameters
    + +
      +
    • path: The path to append to PROMPT_DIR.
    • +
    + +
    Returns
    + +
    +

    The concatenated path.

    +
    diff --git a/docs/api/askai/main/askai/core/askai_settings.html b/docs/api/askai/main/askai/core/askai_settings.html index 148d9769..fae51d7f 100644 --- a/docs/api/askai/main/askai/core/askai_settings.html +++ b/docs/api/askai/main/askai/core/askai_settings.html @@ -3,7 +3,7 @@ - + main.askai.core.askai_settings API documentation @@ -42,9 +42,6 @@

    API Documentation

  • INSTANCE
  • -
  • - RESOURCE_DIR -
  • settings
  • @@ -121,12 +118,12 @@

    13 Copyright (c) 2024, HomeSetup 14""" 15from askai.__classpath__ import classpath - 16from askai.core.support.text_formatter import text_formatter - 17from contextlib import redirect_stdout - 18from hspylib.core.metaclass.singleton import Singleton - 19from hspylib.core.tools.commons import to_bool - 20from io import StringIO - 21from pathlib import Path + 16from contextlib import redirect_stdout + 17from hspylib.core.metaclass.singleton import Singleton + 18from hspylib.core.tools.commons import console_out, to_bool + 19from io import StringIO + 20from pathlib import Path + 21from rich import box 22from rich.table import Table 23from setman.settings.settings import Settings 24from setman.settings.settings_config import SettingsConfig @@ -147,123 +144,159 @@

    39 40 41class AskAiSettings(metaclass=Singleton): - 42 """The Setman Settings.""" + 42 """The AskAI 'SetMan' Settings.""" 43 44 INSTANCE: "AskAiSettings" 45 - 46 RESOURCE_DIR = str(classpath.resource_path()) - 47 - 48 _ACTUAL_VERSION: str = "0.1.8" - 49 - 50 def __init__(self) -> None: - 51 self._configs = SettingsConfig(self.RESOURCE_DIR, "application.properties") - 52 self._settings = Settings(self._configs) - 53 if not self._settings.count() or self.get("askai.settings.version.id") != self._ACTUAL_VERSION: - 54 self.defaults() - 55 - 56 def __str__(self) -> str: - 57 return self.search() - 58 - 59 def __repr__(self) -> str: - 60 return self.__str__() - 61 - 62 def __getitem__(self, name: str) -> Optional[SettingsEntry]: - 63 return self._settings.get(name) - 64 - 65 def __setitem__(self, name: str, item: Any) -> None: - 66 self._settings.put(name, name[: name.find(".")], item) - 67 - 68 @property - 69 def settings(self) -> Settings: - 70 return self._settings - 71 - 72 def search(self, filters: str | None = None) -> Optional[str]: - 73 """Search setting using the specified filters. - 74 :param filters: Filters used on the search. - 75 """ - 76 data = [(s.name, s.value) for s in self._settings.search(filters)] - 77 if data: - 78 table = Table(title="AskAI - Settings") - 79 table.add_column("No.", style="white", no_wrap=True) - 80 table.add_column("Name", style="cyan", no_wrap=True) - 81 table.add_column("Value", style="green", no_wrap=True) - 82 for i, d in enumerate(data): - 83 table.add_row(str(i), d[0], d[1]) - 84 with StringIO() as buf, redirect_stdout(buf): - 85 text_formatter.console.print(table) - 86 return buf.getvalue() - 87 return None - 88 - 89 def defaults(self) -> None: - 90 """Create the default settings database if they doesn't exist.""" - 91 # AskAI General - 92 self._settings.clear() - 93 self._settings.put("askai.settings.version.id", "askai", self._ACTUAL_VERSION) - 94 self._settings.put("askai.debug.enabled", "askai", False) - 95 self._settings.put("askai.speak.enabled", "askai", False) - 96 self._settings.put("askai.cache.enabled", "askai", False) - 97 self._settings.put("askai.cache.ttl.minutes", "askai", 25) - 98 self._settings.put("askai.text.to.speech.tempo", "askai", 1) - 99 self._settings.put("askai.text.splitter.chunk.size", "askai", 1000) -100 self._settings.put("askai.text.splitter.chunk.overlap", "askai", 100) -101 self._settings.put("askai.preferred.language", "askai", '') -102 self._settings.put("askai.default.engine", "askai", "openai") -103 self._settings.put("askai.default.engine.model", "askai", "gpt-4o-mini") -104 # Router -105 self._settings.put("askai.max.short.memory.size", "askai", 10) -106 self._settings.put("askai.max.router.iteractions", "askai", 30) -107 self._settings.put("askai.max.agent.retries", "askai", 3) -108 self._settings.put("askai.max.agent.execution.time.seconds", "askai", 45) -109 # Recorder -110 self._settings.put("askai.recorder.devices", "askai", "") -111 self._settings.put("askai.recorder.silence.timeout.millis", "askai", 1200) -112 self._settings.put("askai.recorder.phrase.limit.millis", "askai", 10000) -113 self._settings.put("askai.recorder.noise.detection.duration.millis", "askai", 600) -114 self._settings.put("askai.recorder.input.device.auto.swap", "askai", True) -115 # Camera -116 self._settings.put("askai.camera.face-detect.alg", "askai", "haarcascade_frontalface_default.xml") -117 self._settings.put("askai.camera.scale.factor", "askai", 1.1) -118 self._settings.put("askai.camera.min.neighbors", "askai", 3) -119 self._settings.put("askai.camera.min-max.size", "askai", "60, 60") -120 self._settings.put("askai.camera.identity.max.distance", "askai", 0.70) -121 # OpenAI -122 self._settings.put("openai.speech.to.text.model", "openai", "whisper-1") -123 self._settings.put("openai.text.to.speech.model", "openai", "tts-1") -124 self._settings.put("openai.text.to.speech.voice", "openai", "onyx") -125 self._settings.put("openai.text.to.speech.audio.format", "openai", "mp3") -126 log.debug(f"Settings database created !") -127 -128 def get(self, key: str, default_value: str | None = "") -> str: -129 val = self.__getitem__(key) -130 return str(val.value) if val else default_value -131 -132 def put(self, key: str, value: Any) -> None: -133 self.__setitem__(key, value) -134 -135 def get_bool(self, key: str, default_value: bool | None = False) -> bool: -136 return to_bool(self.get(key) or default_value) -137 -138 def get_int(self, key: str, default_value: int | None = 0) -> int: -139 try: -140 return int(self.get(key) or default_value) -141 except ValueError: -142 return 0 -143 -144 def get_float(self, key: str, default_value: float | None = 0.0) -> float: -145 try: -146 return float(self.get(key) or default_value) -147 except ValueError: -148 return 0 -149 -150 def get_list(self, key: str, default_value: list | None = None) -> list: -151 str_val: str = self.get(key) or "" -152 val: list | None = None -153 if str_val: -154 val: list = re.split(r"[;,|]", str_val) -155 return val or default_value or [] + 46 # Current settings version. Updating this value will trigger a database recreation using the defaults. + 47 __ACTUAL_VERSION: str = "0.2.1" + 48 + 49 __RESOURCE_DIR = str(classpath.resource_path()) + 50 + 51 def __init__(self) -> None: + 52 self._configs = SettingsConfig(self.__RESOURCE_DIR, "application.properties") + 53 self._settings = Settings(self._configs) + 54 if not self._settings.count() or self.get("askai.settings.version.id") != self.__ACTUAL_VERSION: + 55 self.defaults() + 56 + 57 def __str__(self) -> str: + 58 return self.search() + 59 + 60 def __repr__(self) -> str: + 61 return self.__str__() + 62 + 63 def __getitem__(self, name: str) -> Optional[SettingsEntry]: + 64 return self._settings.get(name) + 65 + 66 def __setitem__(self, name: str, item: Any) -> None: + 67 self._settings.put(name, name[: name.find(".")], item) + 68 + 69 @property + 70 def settings(self) -> Settings: + 71 return self._settings + 72 + 73 def search(self, filters: str | None = None) -> Optional[str]: + 74 """Search settings using the specified filters. + 75 :param filters: Optional filters to apply to the search. + 76 :return: The search results as a string, or None if no results are found. + 77 """ + 78 data = [(s.name, s.value) for s in self._settings.search(filters)] + 79 if data: + 80 table = Table(title="AskAI - Settings", box=box.SQUARE_DOUBLE_HEAD) + 81 table.add_column("No.", style="white", no_wrap=True) + 82 table.add_column("Name", style="cyan", no_wrap=True) + 83 table.add_column("Value", style="green", no_wrap=True) + 84 for i, d in enumerate(data): + 85 table.add_row(str(i), d[0], d[1]) + 86 with StringIO() as buf, redirect_stdout(buf): + 87 console_out.print(table) + 88 return buf.getvalue() + 89 return None + 90 + 91 def defaults(self) -> None: + 92 """Create the default settings database.""" + 93 # AskAI General + 94 self._settings.clear("askai.*") + 95 self._settings.put("askai.settings.version.id", "askai", self.__ACTUAL_VERSION) + 96 self._settings.put("askai.debug.enabled", "askai", False) + 97 self._settings.put("askai.speak.enabled", "askai", False) + 98 self._settings.put("askai.cache.enabled", "askai", False) + 99 self._settings.put("askai.cache.ttl.minutes", "askai", 25) +100 self._settings.put("askai.preferred.language", "askai", "") +101 self._settings.put("askai.default.engine", "askai", "openai") +102 self._settings.put("askai.default.engine.model", "askai", "gpt-4o-mini") +103 self._settings.put("askai.verbosity.level", "askai", 3) +104 self._settings.put("askai.text.to.speech.tempo", "askai", 1) +105 self._settings.put("askai.text.splitter.chunk.size", "askai", 1000) +106 self._settings.put("askai.text.splitter.chunk.overlap", "askai", 100) +107 self._settings.put("askai.rag.retrival.amount", "askai", 3) +108 self._settings.put("askai.rag.enabled", "askai", True) +109 # Router +110 self._settings.put("askai.max.short.memory.size", "askai", 15) +111 self._settings.put("askai.max.router.iteractions", "askai", 30) +112 self._settings.put("askai.max.agent.retries", "askai", 3) +113 self._settings.put("askai.max.agent.execution.time.seconds", "askai", 45) +114 # Recorder +115 self._settings.put("askai.recorder.devices", "askai", "") +116 self._settings.put("askai.recorder.silence.timeout.millis", "askai", 1200) +117 self._settings.put("askai.recorder.phrase.limit.millis", "askai", 10000) +118 self._settings.put("askai.recorder.noise.detection.duration.millis", "askai", 600) +119 self._settings.put("askai.recorder.input.device.auto.swap", "askai", True) +120 # Camera +121 self._settings.put("askai.camera.face-detect.alg", "askai", "haarcascade_frontalface_default.xml") +122 self._settings.put("askai.camera.scale.factor", "askai", 1.1) +123 self._settings.put("askai.camera.min.neighbors", "askai", 3) +124 self._settings.put("askai.camera.min-max.size", "askai", "100, 100") +125 self._settings.put("askai.camera.identity.max.distance", "askai", 0.70) +126 # OpenAI +127 self._settings.put("askai.openai.speech.to.text.model", "askai", "whisper-1") +128 self._settings.put("askai.openai.text.to.speech.model", "askai", "tts-1") +129 self._settings.put("askai.openai.text.to.speech.voice", "askai", "onyx") +130 self._settings.put("askai.openai.text.to.speech.audio.format", "askai", "mp3") +131 log.debug(f"Settings database created !") +132 +133 def get(self, key: str, default_value: str | None = "") -> str: +134 """Retrieve the setting specified by the given key. +135 :param key: The name of the setting to retrieve. +136 :param default_value: The value to return if the setting does not exist. +137 :return: The setting value if it exists, otherwise the default_value. +138 """ +139 val = self.__getitem__(key) +140 return str(val.value) if val else default_value +141 +142 def put(self, key: str, value: Any) -> None: +143 """Set the setting specified by the given key. +144 :param key: The name of the setting to update. +145 :param value: The value to associate with the key. +146 """ +147 self.__setitem__(key, value) +148 +149 def get_bool(self, key: str, default_value: bool | None = False) -> bool: +150 """Retrieve the setting specified by the given key, converting it to boolean. +151 :param key: The name of the setting to retrieve. +152 :param default_value: The value to return if the setting does not exist. +153 :return: The setting value if it exists, otherwise the default_value. +154 """ +155 return to_bool(self.get(key) or default_value) 156 -157 -158assert (settings := AskAiSettings().INSTANCE) is not None +157 def get_int(self, key: str, default_value: int | None = 0) -> int: +158 """Retrieve the setting specified by the given key, converting it to integer. +159 :param key: The name of the setting to retrieve. +160 :param default_value: The value to return if the setting does not exist. +161 :return: The setting value if it exists, otherwise the default_value. +162 """ +163 try: +164 return int(self.get(key) or default_value) +165 except ValueError: +166 return default_value +167 +168 def get_float(self, key: str, default_value: float | None = 0.0) -> float: +169 """Retrieve the setting specified by the given key, converting it to float. +170 :param key: The name of the setting to retrieve. +171 :param default_value: The value to return if the setting does not exist. +172 :return: The setting value if it exists, otherwise the default_value. +173 """ +174 try: +175 return float(self.get(key) or default_value) +176 except ValueError: +177 return default_value +178 +179 def get_list(self, key: str, default_value: list | None = None) -> list: +180 """Retrieve the setting specified by the given key, converting it to list. +181 :param key: The name of the setting to retrieve. +182 :param default_value: The value to return if the setting does not exist. +183 :return: The setting value if it exists, otherwise the default_value. +184 """ +185 try: +186 val: list | None = None +187 if str_val := (self.get(key) or ""): +188 val = re.split(r"[;,|]", str_val) +189 return val or default_value or list() +190 except ValueError: +191 return default_value or list() +192 +193 +194assert (settings := AskAiSettings().INSTANCE) is not None

    @@ -292,124 +325,160 @@

     42class AskAiSettings(metaclass=Singleton):
    - 43    """The Setman Settings."""
    + 43    """The AskAI 'SetMan' Settings."""
      44
      45    INSTANCE: "AskAiSettings"
      46
    - 47    RESOURCE_DIR = str(classpath.resource_path())
    - 48
    - 49    _ACTUAL_VERSION: str = "0.1.8"
    - 50
    - 51    def __init__(self) -> None:
    - 52        self._configs = SettingsConfig(self.RESOURCE_DIR, "application.properties")
    - 53        self._settings = Settings(self._configs)
    - 54        if not self._settings.count() or self.get("askai.settings.version.id") != self._ACTUAL_VERSION:
    - 55            self.defaults()
    - 56
    - 57    def __str__(self) -> str:
    - 58        return self.search()
    - 59
    - 60    def __repr__(self) -> str:
    - 61        return self.__str__()
    - 62
    - 63    def __getitem__(self, name: str) -> Optional[SettingsEntry]:
    - 64        return self._settings.get(name)
    - 65
    - 66    def __setitem__(self, name: str, item: Any) -> None:
    - 67        self._settings.put(name, name[: name.find(".")], item)
    - 68
    - 69    @property
    - 70    def settings(self) -> Settings:
    - 71        return self._settings
    - 72
    - 73    def search(self, filters: str | None = None) -> Optional[str]:
    - 74        """Search setting using the specified filters.
    - 75        :param filters: Filters used on the search.
    - 76        """
    - 77        data = [(s.name, s.value) for s in self._settings.search(filters)]
    - 78        if data:
    - 79            table = Table(title="AskAI - Settings")
    - 80            table.add_column("No.", style="white", no_wrap=True)
    - 81            table.add_column("Name", style="cyan", no_wrap=True)
    - 82            table.add_column("Value", style="green", no_wrap=True)
    - 83            for i, d in enumerate(data):
    - 84                table.add_row(str(i), d[0], d[1])
    - 85            with StringIO() as buf, redirect_stdout(buf):
    - 86                text_formatter.console.print(table)
    - 87                return buf.getvalue()
    - 88        return None
    - 89
    - 90    def defaults(self) -> None:
    - 91        """Create the default settings database if they doesn't exist."""
    - 92        # AskAI General
    - 93        self._settings.clear()
    - 94        self._settings.put("askai.settings.version.id", "askai", self._ACTUAL_VERSION)
    - 95        self._settings.put("askai.debug.enabled", "askai", False)
    - 96        self._settings.put("askai.speak.enabled", "askai", False)
    - 97        self._settings.put("askai.cache.enabled", "askai", False)
    - 98        self._settings.put("askai.cache.ttl.minutes", "askai", 25)
    - 99        self._settings.put("askai.text.to.speech.tempo", "askai", 1)
    -100        self._settings.put("askai.text.splitter.chunk.size", "askai", 1000)
    -101        self._settings.put("askai.text.splitter.chunk.overlap", "askai", 100)
    -102        self._settings.put("askai.preferred.language", "askai", '')
    -103        self._settings.put("askai.default.engine", "askai", "openai")
    -104        self._settings.put("askai.default.engine.model", "askai", "gpt-4o-mini")
    -105        # Router
    -106        self._settings.put("askai.max.short.memory.size", "askai", 10)
    -107        self._settings.put("askai.max.router.iteractions", "askai", 30)
    -108        self._settings.put("askai.max.agent.retries", "askai", 3)
    -109        self._settings.put("askai.max.agent.execution.time.seconds", "askai", 45)
    -110        # Recorder
    -111        self._settings.put("askai.recorder.devices", "askai", "")
    -112        self._settings.put("askai.recorder.silence.timeout.millis", "askai", 1200)
    -113        self._settings.put("askai.recorder.phrase.limit.millis", "askai", 10000)
    -114        self._settings.put("askai.recorder.noise.detection.duration.millis", "askai", 600)
    -115        self._settings.put("askai.recorder.input.device.auto.swap", "askai", True)
    -116        # Camera
    -117        self._settings.put("askai.camera.face-detect.alg", "askai", "haarcascade_frontalface_default.xml")
    -118        self._settings.put("askai.camera.scale.factor", "askai", 1.1)
    -119        self._settings.put("askai.camera.min.neighbors", "askai", 3)
    -120        self._settings.put("askai.camera.min-max.size", "askai", "60, 60")
    -121        self._settings.put("askai.camera.identity.max.distance", "askai", 0.70)
    -122        # OpenAI
    -123        self._settings.put("openai.speech.to.text.model", "openai", "whisper-1")
    -124        self._settings.put("openai.text.to.speech.model", "openai", "tts-1")
    -125        self._settings.put("openai.text.to.speech.voice", "openai", "onyx")
    -126        self._settings.put("openai.text.to.speech.audio.format", "openai", "mp3")
    -127        log.debug(f"Settings database created !")
    -128
    -129    def get(self, key: str, default_value: str | None = "") -> str:
    -130        val = self.__getitem__(key)
    -131        return str(val.value) if val else default_value
    -132
    -133    def put(self, key: str, value: Any) -> None:
    -134        self.__setitem__(key, value)
    -135
    -136    def get_bool(self, key: str, default_value: bool | None = False) -> bool:
    -137        return to_bool(self.get(key) or default_value)
    -138
    -139    def get_int(self, key: str, default_value: int | None = 0) -> int:
    -140        try:
    -141            return int(self.get(key) or default_value)
    -142        except ValueError:
    -143            return 0
    -144
    -145    def get_float(self, key: str, default_value: float | None = 0.0) -> float:
    -146        try:
    -147            return float(self.get(key) or default_value)
    -148        except ValueError:
    -149            return 0
    -150
    -151    def get_list(self, key: str, default_value: list | None = None) -> list:
    -152        str_val: str = self.get(key) or ""
    -153        val: list | None = None
    -154        if str_val:
    -155            val: list = re.split(r"[;,|]", str_val)
    -156        return val or default_value or []
    + 47    # Current settings version. Updating this value will trigger a database recreation using the defaults.
    + 48    __ACTUAL_VERSION: str = "0.2.1"
    + 49
    + 50    __RESOURCE_DIR = str(classpath.resource_path())
    + 51
    + 52    def __init__(self) -> None:
    + 53        self._configs = SettingsConfig(self.__RESOURCE_DIR, "application.properties")
    + 54        self._settings = Settings(self._configs)
    + 55        if not self._settings.count() or self.get("askai.settings.version.id") != self.__ACTUAL_VERSION:
    + 56            self.defaults()
    + 57
    + 58    def __str__(self) -> str:
    + 59        return self.search()
    + 60
    + 61    def __repr__(self) -> str:
    + 62        return self.__str__()
    + 63
    + 64    def __getitem__(self, name: str) -> Optional[SettingsEntry]:
    + 65        return self._settings.get(name)
    + 66
    + 67    def __setitem__(self, name: str, item: Any) -> None:
    + 68        self._settings.put(name, name[: name.find(".")], item)
    + 69
    + 70    @property
    + 71    def settings(self) -> Settings:
    + 72        return self._settings
    + 73
    + 74    def search(self, filters: str | None = None) -> Optional[str]:
    + 75        """Search settings using the specified filters.
    + 76        :param filters: Optional filters to apply to the search.
    + 77        :return: The search results as a string, or None if no results are found.
    + 78        """
    + 79        data = [(s.name, s.value) for s in self._settings.search(filters)]
    + 80        if data:
    + 81            table = Table(title="AskAI - Settings", box=box.SQUARE_DOUBLE_HEAD)
    + 82            table.add_column("No.", style="white", no_wrap=True)
    + 83            table.add_column("Name", style="cyan", no_wrap=True)
    + 84            table.add_column("Value", style="green", no_wrap=True)
    + 85            for i, d in enumerate(data):
    + 86                table.add_row(str(i), d[0], d[1])
    + 87            with StringIO() as buf, redirect_stdout(buf):
    + 88                console_out.print(table)
    + 89                return buf.getvalue()
    + 90        return None
    + 91
    + 92    def defaults(self) -> None:
    + 93        """Create the default settings database."""
    + 94        # AskAI General
    + 95        self._settings.clear("askai.*")
    + 96        self._settings.put("askai.settings.version.id", "askai", self.__ACTUAL_VERSION)
    + 97        self._settings.put("askai.debug.enabled", "askai", False)
    + 98        self._settings.put("askai.speak.enabled", "askai", False)
    + 99        self._settings.put("askai.cache.enabled", "askai", False)
    +100        self._settings.put("askai.cache.ttl.minutes", "askai", 25)
    +101        self._settings.put("askai.preferred.language", "askai", "")
    +102        self._settings.put("askai.default.engine", "askai", "openai")
    +103        self._settings.put("askai.default.engine.model", "askai", "gpt-4o-mini")
    +104        self._settings.put("askai.verbosity.level", "askai", 3)
    +105        self._settings.put("askai.text.to.speech.tempo", "askai", 1)
    +106        self._settings.put("askai.text.splitter.chunk.size", "askai", 1000)
    +107        self._settings.put("askai.text.splitter.chunk.overlap", "askai", 100)
    +108        self._settings.put("askai.rag.retrival.amount", "askai", 3)
    +109        self._settings.put("askai.rag.enabled", "askai", True)
    +110        # Router
    +111        self._settings.put("askai.max.short.memory.size", "askai", 15)
    +112        self._settings.put("askai.max.router.iteractions", "askai", 30)
    +113        self._settings.put("askai.max.agent.retries", "askai", 3)
    +114        self._settings.put("askai.max.agent.execution.time.seconds", "askai", 45)
    +115        # Recorder
    +116        self._settings.put("askai.recorder.devices", "askai", "")
    +117        self._settings.put("askai.recorder.silence.timeout.millis", "askai", 1200)
    +118        self._settings.put("askai.recorder.phrase.limit.millis", "askai", 10000)
    +119        self._settings.put("askai.recorder.noise.detection.duration.millis", "askai", 600)
    +120        self._settings.put("askai.recorder.input.device.auto.swap", "askai", True)
    +121        # Camera
    +122        self._settings.put("askai.camera.face-detect.alg", "askai", "haarcascade_frontalface_default.xml")
    +123        self._settings.put("askai.camera.scale.factor", "askai", 1.1)
    +124        self._settings.put("askai.camera.min.neighbors", "askai", 3)
    +125        self._settings.put("askai.camera.min-max.size", "askai", "100, 100")
    +126        self._settings.put("askai.camera.identity.max.distance", "askai", 0.70)
    +127        # OpenAI
    +128        self._settings.put("askai.openai.speech.to.text.model", "askai", "whisper-1")
    +129        self._settings.put("askai.openai.text.to.speech.model", "askai", "tts-1")
    +130        self._settings.put("askai.openai.text.to.speech.voice", "askai", "onyx")
    +131        self._settings.put("askai.openai.text.to.speech.audio.format", "askai", "mp3")
    +132        log.debug(f"Settings database created !")
    +133
    +134    def get(self, key: str, default_value: str | None = "") -> str:
    +135        """Retrieve the setting specified by the given key.
    +136        :param key: The name of the setting to retrieve.
    +137        :param default_value: The value to return if the setting does not exist.
    +138        :return: The setting value if it exists, otherwise the default_value.
    +139        """
    +140        val = self.__getitem__(key)
    +141        return str(val.value) if val else default_value
    +142
    +143    def put(self, key: str, value: Any) -> None:
    +144        """Set the setting specified by the given key.
    +145        :param key: The name of the setting to update.
    +146        :param value: The value to associate with the key.
    +147        """
    +148        self.__setitem__(key, value)
    +149
    +150    def get_bool(self, key: str, default_value: bool | None = False) -> bool:
    +151        """Retrieve the setting specified by the given key, converting it to boolean.
    +152        :param key: The name of the setting to retrieve.
    +153        :param default_value: The value to return if the setting does not exist.
    +154        :return: The setting value if it exists, otherwise the default_value.
    +155        """
    +156        return to_bool(self.get(key) or default_value)
    +157
    +158    def get_int(self, key: str, default_value: int | None = 0) -> int:
    +159        """Retrieve the setting specified by the given key, converting it to integer.
    +160        :param key: The name of the setting to retrieve.
    +161        :param default_value: The value to return if the setting does not exist.
    +162        :return: The setting value if it exists, otherwise the default_value.
    +163        """
    +164        try:
    +165            return int(self.get(key) or default_value)
    +166        except ValueError:
    +167            return default_value
    +168
    +169    def get_float(self, key: str, default_value: float | None = 0.0) -> float:
    +170        """Retrieve the setting specified by the given key, converting it to float.
    +171        :param key: The name of the setting to retrieve.
    +172        :param default_value: The value to return if the setting does not exist.
    +173        :return: The setting value if it exists, otherwise the default_value.
    +174        """
    +175        try:
    +176            return float(self.get(key) or default_value)
    +177        except ValueError:
    +178            return default_value
    +179
    +180    def get_list(self, key: str, default_value: list | None = None) -> list:
    +181        """Retrieve the setting specified by the given key, converting it to list.
    +182        :param key: The name of the setting to retrieve.
    +183        :param default_value: The value to return if the setting does not exist.
    +184        :return: The setting value if it exists, otherwise the default_value.
    +185        """
    +186        try:
    +187            val: list | None = None
    +188            if str_val := (self.get(key) or ""):
    +189                val = re.split(r"[;,|]", str_val)
    +190            return val or default_value or list()
    +191        except ValueError:
    +192            return default_value or list()
     
    -

    The Setman Settings.

    +

    The AskAI 'SetMan' Settings.

    @@ -454,18 +523,6 @@

    -

    -
    -
    - RESOURCE_DIR = -'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources' - - -
    - - - -
    @@ -476,9 +533,9 @@

    -
    69    @property
    -70    def settings(self) -> Settings:
    -71        return self._settings
    +            
    70    @property
    +71    def settings(self) -> Settings:
    +72        return self._settings
     
    @@ -496,32 +553,39 @@

    -
    73    def search(self, filters: str | None = None) -> Optional[str]:
    -74        """Search setting using the specified filters.
    -75        :param filters: Filters used on the search.
    -76        """
    -77        data = [(s.name, s.value) for s in self._settings.search(filters)]
    -78        if data:
    -79            table = Table(title="AskAI - Settings")
    -80            table.add_column("No.", style="white", no_wrap=True)
    -81            table.add_column("Name", style="cyan", no_wrap=True)
    -82            table.add_column("Value", style="green", no_wrap=True)
    -83            for i, d in enumerate(data):
    -84                table.add_row(str(i), d[0], d[1])
    -85            with StringIO() as buf, redirect_stdout(buf):
    -86                text_formatter.console.print(table)
    -87                return buf.getvalue()
    -88        return None
    +            
    74    def search(self, filters: str | None = None) -> Optional[str]:
    +75        """Search settings using the specified filters.
    +76        :param filters: Optional filters to apply to the search.
    +77        :return: The search results as a string, or None if no results are found.
    +78        """
    +79        data = [(s.name, s.value) for s in self._settings.search(filters)]
    +80        if data:
    +81            table = Table(title="AskAI - Settings", box=box.SQUARE_DOUBLE_HEAD)
    +82            table.add_column("No.", style="white", no_wrap=True)
    +83            table.add_column("Name", style="cyan", no_wrap=True)
    +84            table.add_column("Value", style="green", no_wrap=True)
    +85            for i, d in enumerate(data):
    +86                table.add_row(str(i), d[0], d[1])
    +87            with StringIO() as buf, redirect_stdout(buf):
    +88                console_out.print(table)
    +89                return buf.getvalue()
    +90        return None
     
    -

    Search setting using the specified filters.

    +

    Search settings using the specified filters.

    Parameters
      -
    • filters: Filters used on the search.
    • +
    • filters: Optional filters to apply to the search.
    + +
    Returns
    + +
    +

    The search results as a string, or None if no results are found.

    +
    @@ -537,48 +601,51 @@
    Parameters
    -
     90    def defaults(self) -> None:
    - 91        """Create the default settings database if they doesn't exist."""
    - 92        # AskAI General
    - 93        self._settings.clear()
    - 94        self._settings.put("askai.settings.version.id", "askai", self._ACTUAL_VERSION)
    - 95        self._settings.put("askai.debug.enabled", "askai", False)
    - 96        self._settings.put("askai.speak.enabled", "askai", False)
    - 97        self._settings.put("askai.cache.enabled", "askai", False)
    - 98        self._settings.put("askai.cache.ttl.minutes", "askai", 25)
    - 99        self._settings.put("askai.text.to.speech.tempo", "askai", 1)
    -100        self._settings.put("askai.text.splitter.chunk.size", "askai", 1000)
    -101        self._settings.put("askai.text.splitter.chunk.overlap", "askai", 100)
    -102        self._settings.put("askai.preferred.language", "askai", '')
    -103        self._settings.put("askai.default.engine", "askai", "openai")
    -104        self._settings.put("askai.default.engine.model", "askai", "gpt-4o-mini")
    -105        # Router
    -106        self._settings.put("askai.max.short.memory.size", "askai", 10)
    -107        self._settings.put("askai.max.router.iteractions", "askai", 30)
    -108        self._settings.put("askai.max.agent.retries", "askai", 3)
    -109        self._settings.put("askai.max.agent.execution.time.seconds", "askai", 45)
    -110        # Recorder
    -111        self._settings.put("askai.recorder.devices", "askai", "")
    -112        self._settings.put("askai.recorder.silence.timeout.millis", "askai", 1200)
    -113        self._settings.put("askai.recorder.phrase.limit.millis", "askai", 10000)
    -114        self._settings.put("askai.recorder.noise.detection.duration.millis", "askai", 600)
    -115        self._settings.put("askai.recorder.input.device.auto.swap", "askai", True)
    -116        # Camera
    -117        self._settings.put("askai.camera.face-detect.alg", "askai", "haarcascade_frontalface_default.xml")
    -118        self._settings.put("askai.camera.scale.factor", "askai", 1.1)
    -119        self._settings.put("askai.camera.min.neighbors", "askai", 3)
    -120        self._settings.put("askai.camera.min-max.size", "askai", "60, 60")
    -121        self._settings.put("askai.camera.identity.max.distance", "askai", 0.70)
    -122        # OpenAI
    -123        self._settings.put("openai.speech.to.text.model", "openai", "whisper-1")
    -124        self._settings.put("openai.text.to.speech.model", "openai", "tts-1")
    -125        self._settings.put("openai.text.to.speech.voice", "openai", "onyx")
    -126        self._settings.put("openai.text.to.speech.audio.format", "openai", "mp3")
    -127        log.debug(f"Settings database created !")
    +            
     92    def defaults(self) -> None:
    + 93        """Create the default settings database."""
    + 94        # AskAI General
    + 95        self._settings.clear("askai.*")
    + 96        self._settings.put("askai.settings.version.id", "askai", self.__ACTUAL_VERSION)
    + 97        self._settings.put("askai.debug.enabled", "askai", False)
    + 98        self._settings.put("askai.speak.enabled", "askai", False)
    + 99        self._settings.put("askai.cache.enabled", "askai", False)
    +100        self._settings.put("askai.cache.ttl.minutes", "askai", 25)
    +101        self._settings.put("askai.preferred.language", "askai", "")
    +102        self._settings.put("askai.default.engine", "askai", "openai")
    +103        self._settings.put("askai.default.engine.model", "askai", "gpt-4o-mini")
    +104        self._settings.put("askai.verbosity.level", "askai", 3)
    +105        self._settings.put("askai.text.to.speech.tempo", "askai", 1)
    +106        self._settings.put("askai.text.splitter.chunk.size", "askai", 1000)
    +107        self._settings.put("askai.text.splitter.chunk.overlap", "askai", 100)
    +108        self._settings.put("askai.rag.retrival.amount", "askai", 3)
    +109        self._settings.put("askai.rag.enabled", "askai", True)
    +110        # Router
    +111        self._settings.put("askai.max.short.memory.size", "askai", 15)
    +112        self._settings.put("askai.max.router.iteractions", "askai", 30)
    +113        self._settings.put("askai.max.agent.retries", "askai", 3)
    +114        self._settings.put("askai.max.agent.execution.time.seconds", "askai", 45)
    +115        # Recorder
    +116        self._settings.put("askai.recorder.devices", "askai", "")
    +117        self._settings.put("askai.recorder.silence.timeout.millis", "askai", 1200)
    +118        self._settings.put("askai.recorder.phrase.limit.millis", "askai", 10000)
    +119        self._settings.put("askai.recorder.noise.detection.duration.millis", "askai", 600)
    +120        self._settings.put("askai.recorder.input.device.auto.swap", "askai", True)
    +121        # Camera
    +122        self._settings.put("askai.camera.face-detect.alg", "askai", "haarcascade_frontalface_default.xml")
    +123        self._settings.put("askai.camera.scale.factor", "askai", 1.1)
    +124        self._settings.put("askai.camera.min.neighbors", "askai", 3)
    +125        self._settings.put("askai.camera.min-max.size", "askai", "100, 100")
    +126        self._settings.put("askai.camera.identity.max.distance", "askai", 0.70)
    +127        # OpenAI
    +128        self._settings.put("askai.openai.speech.to.text.model", "askai", "whisper-1")
    +129        self._settings.put("askai.openai.text.to.speech.model", "askai", "tts-1")
    +130        self._settings.put("askai.openai.text.to.speech.voice", "askai", "onyx")
    +131        self._settings.put("askai.openai.text.to.speech.audio.format", "askai", "mp3")
    +132        log.debug(f"Settings database created !")
     
    -

    Create the default settings database if they doesn't exist.

    +

    Create the default settings database.

    @@ -594,13 +661,33 @@
    Parameters
    -
    129    def get(self, key: str, default_value: str | None = "") -> str:
    -130        val = self.__getitem__(key)
    -131        return str(val.value) if val else default_value
    +            
    134    def get(self, key: str, default_value: str | None = "") -> str:
    +135        """Retrieve the setting specified by the given key.
    +136        :param key: The name of the setting to retrieve.
    +137        :param default_value: The value to return if the setting does not exist.
    +138        :return: The setting value if it exists, otherwise the default_value.
    +139        """
    +140        val = self.__getitem__(key)
    +141        return str(val.value) if val else default_value
     
    - +

    Retrieve the setting specified by the given key.

    + +
    Parameters
    + +
      +
    • key: The name of the setting to retrieve.
    • +
    • default_value: The value to return if the setting does not exist.
    • +
    + +
    Returns
    + +
    +

    The setting value if it exists, otherwise the default_value.

    +
    +
    +
    @@ -614,12 +701,25 @@
    Parameters
    -
    133    def put(self, key: str, value: Any) -> None:
    -134        self.__setitem__(key, value)
    +            
    143    def put(self, key: str, value: Any) -> None:
    +144        """Set the setting specified by the given key.
    +145        :param key: The name of the setting to update.
    +146        :param value: The value to associate with the key.
    +147        """
    +148        self.__setitem__(key, value)
     
    - +

    Set the setting specified by the given key.

    + +
    Parameters
    + +
      +
    • key: The name of the setting to update.
    • +
    • value: The value to associate with the key.
    • +
    +
    +
    @@ -633,12 +733,32 @@
    Parameters
    -
    136    def get_bool(self, key: str, default_value: bool | None = False) -> bool:
    -137        return to_bool(self.get(key) or default_value)
    +            
    150    def get_bool(self, key: str, default_value: bool | None = False) -> bool:
    +151        """Retrieve the setting specified by the given key, converting it to boolean.
    +152        :param key: The name of the setting to retrieve.
    +153        :param default_value: The value to return if the setting does not exist.
    +154        :return: The setting value if it exists, otherwise the default_value.
    +155        """
    +156        return to_bool(self.get(key) or default_value)
     
    - +

    Retrieve the setting specified by the given key, converting it to boolean.

    + +
    Parameters
    + +
      +
    • key: The name of the setting to retrieve.
    • +
    • default_value: The value to return if the setting does not exist.
    • +
    + +
    Returns
    + +
    +

    The setting value if it exists, otherwise the default_value.

    +
    +
    +
    @@ -652,15 +772,35 @@
    Parameters
    -
    139    def get_int(self, key: str, default_value: int | None = 0) -> int:
    -140        try:
    -141            return int(self.get(key) or default_value)
    -142        except ValueError:
    -143            return 0
    +            
    158    def get_int(self, key: str, default_value: int | None = 0) -> int:
    +159        """Retrieve the setting specified by the given key, converting it to integer.
    +160        :param key: The name of the setting to retrieve.
    +161        :param default_value: The value to return if the setting does not exist.
    +162        :return: The setting value if it exists, otherwise the default_value.
    +163        """
    +164        try:
    +165            return int(self.get(key) or default_value)
    +166        except ValueError:
    +167            return default_value
     
    - +

    Retrieve the setting specified by the given key, converting it to integer.

    + +
    Parameters
    + +
      +
    • key: The name of the setting to retrieve.
    • +
    • default_value: The value to return if the setting does not exist.
    • +
    + +
    Returns
    + +
    +

    The setting value if it exists, otherwise the default_value.

    +
    +
    +
    @@ -674,15 +814,35 @@
    Parameters
    -
    145    def get_float(self, key: str, default_value: float | None = 0.0) -> float:
    -146        try:
    -147            return float(self.get(key) or default_value)
    -148        except ValueError:
    -149            return 0
    +            
    169    def get_float(self, key: str, default_value: float | None = 0.0) -> float:
    +170        """Retrieve the setting specified by the given key, converting it to float.
    +171        :param key: The name of the setting to retrieve.
    +172        :param default_value: The value to return if the setting does not exist.
    +173        :return: The setting value if it exists, otherwise the default_value.
    +174        """
    +175        try:
    +176            return float(self.get(key) or default_value)
    +177        except ValueError:
    +178            return default_value
     
    - +

    Retrieve the setting specified by the given key, converting it to float.

    + +
    Parameters
    + +
      +
    • key: The name of the setting to retrieve.
    • +
    • default_value: The value to return if the setting does not exist.
    • +
    + +
    Returns
    + +
    +

    The setting value if it exists, otherwise the default_value.

    +
    +
    +
    @@ -696,16 +856,38 @@
    Parameters
    -
    151    def get_list(self, key: str, default_value: list | None = None) -> list:
    -152        str_val: str = self.get(key) or ""
    -153        val: list | None = None
    -154        if str_val:
    -155            val: list = re.split(r"[;,|]", str_val)
    -156        return val or default_value or []
    +            
    180    def get_list(self, key: str, default_value: list | None = None) -> list:
    +181        """Retrieve the setting specified by the given key, converting it to list.
    +182        :param key: The name of the setting to retrieve.
    +183        :param default_value: The value to return if the setting does not exist.
    +184        :return: The setting value if it exists, otherwise the default_value.
    +185        """
    +186        try:
    +187            val: list | None = None
    +188            if str_val := (self.get(key) or ""):
    +189                val = re.split(r"[;,|]", str_val)
    +190            return val or default_value or list()
    +191        except ValueError:
    +192            return default_value or list()
     
    - +

    Retrieve the setting specified by the given key, converting it to list.

    + +
    Parameters
    + +
      +
    • key: The name of the setting to retrieve.
    • +
    • default_value: The value to return if the setting does not exist.
    • +
    + +
    Returns
    + +
    +

    The setting value if it exists, otherwise the default_value.

    +
    +
    +

    diff --git a/docs/api/askai/main/askai/core/commander.html b/docs/api/askai/main/askai/core/commander.html index 52ea70b6..c1865804 100644 --- a/docs/api/askai/main/askai/core/commander.html +++ b/docs/api/askai/main/askai/core/commander.html @@ -3,7 +3,7 @@ - + main.askai.core.commander API documentation @@ -56,7 +56,7 @@

     1# _*_ coding: utf-8 _*_
      2#
    - 3# hspylib-askai v1.0.11
    + 3# hspylib-askai v1.0.13
      4#
      5# Package: main.askai.core.commander
      6"""Package initialization."""
    @@ -65,7 +65,7 @@ 

    9 'commander', 10 'commands' 11] -12__version__ = '1.0.11' +12__version__ = '1.0.13'

    diff --git a/docs/api/askai/main/askai/core/commander/commander.html b/docs/api/askai/main/askai/core/commander/commander.html index 4481f3d7..47a96dfe 100644 --- a/docs/api/askai/main/askai/core/commander/commander.html +++ b/docs/api/askai/main/askai/core/commander/commander.html @@ -3,7 +3,7 @@ - + main.askai.core.commander.commander API documentation @@ -33,6 +33,9 @@

    API Documentation

  • COMMANDER_HELP_TPL
  • +
  • + COMMANDER_HELP_CMD_TPL +
  • RE_ASKAI_CMD
  • @@ -42,6 +45,9 @@

    API Documentation

  • commander_help
  • +
  • + is_command +
  • ask_commander
  • @@ -141,392 +147,430 @@

    12 13 Copyright (c) 2024, HomeSetup 14""" - 15import os - 16from functools import partial - 17from os.path import dirname - 18from pathlib import Path - 19from string import Template - 20 - 21import click - 22from click import Command, Group - 23from clitt.core.term.cursor import cursor - 24from hspylib.core.enums.charset import Charset - 25from hspylib.core.tools.commons import sysout, to_bool - 26from hspylib.modules.eventbus.event import Event - 27 - 28from askai.core.askai_configs import configs - 29from askai.core.askai_events import events, AskAiEvents, ASKAI_BUS_NAME, REPLY_EVENT, REPLY_ERROR_EVENT - 30from askai.core.commander.commands.cache_cmd import CacheCmd - 31from askai.core.commander.commands.camera_cmd import CameraCmd - 32from askai.core.commander.commands.general_cmd import GeneralCmd - 33from askai.core.commander.commands.history_cmd import HistoryCmd - 34from askai.core.commander.commands.settings_cmd import SettingsCmd - 35from askai.core.commander.commands.tts_stt_cmd import TtsSttCmd - 36from askai.core.support.shared_instances import shared - 37from askai.core.support.text_formatter import text_formatter - 38from askai.core.support.utilities import display_text - 39from askai.language.language import Language, AnyLocale - 40 - 41COMMANDER_HELP_TPL = Template( - 42 """ - 43# AskAI Commander - HELP - 44 - 45> Commands: + 15from askai.core.askai_configs import configs + 16from askai.core.askai_events import ASKAI_BUS_NAME, AskAiEvents, REPLY_EVENT + 17from askai.core.commander.commands.cache_cmd import CacheCmd + 18from askai.core.commander.commands.camera_cmd import CameraCmd + 19from askai.core.commander.commands.general_cmd import GeneralCmd + 20from askai.core.commander.commands.history_cmd import HistoryCmd + 21from askai.core.commander.commands.settings_cmd import SettingsCmd + 22from askai.core.commander.commands.tts_stt_cmd import TtsSttCmd + 23from askai.core.support.shared_instances import shared + 24from askai.core.support.text_formatter import text_formatter + 25from askai.core.support.utilities import display_text + 26from askai.language.language import AnyLocale, Language + 27from click import Command, Group + 28from clitt.core.term.cursor import cursor + 29from functools import lru_cache + 30from hspylib.core.enums.charset import Charset + 31from hspylib.core.tools.commons import sysout, to_bool + 32from hspylib.modules.eventbus.event import Event + 33from os.path import dirname + 34from pathlib import Path + 35from string import Template + 36from textwrap import dedent + 37 + 38import click + 39import os + 40import re + 41 + 42COMMANDER_HELP_TPL = Template( + 43 dedent( + 44 """ + 45 # AskAI Commander - HELP 46 - 47${commands} - 48--- - 49 - 50> CLI-Input Key-Bindings: + 47 > Commands: + 48 + 49 ${commands} + 50 --- 51 - 52| **Key** | **Action** | - 53| -------- | ----------------------------- | - 54| *Ctrl+L* | **Push-To-Talk.** | - 55| *Ctrl+R* | **Reset the input field.** | - 56| *Ctrl+F* | **Forget the input history.** | - 57""" - 58) + 52 > CLI-Input Key-Bindings: + 53 + 54 | **Key** | **Action** | + 55 | -------- | ----------------------------- | + 56 | *Ctrl+L* | **Push-To-Talk.** | + 57 | *Ctrl+R* | **Reset the input field.** | + 58 | *Ctrl+F* | **Forget the input history.** | 59 - 60RE_ASKAI_CMD: str = r"^(?<!\\)/(\w+)( (.*))*$" - 61 - 62__module__ = locals() - 63 + 60 >  To get help about a specific command type: '/help \<command\>' + 61 """ + 62 ) + 63) 64 - 65def commands() -> list[str]: - 66 """Return the list of all commander commands.""" - 67 all_commands: set[str] = set() - 68 for name, obj in __module__.items(): - 69 if obj and isinstance(obj, Command) and not isinstance(obj, Group): - 70 all_commands.add(f"/{name}") - 71 return sorted(all_commands, reverse=True) - 72 + 65COMMANDER_HELP_CMD_TPL = Template( + 66 dedent( + 67 """ + 68 # AskAI Commander - HELP + 69 ``` + 70 %CYAN%Command: %ORANGE%${command}%NC% + 71 + 72 ${docstr} 73 - 74def commander_help() -> str: - 75 """Return the commander help string.""" - 76 helpstr: str = "" - 77 for cmd, obj in __module__.items(): - 78 if obj and isinstance(obj, Command) and not isinstance(obj, Group): - 79 cmd_doc: str = f"{obj.__doc__.split(os.linesep, 1)[0]}**" - 80 helpstr += f"| /{'*' + cmd + '*':<8} | **{cmd_doc:<43} |\n" - 81 h_str: str = f"| {'**Command**':<9} | {'**Description**':<45} |\n" - 82 h_str += f"| {'-' * 9} | {'-' * 45} |\n" - 83 return COMMANDER_HELP_TPL.substitute(commands=f"{h_str}{helpstr}") + 74 %CYAN%Usage:\t%WHITE%/${usage} + 75 ``` + 76 """ + 77 ) + 78) + 79 + 80RE_ASKAI_CMD: str = r"^(?<!\\)/(\w+)( (.*))*$" + 81 + 82__module__ = locals() + 83 84 - 85 - 86def _init_context(context_size: int = 1000, engine_name: str = "openai", model_name: str = "gpt-4o-mini") -> None: - 87 """Initialize AskAI context and startup components. - 88 :param context_size: The max size of e context window. - 89 :param engine_name: The name of the engine to initialize. - 90 :param model_name: The engine's model name to initialize. - 91 """ - 92 if not (shared.engine and shared.context): - 93 shared.create_engine(engine_name=engine_name, model_name=model_name) - 94 shared.create_context(context_size) - 95 events.reply.subscribe(cb_event_handler=lambda ev: display_text(ev.args.message)) + 85@lru_cache + 86def commands() -> list[str]: + 87 """Return the list of all available commander commands. + 88 :return: A list of strings representing the available commands. + 89 """ + 90 all_commands: set[str] = set() + 91 for name, obj in __module__.items(): + 92 if obj and isinstance(obj, Command) and not isinstance(obj, Group): + 93 all_commands.add(f"/{name}") + 94 return sorted(all_commands, reverse=True) + 95 96 - 97 - 98@click.group() - 99@click.pass_context -100def ask_commander(_) -> None: -101 """AskAI commands group.""" -102 -103 def _reply_event(ev: Event, error: bool = False) -> None: -104 if message := ev.args.message: -105 if error: -106 text_formatter.cmd_print(f"%RED%{message}!%NC%") -107 else: -108 if ev.args.erase_last: -109 cursor.erase_line() -110 display_text(message) -111 -112 askai_bus = AskAiEvents.bus(ASKAI_BUS_NAME) -113 askai_bus.subscribe(REPLY_EVENT, _reply_event) -114 askai_bus.subscribe(REPLY_ERROR_EVENT, partial(_reply_event, error=True)) + 97@lru_cache + 98def commander_help(command: str | None = None) -> str: + 99 """Return the help string for the specified commander command. +100 :param command: The command for which to retrieve help. +101 :return: A string containing the help information for the specified command. +102 """ +103 if (command in __module__) and (cmd := __module__[command]): +104 return _format_help(cmd) +105 else: +106 help_str: str = "" +107 for cmd, obj in __module__.items(): +108 if obj and isinstance(obj, Command) and not isinstance(obj, Group): +109 cmd_doc: str = f"{obj.__doc__.split(os.linesep, 1)[0]}**" +110 help_str += f"| /{'*' + cmd + '*':<8} | **{cmd_doc:<43} |\n" +111 h_str: str = f"| {'**Command**':<9} | {'**Description**':<45} |\n" +112 h_str += f"| {'-' * 9} | {'-' * 45} |\n" +113 return COMMANDER_HELP_TPL.substitute(commands=f"{h_str}{help_str}") +114 115 -116 -117@ask_commander.command() -118def help() -> None: -119 """Show this help message and exit.""" -120 text_formatter.cmd_print(commander_help()) -121 +116def is_command(string: str) -> bool: +117 """Check if the given string is a command. +118 :param string: The string to check. +119 :return: True if the string is a command, False otherwise. +120 """ +121 return string.startswith("/") and string in commands() 122 -123@ask_commander.command() -124def debug() -> None: -125 """Toggle debug mode ON/OFF.""" -126 configs.is_debug = not configs.is_debug -127 text_formatter.cmd_print(f"`Debugging` is {'%GREEN%ON' if configs.is_debug else '%RED%OFF'}%NC%") -128 -129 -130@ask_commander.command() -131def speak() -> None: -132 """Toggle speak mode ON/OFF.""" -133 configs.is_speak = not configs.is_speak -134 text_formatter.cmd_print(f"`Speech-To-Text` is {'%GREEN%ON' if configs.is_speak else '%RED%OFF'}%NC%") -135 -136 -137@ask_commander.command() -138@click.argument("operation", default="list") -139@click.argument("name", default="ALL") -140def context(operation: str, name: str | None = None) -> None: -141 """List/forget the current context window. -142 :param operation The operation to manage contexts. -143 :param name The context name. -144 """ -145 match operation: -146 case "forget": -147 HistoryCmd.context_forget(name) -148 case "list": -149 HistoryCmd.context_list() -150 -151 -152@ask_commander.command() -153@click.argument("operation", default="list") -154def history(operation: str) -> None: -155 """List/forget the input history. -156 :param operation The operation to manage history. -157 """ -158 match operation: -159 case "forget": -160 HistoryCmd.history_forget() -161 case "list": -162 HistoryCmd.history_list() -163 -164 -165@ask_commander.command() -166@click.argument("name", default="LAST_REPLY") -167def copy(name: str) -> None: -168 """Copy a context entry to the clipboard -169 :param name: The context name. -170 """ -171 HistoryCmd.context_copy(name) -172 -173 -174@ask_commander.command() -175@click.argument("operation", default="list") -176@click.argument("name", default="") -177def devices(operation: str, name: str | None = None) -> None: -178 """List/set the audio input devices. -179 :param operation The operation to manage devices. -180 :param name The device name to set. -181 """ -182 match operation: -183 case "list": -184 TtsSttCmd.device_list() -185 case "set": -186 TtsSttCmd.device_set(name) -187 case _: -188 err = str(click.BadParameter(f"Invalid settings operation: '{operation}'")) -189 text_formatter.cmd_print(f"Error: {err}") -190 -191 -192@ask_commander.command() -193@click.argument("operation", default="list") -194@click.argument("name", default="") -195@click.argument("value", default="") -196def settings(operation: str, name: str | None = None, value: str | None = None) -> None: -197 """List/get/set/reset AskAI settings. -198 :param operation The operation to manage settings. -199 :param name The settings key to operate. -200 :param value The settings value to be set. -201 """ -202 match operation: -203 case "list": -204 SettingsCmd.list(name) -205 case "get": -206 SettingsCmd.get(name) -207 case "set": -208 SettingsCmd.set(name, value) -209 case "reset": -210 SettingsCmd.reset() -211 case _: -212 err = str(click.BadParameter(f"Invalid settings operation: '{operation}'")) -213 text_formatter.cmd_print(f"Error: {err}") -214 -215 -216@ask_commander.command() -217@click.argument("operation", default="list") -218@click.argument("args", nargs=-1) -219def cache(operation: str, args: tuple[str, ...]) -> None: -220 """List/get/clear/files AskAI TTL cache and files. -221 :param operation The operation to manage cache. -222 :param args The operation arguments operate. -223 """ -224 match operation: -225 case "list": -226 CacheCmd.list() -227 case "get": -228 if not args: -229 err: str = str(click.MissingParameter(f"Argument 'name' is required. Usage /cache get \\<name\\>")) -230 text_formatter.cmd_print(f"Error: {err}") -231 else: -232 set(map(sysout, map(CacheCmd.get, args))) -233 case "clear": -234 if args and (invalid := next((a for a in args if not isinstance(a, str | int)), None)): -235 err: str = str(click.BadParameter(f"Invalid argument: '{invalid}'")) -236 text_formatter.cmd_print(f"Error: {err}") -237 elif args: -238 set(map(CacheCmd.clear, args)) -239 else: -240 CacheCmd.clear() -241 case "files": -242 CacheCmd.files("cleanup" in args, *args) -243 case "enable": -244 if not args: -245 err: str = str(click.MissingParameter(f"Arguments missing. Usage /cache enable \\<0|1\\>")) -246 text_formatter.cmd_print(f"Error: {err}") -247 else: -248 configs.is_cache = to_bool(args[0]) -249 text_formatter.cmd_print(f"Caching has been *{'en' if configs.is_cache else 'dis'}abled* !") -250 case "ttl": -251 if not args: -252 text_formatter.cmd_print(f"Cache TTL is set to *{configs.ttl} minutes* !") -253 elif not args[0].isdigit(): -254 err: str = str(click.MissingParameter(f"Argument 'minutes' is invalid. Usage /cache ttl \\<minutes\\>")) -255 text_formatter.cmd_print(f"Error: {err}") -256 else: -257 configs.ttl = int(args[0]) -258 text_formatter.cmd_print(f"Cache TTL was set to *{args[0]} minutes* !") -259 case _: -260 err: str = str(click.BadParameter(f"Invalid cache operation: '{operation}'")) -261 text_formatter.cmd_print(f"Error: {err}") +123 +124def _format_help(command: Command) -> str: +125 """Return a formatted help string for the given command. +126 :param command: The command for which the help string will be formatted. +127 :return: A formatted string containing the help information for the specified command. +128 """ +129 docstr: str = "" +130 re_flags = re.MULTILINE | re.IGNORECASE +131 splits: list[str] = re.split(os.linesep, command.__doc__, flags=re_flags) +132 for i, arg in enumerate(splits): +133 if mat := re.match(r":\w+\s+(\w+):\s+(.+)", arg.strip()): +134 docstr += f"\n\t\t- %BLUE%{mat.group(1).casefold():<15}%WHITE%\t: {mat.group(2).title()}" +135 elif arg.strip(): +136 docstr += f"{arg}\n\n%CYAN%Arguments:%NC%\n" +137 usage_str: str = f"{command.name} {' '.join(['<' + p.name + '>' for p in command.params])}" +138 +139 return COMMANDER_HELP_CMD_TPL.substitute(command=command.name.title(), docstr=docstr, usage=usage_str) +140 +141 +142def _init_context(context_size: int = 1000, engine_name: str = "openai", model_name: str = "gpt-4o-mini") -> None: +143 """Initialize the AskAI context and startup components. +144 :param context_size: The maximum size of the context window (default is 1000). +145 :param engine_name: The name of the engine to initialize (default is "openai"). +146 :param model_name: The model name of the engine to initialize (default is "gpt-4o-mini"). +147 """ +148 +149 def _reply_event(ev: Event, error: bool = False) -> None: +150 """Callback for handling the reply event. +151 :param ev: The event object representing the reply event. +152 :param error: Indicates whether the reply contains an error (default is False). +153 """ +154 if message := ev.args.message: +155 if error: +156 text_formatter.cmd_print(f"%RED%{message}!%NC%") +157 else: +158 if ev.args.erase_last: +159 cursor.erase_line() +160 display_text(message) +161 +162 if shared.engine is None and shared.context is None: +163 shared.create_engine(engine_name=engine_name, model_name=model_name) +164 shared.create_context(context_size) +165 askai_bus = AskAiEvents.bus(ASKAI_BUS_NAME) +166 askai_bus.subscribe(REPLY_EVENT, _reply_event) +167 +168 +169@click.group() +170@click.pass_context +171def ask_commander(_) -> None: +172 """AskAI commands group. This function serves as the entry point for the AskAI command-line interface (CLI) +173 commands, grouping related commands together. +174 """ +175 _init_context() +176 +177 +178@ask_commander.command() +179@click.argument("command", default="") +180def help(command: str | None) -> None: +181 """Display the help message for the specified command and exit. If no command is provided, it displays the general +182 help message. +183 :param command: The command to retrieve help for (optional). +184 """ +185 display_text(commander_help(command.replace("/", ""))) +186 +187 +188@ask_commander.command() +189def debug() -> None: +190 """Toggle debug mode ON/OFF.""" +191 configs.is_debug = not configs.is_debug +192 text_formatter.cmd_print(f"`Debugging` is {'%GREEN%ON' if configs.is_debug else '%RED%OFF'}%NC%") +193 +194 +195@ask_commander.command() +196def speak() -> None: +197 """Toggle speak mode ON/OFF.""" +198 configs.is_speak = not configs.is_speak +199 text_formatter.cmd_print(f"`Speech-To-Text` is {'%GREEN%ON' if configs.is_speak else '%RED%OFF'}%NC%") +200 +201 +202@ask_commander.command() +203@click.argument("operation", default="list") +204@click.argument("name", default="ALL") +205def context(operation: str, name: str | None = None) -> None: +206 """Manage the current chat context window. +207 :param operation: The operation to perform on contexts. Options: [list | forget]. +208 :param name: The name of the context to target (default is "ALL"). +209 """ +210 match operation: +211 case "list": +212 HistoryCmd.context_list() +213 case "forget": +214 HistoryCmd.context_forget(name) +215 case _: +216 err = str(click.BadParameter(f"Invalid settings operation: '{operation}'")) +217 text_formatter.cmd_print(f"Error: {err}") +218 +219 +220@ask_commander.command() +221@click.argument("operation", default="list") +222def history(operation: str) -> None: +223 """Manages the current input history. +224 :param operation: The operation to perform on contexts. Options: [list|forget]. +225 """ +226 match operation: +227 case "list": +228 HistoryCmd.history_list() +229 case "forget": +230 HistoryCmd.history_forget() +231 case _: +232 err = str(click.BadParameter(f"Invalid settings operation: '{operation}'")) +233 text_formatter.cmd_print(f"Error: {err}") +234 +235 +236@ask_commander.command() +237@click.argument("name", default="LAST_REPLY") +238def copy(name: str) -> None: +239 """Copy the specified context entry to the clipboard. +240 :param name: The name of the context entry to copy. +241 """ +242 HistoryCmd.context_copy(name) +243 +244 +245@ask_commander.command() +246@click.argument("operation", default="list") +247@click.argument("name", default="") +248def devices(operation: str, name: str | None = None) -> None: +249 """Manages the audio input devices. +250 :param operation: Specifies the device operation. Options: [list|set]. +251 :param name: The target device name for setting. +252 """ +253 match operation: +254 case "list": +255 TtsSttCmd.device_list() +256 case "set": +257 TtsSttCmd.device_set(name) +258 case _: +259 err = str(click.BadParameter(f"Invalid settings operation: '{operation}'")) +260 text_formatter.cmd_print(f"Error: {err}") +261 262 -263 -264@ask_commander.command() -265@click.argument("speed", type=click.INT, default=1) -266def tempo(speed: int | None = None) -> None: -267 """List/set speech-to-text tempo. -268 :param speed The tempo to set. -269 """ -270 TtsSttCmd.tempo(speed) -271 -272 -273@ask_commander.command() -274@click.argument("operation", default="list") -275@click.argument("name", default="onyx") -276def voices(operation: str, name: str | int | None = None) -> None: -277 """List/set/play speech-to-text voices. -278 :param operation The operation to manage voices. -279 :param name The voice name. -280 """ -281 match operation: -282 case "list": -283 TtsSttCmd.voice_list() -284 case "set": -285 TtsSttCmd.voice_set(name) -286 case "play": -287 TtsSttCmd.voice_play(name) -288 case _: -289 err = str(click.BadParameter(f"Invalid voices operation: '{operation}'")) -290 text_formatter.cmd_print(f"%RED%{err}%NC%") -291 -292 -293@ask_commander.command() -294@click.argument("text") -295@click.argument("dest_dir", default="") -296@click.argument("playback", default="True") -297def tts(text: str, dest_dir: str | None = None, playback: bool = True) -> None: -298 """Convert a text to speech using the default AI engine. -299 :param text: The text to be converted. If the text denotes a valid file, its contents will be used instead. -300 :param dest_dir: The destination directory, where to save the converted file. -301 :param playback: Whether to plat the audio file or not. -302 """ -303 _init_context() -304 if (text_path := Path(text)).exists and text_path.is_file(): -305 text: str = text_path.read_text(encoding=Charset.UTF_8.val) -306 TtsSttCmd.tts(text.strip(), dirname(dest_dir), playback) -307 -308 -309@ask_commander.command() -310@click.argument("folder") -311@click.argument("glob", default="**/*") -312def summarize(folder: str, glob: str) -> None: -313 """Generate a summarization of the folder contents. -314 :param folder: The base folder of the summarization. -315 :param glob: The glob pattern or file of the summarization. -316 """ -317 _init_context() -318 GeneralCmd.summarize(folder, glob) -319 -320 -321@ask_commander.command() -322@click.argument("locale_str", default="") -323def idiom(locale_str: str) -> None: -324 """Set the application language. -325 :param locale_str: The locale string. -326 """ -327 GeneralCmd.idiom(locale_str) -328 -329 -330@ask_commander.command() -331def info() -> None: -332 """Display some useful application information.""" -333 if os.getenv("ASKAI_APP"): -334 GeneralCmd.app_info() -335 else: -336 text_formatter.cmd_print("No information available (offline)!") -337 -338 -339@ask_commander.command() -340@click.argument("from_locale_str") -341@click.argument("to_locale_str") -342@click.argument("texts", nargs=-1) -343def translate(from_locale_str: AnyLocale, to_locale_str: AnyLocale, texts: tuple[str, ...]) -> None: -344 """Translate a text from the source language to the target language. -345 :param from_locale_str: The source idiom. -346 :param to_locale_str: The target idiom. -347 :param texts: The texts to be translated. -348 """ -349 GeneralCmd.translate( -350 Language.of_locale(from_locale_str), -351 Language.of_locale(to_locale_str), -352 ' '.join(texts) -353 ) -354 -355 -356@ask_commander.command() -357@click.argument("operation", default="capture") -358@click.argument("args", nargs=-1) -359def camera(operation: str, args: tuple[str, ...]) -> None: -360 """Take photos, import images or identify a person using hte WebCam. -361 :param operation: the camera operation. -362 :param args The operation arguments operate. -363 """ -364 match operation: -365 case "capture" | "photo": -366 CameraCmd.capture(*args) -367 case "identify" | "id": -368 CameraCmd.identify(*args) -369 case "import": -370 CameraCmd.import_images(*args) -371 case _: -372 err = str(click.BadParameter(f"Invalid camera operation: '{operation}'")) -373 text_formatter.cmd_print(f"%RED%{err}%NC%") -374 -375 -376if __name__ == "__main__": -377 pass -378 # shared.create_context(1000) -379 # shared.context.push("LAST_REPLY", "This is the last reply!") -380 # shared.context.push("LAST_REPLY", "This is the another last reply!") -381 # ask_commander(["copy"], standalone_mode=False) -382 # print(pyperclip.paste()) -383 # ask_commander(["info"], standalone_mode=False) -384 # ask_commander(['idiom'], standalone_mode=False) -385 # ask_commander(['idiom', 'pt_BR.iso8859-1'], standalone_mode=False) -386 # ask_commander(['idiom'], standalone_mode=False) -387 # cache_service.cache.save_reply('log', "Log message is a big reply that will be wrapped into") -388 # cache_service.cache.save_reply('audio', "Audio message") -389 # ask_commander(['cache'], standalone_mode=False) -390 # ask_commander(['cache', 'get', 'log', 'audio'], standalone_mode=False) -391 # ask_commander(['cache', 'clear'], standalone_mode=False) -392 # ask_commander(['cache', 'clear', 'log', 'audio'], standalone_mode=False) -393 # ask_commander(['cache', 'files'], standalone_mode=False) -394 # ask_commander(['cache', 'files', 'cleanup', 'askai'], standalone_mode=False) -395 # ask_commander(['cache', 'files', 'cleanup', 'recordings'], standalone_mode=False) -396 # ask_commander(['camera'], standalone_mode=False) -397 # ask_commander(['camera', 'id', 'True'], standalone_mode=False) -398 # ask_commander(['camera', 'import', '/tmp/*.jpeg'], standalone_mode=False) -399 # ask_commander(['camera', 'import', '/Users/hjunior/Downloads'], standalone_mode=False) -400 ask_commander(['translate', 'pt_BR', 'fr_FR', "Olá eu sou o Hugo desenvolvedor pleno!"], standalone_mode=False) +263@ask_commander.command() +264@click.argument("operation", default="list") +265@click.argument("name", default="") +266@click.argument("value", default="") +267def settings(operation: str, name: str | None = None, value: str | None = None) -> None: +268 """Handles modifications to AskAI settings. +269 :param operation: The action to perform on settings. Options: [list|get|set|reset] +270 :param name: The key for the setting to modify. +271 :param value: The new value for the specified setting. +272 """ +273 match operation: +274 case "list": +275 SettingsCmd.list(name) +276 case "get": +277 SettingsCmd.get(name) +278 case "set": +279 SettingsCmd.set(name, value) +280 case "reset": +281 SettingsCmd.reset() +282 case _: +283 err = str(click.BadParameter(f"Invalid settings operation: '{operation}'")) +284 text_formatter.cmd_print(f"Error: {err}") +285 +286 +287@ask_commander.command() +288@click.argument("operation", default="list") +289@click.argument("args", nargs=-1) +290def cache(operation: str, args: tuple[str, ...]) -> None: +291 """Manages AskAI TTL-cache management and associated files. +292 :param operation: Specifies the cache operation. Options: [list|get|clear|files|enable|ttl] +293 :param args: Arguments relevant to the chosen operation. +294 """ +295 match operation: +296 case "list": +297 CacheCmd.list() +298 case "get": +299 if not args: +300 err: str = str(click.MissingParameter(f"Argument 'name' is required. Usage /cache get \\<name\\>")) +301 text_formatter.cmd_print(f"Error: {err}") +302 else: +303 set(map(sysout, map(CacheCmd.get, args))) +304 case "clear": +305 if args and (invalid := next((a for a in args if not isinstance(a, str | int)), None)): +306 err: str = str(click.BadParameter(f"Invalid argument: '{invalid}'")) +307 text_formatter.cmd_print(f"Error: {err}") +308 elif args: +309 set(map(CacheCmd.clear, args)) +310 else: +311 CacheCmd.clear() +312 case "files": +313 CacheCmd.files("cleanup" in args, *args) +314 case "enable": +315 if not args: +316 err: str = str(click.MissingParameter(f"Arguments missing. Usage /cache enable \\<0|1\\>")) +317 text_formatter.cmd_print(f"Error: {err}") +318 else: +319 configs.is_cache = to_bool(args[0]) +320 text_formatter.cmd_print(f"Caching has been *{'en' if configs.is_cache else 'dis'}abled* !") +321 case "ttl": +322 if not args: +323 text_formatter.cmd_print(f"Cache TTL is set to *{configs.ttl} minutes* !") +324 elif not args[0].isdigit(): +325 err: str = str(click.MissingParameter(f"Argument 'minutes' is invalid. Usage /cache ttl \\<minutes\\>")) +326 text_formatter.cmd_print(f"Error: {err}") +327 else: +328 configs.ttl = int(args[0]) +329 text_formatter.cmd_print(f"Cache TTL was set to *{args[0]} minutes* !") +330 case _: +331 err: str = str(click.BadParameter(f"Invalid cache operation: '{operation}'")) +332 text_formatter.cmd_print(f"Error: {err}") +333 +334 +335@ask_commander.command() +336@click.argument("speed", type=click.INT, default=1) +337def tempo(speed: int | None = None) -> None: +338 """Manages the speech-to-text tempo. +339 :param speed: Desired speech tempo setting. Options: [list|get] +340 """ +341 TtsSttCmd.tempo(speed) +342 +343 +344@ask_commander.command() +345@click.argument("operation", default="list") +346@click.argument("name", default="onyx") +347def voices(operation: str, name: str | int | None = None) -> None: +348 """Manages speech-to-text voice operations. +349 :param operation: The action to perform on voices. Options: [list/set/play] +350 :param name: The voice name. +351 """ +352 match operation: +353 case "list": +354 TtsSttCmd.voice_list() +355 case "set": +356 TtsSttCmd.voice_set(name) +357 case "play": +358 TtsSttCmd.voice_play(name) +359 case _: +360 err = str(click.BadParameter(f"Invalid voices operation: '{operation}'")) +361 text_formatter.cmd_print(f"%RED%{err}%NC%") +362 +363 +364@ask_commander.command() +365@click.argument("text") +366@click.argument("dest_dir", default="") +367@click.argument("playback", default="True") +368def tts(text: str, dest_dir: str | None = None, playback: bool = True) -> None: +369 """Convert text to speech using the default AI engine. +370 :param text: The text to convert. If text represents a valid file, its contents will be used instead. +371 :param dest_dir: The directory where the converted audio file will be saved. +372 :param playback: Whether to play the audio file after conversion. +373 """ +374 if (text_path := Path(text)).exists and text_path.is_file(): +375 text: str = text_path.read_text(encoding=Charset.UTF_8.val) +376 TtsSttCmd.tts(text.strip(), dirname(dest_dir), playback) +377 +378 +379@ask_commander.command() +380@click.argument("folder") +381@click.argument("glob", default="**/*") +382def summarize(folder: str, glob: str) -> None: +383 """Create a summary of the folder's contents. +384 :param folder: The root directory for the summary. +385 :param glob: The file pattern or path to summarize. +386 """ +387 GeneralCmd.summarize(folder, glob) +388 +389 +390@ask_commander.command() +391@click.argument("locale_str", default="") +392def idiom(locale_str: str) -> None: +393 """Set the application's language preference. +394 :param locale_str: The locale identifier, e.g., 'pt_BR'. +395 """ +396 GeneralCmd.idiom(locale_str) +397 +398 +399@ask_commander.command() +400def info() -> None: +401 """Display key information about the running application.""" +402 if os.getenv("ASKAI_APP"): +403 GeneralCmd.app_info() +404 else: +405 text_formatter.cmd_print("No information available (offline)!") +406 +407 +408@ask_commander.command() +409@click.argument("from_locale_str") +410@click.argument("to_locale_str") +411@click.argument("texts", nargs=-1) +412def translate(from_locale_str: AnyLocale, to_locale_str: AnyLocale, texts: tuple[str, ...]) -> None: +413 """Translate text from the source language to the target language. +414 :param from_locale_str: The source locale identifier, e.g., 'pt_BR'. +415 :param to_locale_str: The target locale identifier, e.g., 'en_US'. +416 :param texts: The list of texts to translate. +417 """ +418 GeneralCmd.translate(Language.of_locale(from_locale_str), Language.of_locale(to_locale_str), " ".join(texts)) +419 +420 +421@ask_commander.command() +422@click.argument("operation", default="capture") +423@click.argument("args", nargs=-1) +424def camera(operation: str, args: tuple[str, ...]) -> None: +425 """Take photos, import images, or identify a person using the WebCam. +426 :param operation: The camera operation to perform. Options: [capture|identify|import] +427 :param args: The arguments required for the operation. +428 """ +429 match operation: +430 case "capture" | "photo": +431 CameraCmd.capture(*args) +432 case "identify" | "id": +433 CameraCmd.identify(*args) +434 case "import": +435 CameraCmd.import_images(*args) +436 case _: +437 err = str(click.BadParameter(f"Invalid camera operation: '{operation}'")) +438 text_formatter.cmd_print(f"%RED%{err}%NC%") @@ -542,6 +586,18 @@

    + +
    +
    + COMMANDER_HELP_CMD_TPL = +<string.Template object> + + +
    + + + +
    @@ -558,7 +614,8 @@

    - +
    @lru_cache
    + def commands() -> list[str]: @@ -566,17 +623,26 @@

    -
    66def commands() -> list[str]:
    -67    """Return the list of all commander commands."""
    -68    all_commands: set[str] = set()
    -69    for name, obj in __module__.items():
    -70        if obj and isinstance(obj, Command) and not isinstance(obj, Group):
    -71            all_commands.add(f"/{name}")
    -72    return sorted(all_commands, reverse=True)
    +            
    86@lru_cache
    +87def commands() -> list[str]:
    +88    """Return the list of all available commander commands.
    +89    :return: A list of strings representing the available commands.
    +90    """
    +91    all_commands: set[str] = set()
    +92    for name, obj in __module__.items():
    +93        if obj and isinstance(obj, Command) and not isinstance(obj, Group):
    +94            all_commands.add(f"/{name}")
    +95    return sorted(all_commands, reverse=True)
     
    -

    Return the list of all commander commands.

    +

    Return the list of all available commander commands.

    + +
    Returns
    + +
    +

    A list of strings representing the available commands.

    +
    @@ -584,28 +650,85 @@

    - +
    @lru_cache
    + def - commander_help() -> str: + commander_help(command: str | None = None) -> str:
    -
    75def commander_help() -> str:
    -76    """Return the commander help string."""
    -77    helpstr: str = ""
    -78    for cmd, obj in __module__.items():
    -79        if obj and isinstance(obj, Command) and not isinstance(obj, Group):
    -80            cmd_doc: str = f"{obj.__doc__.split(os.linesep, 1)[0]}**"
    -81            helpstr += f"| /{'*' + cmd + '*':<8} | **{cmd_doc:<43} |\n"
    -82    h_str: str = f"| {'**Command**':<9} | {'**Description**':<45} |\n"
    -83    h_str += f"| {'-' * 9} | {'-' * 45} |\n"
    -84    return COMMANDER_HELP_TPL.substitute(commands=f"{h_str}{helpstr}")
    +            
     98@lru_cache
    + 99def commander_help(command: str | None = None) -> str:
    +100    """Return the help string for the specified commander command.
    +101    :param command: The command for which to retrieve help.
    +102    :return: A string containing the help information for the specified command.
    +103    """
    +104    if (command in __module__) and (cmd := __module__[command]):
    +105        return _format_help(cmd)
    +106    else:
    +107        help_str: str = ""
    +108        for cmd, obj in __module__.items():
    +109            if obj and isinstance(obj, Command) and not isinstance(obj, Group):
    +110                cmd_doc: str = f"{obj.__doc__.split(os.linesep, 1)[0]}**"
    +111                help_str += f"| /{'*' + cmd + '*':<8} | **{cmd_doc:<43} |\n"
    +112        h_str: str = f"| {'**Command**':<9} | {'**Description**':<45} |\n"
    +113        h_str += f"| {'-' * 9} | {'-' * 45} |\n"
    +114        return COMMANDER_HELP_TPL.substitute(commands=f"{h_str}{help_str}")
     
    -

    Return the commander help string.

    +

    Return the help string for the specified commander command.

    + +
    Parameters
    + +
      +
    • command: The command for which to retrieve help.
    • +
    + +
    Returns
    + +
    +

    A string containing the help information for the specified command.

    +
    +
    + + +
    +
    + +
    + + def + is_command(string: str) -> bool: + + + +
    + +
    117def is_command(string: str) -> bool:
    +118    """Check if the given string is a command.
    +119    :param string: The string to check.
    +120    :return: True if the string is a command, False otherwise.
    +121    """
    +122    return string.startswith("/") and string in commands()
    +
    + + +

    Check if the given string is a command.

    + +
    Parameters
    + +
      +
    • string: The string to check.
    • +
    + +
    Returns
    + +
    +

    True if the string is a command, False otherwise.

    +
    @@ -619,7 +742,8 @@

    -

    AskAI commands group.

    +

    AskAI commands group. This function serves as the entry point for the AskAI command-line interface (CLI) +commands, grouping related commands together.

    @@ -633,7 +757,14 @@

    -

    Show this help message and exit.

    +

    Display the help message for the specified command and exit. If no command is provided, it displays the general +help message.

    + +
    Parameters
    + +
      +
    • command: The command to retrieve help for (optional).
    • +
    @@ -675,9 +806,14 @@

    -

    List/forget the current context window. -:param operation The operation to manage contexts. -:param name The context name.

    +

    Manage the current chat context window.

    + +
    Parameters
    + +
      +
    • operation: The operation to perform on contexts. Options: [list | forget].
    • +
    • name: The name of the context to target (default is "ALL").
    • +
    @@ -691,8 +827,13 @@

    -

    List/forget the input history. -:param operation The operation to manage history.

    +

    Manages the current input history.

    + +
    Parameters
    + +
      +
    • operation: The operation to perform on contexts. Options: [list|forget].
    • +
    @@ -706,12 +847,12 @@

    -

    Copy a context entry to the clipboard

    +

    Copy the specified context entry to the clipboard.

    Parameters
      -
    • name: The context name.
    • +
    • name: The name of the context entry to copy.
    @@ -726,9 +867,14 @@
    Parameters
    -

    List/set the audio input devices. -:param operation The operation to manage devices. -:param name The device name to set.

    +

    Manages the audio input devices.

    + +
    Parameters
    + +
      +
    • operation: Specifies the device operation. Options: [list|set].
    • +
    • name: The target device name for setting.
    • +
    @@ -742,10 +888,15 @@
    Parameters
    -

    List/get/set/reset AskAI settings. -:param operation The operation to manage settings. -:param name The settings key to operate. -:param value The settings value to be set.

    +

    Handles modifications to AskAI settings.

    + +
    Parameters
    + +
      +
    • operation: The action to perform on settings. Options: [list|get|set|reset]
    • +
    • name: The key for the setting to modify.
    • +
    • value: The new value for the specified setting.
    • +
    @@ -759,9 +910,14 @@
    Parameters
    -

    List/get/clear/files AskAI TTL cache and files. -:param operation The operation to manage cache. -:param args The operation arguments operate.

    +

    Manages AskAI TTL-cache management and associated files.

    + +
    Parameters
    + +
      +
    • operation: Specifies the cache operation. Options: [list|get|clear|files|enable|ttl]
    • +
    • args: Arguments relevant to the chosen operation.
    • +
    @@ -775,8 +931,13 @@
    Parameters
    -

    List/set speech-to-text tempo. -:param speed The tempo to set.

    +

    Manages the speech-to-text tempo.

    + +
    Parameters
    + +
      +
    • speed: Desired speech tempo setting. Options: [list|get]
    • +
    @@ -790,9 +951,14 @@
    Parameters
    -

    List/set/play speech-to-text voices. -:param operation The operation to manage voices. -:param name The voice name.

    +

    Manages speech-to-text voice operations.

    + +
    Parameters
    + +
      +
    • operation: The action to perform on voices. Options: [list/set/play]
    • +
    • name: The voice name.
    • +
    @@ -806,14 +972,14 @@
    Parameters
    -

    Convert a text to speech using the default AI engine.

    +

    Convert text to speech using the default AI engine.

    Parameters
      -
    • text: The text to be converted. If the text denotes a valid file, its contents will be used instead.
    • -
    • dest_dir: The destination directory, where to save the converted file.
    • -
    • playback: Whether to plat the audio file or not.
    • +
    • text: The text to convert. If text represents a valid file, its contents will be used instead.
    • +
    • dest_dir: The directory where the converted audio file will be saved.
    • +
    • playback: Whether to play the audio file after conversion.
    @@ -828,13 +994,13 @@
    Parameters
    -

    Generate a summarization of the folder contents.

    +

    Create a summary of the folder's contents.

    Parameters
      -
    • folder: The base folder of the summarization.
    • -
    • glob: The glob pattern or file of the summarization.
    • +
    • folder: The root directory for the summary.
    • +
    • glob: The file pattern or path to summarize.
    @@ -849,12 +1015,12 @@
    Parameters
    -

    Set the application language.

    +

    Set the application's language preference.

    Parameters
      -
    • locale_str: The locale string.
    • +
    • locale_str: The locale identifier, e.g., 'pt_BR'.
    @@ -869,7 +1035,7 @@
    Parameters
    -

    Display some useful application information.

    +

    Display key information about the running application.

    @@ -883,14 +1049,14 @@
    Parameters
    -

    Translate a text from the source language to the target language.

    +

    Translate text from the source language to the target language.

    Parameters
      -
    • from_locale_str: The source idiom.
    • -
    • to_locale_str: The target idiom.
    • -
    • texts: The texts to be translated.
    • +
    • from_locale_str: The source locale identifier, e.g., 'pt_BR'.
    • +
    • to_locale_str: The target locale identifier, e.g., 'en_US'.
    • +
    • texts: The list of texts to translate.
    @@ -905,13 +1071,13 @@
    Parameters
    -

    Take photos, import images or identify a person using hte WebCam.

    +

    Take photos, import images, or identify a person using the WebCam.

    Parameters
      -
    • operation: the camera operation. -:param args The operation arguments operate.
    • +
    • operation: The camera operation to perform. Options: [capture|identify|import]
    • +
    • args: The arguments required for the operation.
    diff --git a/docs/api/askai/main/askai/core/commander/commands.html b/docs/api/askai/main/askai/core/commander/commands.html index 1fd05b8b..787fc90a 100644 --- a/docs/api/askai/main/askai/core/commander/commands.html +++ b/docs/api/askai/main/askai/core/commander/commands.html @@ -3,7 +3,7 @@ - + main.askai.core.commander.commands API documentation @@ -60,7 +60,7 @@

     1# _*_ coding: utf-8 _*_
      2#
    - 3# hspylib-askai v1.0.11
    + 3# hspylib-askai v1.0.13
      4#
      5# Package: main.askai.core.commander.commands
      6"""Package initialization."""
    @@ -73,7 +73,7 @@ 

    13 'settings_cmd', 14 'tts_stt_cmd' 15] -16__version__ = '1.0.11' +16__version__ = '1.0.13'

    diff --git a/docs/api/askai/main/askai/core/commander/commands/cache_cmd.html b/docs/api/askai/main/askai/core/commander/commands/cache_cmd.html index dad09781..b8dfc4c1 100644 --- a/docs/api/askai/main/askai/core/commander/commands/cache_cmd.html +++ b/docs/api/askai/main/askai/core/commander/commands/cache_cmd.html @@ -3,7 +3,7 @@ - + main.askai.core.commander.commands.cache_cmd API documentation @@ -93,100 +93,107 @@

    12 13 Copyright (c) 2024, HomeSetup 14""" - 15import os - 16from abc import ABC - 17from functools import partial - 18from pathlib import Path - 19from typing import Optional - 20 + 15from abc import ABC + 16from askai.core.askai_configs import configs + 17from askai.core.component.cache_service import cache, CACHE_DIR + 18from askai.core.support.text_formatter import text_formatter + 19from askai.core.support.utilities import display_text + 20from functools import partial 21from hspylib.core.tools.commons import human_readable_bytes, sysout 22from hspylib.core.tools.text_tools import elide_text - 23 - 24from askai.core.askai_configs import configs - 25from askai.core.component.cache_service import cache, CACHE_DIR - 26from askai.core.support.text_formatter import text_formatter - 27from askai.core.support.utilities import display_text + 23from pathlib import Path + 24from typing import Optional + 25 + 26import os + 27 28 - 29 - 30class CacheCmd(ABC): - 31 """Provides cache command functionalities.""" - 32 - 33 @staticmethod - 34 def list() -> None: - 35 """List all cache entries.""" - 36 if not configs.is_cache: - 37 sysout(f"\n%ORANGE%-=- Cache is disabled! -=-%NC%\n") - 38 elif (all_keys := sorted(cache.keys)) and (length := len(all_keys)) > 0: - 39 display_text(f"### Listing ALL ({length}) Cached Questions:\n\n---\n\n") - 40 entries: str = "" - 41 for i, query in enumerate(all_keys, start=1): - 42 answer: str | None = cache.read_reply(query) - 43 if not answer: - 44 continue - 45 cache_str: str = elide_text(answer, 80).replace(os.linesep, "␊") - 46 entries += f"{i}. **{query}**: `{cache_str}` \n" - 47 display_text(entries) - 48 else: - 49 sysout(f"\n%RED%-=- Caching is empty! -=-%NC%") - 50 display_text("\n> Hint: Type: '/cache [get|clear|files|enable|ttl] <args>'.") - 51 - 52 @staticmethod - 53 def get(name: str) -> Optional[str]: - 54 """Get cache entry by name.""" - 55 entry: str = cache.read_reply(name) - 56 return ( - 57 f'%GREEN%{name}%NC% cache(s) is %CYAN%"{entry}"%NC%' - 58 if entry - 59 else f"%YELLOW%'{name}'%NC% was not found in the cache!" - 60 ) - 61 - 62 @staticmethod - 63 def clear(entry: str | int | None = None) -> None: - 64 """Clear a specified, or all, cache entries.""" - 65 deleted: str | None = None - 66 if entry: - 67 if isinstance(entry, int): - 68 if name := sorted(cache.keys)[entry]: - 69 deleted = cache.del_reply(name) - 70 elif isinstance(entry, str): - 71 if name := next((obj for obj in cache.keys if obj == entry), None): - 72 deleted = cache.del_reply(name) - 73 else: - 74 deleted = str(cache.clear_replies()) - 75 text_formatter.cmd_print(f"*{deleted if deleted else 'No'}* cache(s) has been cleared!") - 76 - 77 @staticmethod - 78 def files(cleanup: bool = False, *args: str | int) -> None: - 79 """Enlist all cached files (from askai cache dir).""" - 80 if os.path.exists(CACHE_DIR): - 81 if cleanup: - 82 for arg in args[1 if cleanup else 0 :]: - 83 i_files = Path(f"{CACHE_DIR}").glob(f"{arg}*") - 84 while (cached := next(i_files, None)) and os.path.exists(cached): - 85 f_join = partial(os.path.join, f"{CACHE_DIR}/{arg}") - 86 if os.path.isdir(cached): - 87 set(map(os.remove, map(f_join, os.listdir(cached)))) - 88 text_formatter.cmd_print(f"Folder *{cached}* has been cleared!") - 89 elif os.path.isfile(cached): - 90 os.remove(cached) - 91 text_formatter.cmd_print(f"File **{cached}** was removed!") - 92 else: - 93 display_text(f"### Listing cached files from '{CACHE_DIR}':\n\n---\n\n") - 94 files, dirs = "", "" - 95 for cached in os.listdir(CACHE_DIR): - 96 f_cached = os.path.join(CACHE_DIR, cached) - 97 if os.path.isdir(f_cached): - 98 f_join = partial(os.path.join, f"{CACHE_DIR}/{cached}") - 99 f_all = list(map(os.path.getsize, map(f_join, os.listdir(f_cached)))) -100 size, unit = human_readable_bytes(sum(f_all)) -101 dirs += f"- *{cached}* ({len(f_all)} files {size} {unit}) \n" -102 else: -103 size, unit = human_readable_bytes(os.path.getsize(f_cached)) -104 files += f"- **{cached}** ({size} {unit})\n" -105 display_text(f"{dirs}\n{files}") -106 display_text(f"\n> Hint: Type: '/cache files cleanup [globs ...]' to delete cached files.") -107 else: -108 sysout(f"\n%RED%-=- Cache dir {CACHE_DIR} does not exist! -=-%NC%\n") + 29class CacheCmd(ABC): + 30 """Provides cache command functionalities.""" + 31 + 32 @staticmethod + 33 def list() -> None: + 34 """List all cache entries.""" + 35 if not configs.is_cache: + 36 sysout(f"\n%ORANGE%-=- Cache is disabled! -=-%NC%\n") + 37 elif (all_keys := sorted(cache.keys)) and (length := len(all_keys)) > 0: + 38 display_text(f"### Listing ALL ({length}) Cached Questions:\n\n---\n\n") + 39 entries: str = "" + 40 for i, query in enumerate(all_keys, start=1): + 41 answer: str | None = cache.read_reply(query) + 42 if not answer: + 43 continue + 44 cache_str: str = elide_text(answer, 80).replace(os.linesep, "␊") + 45 entries += f"{i}. **{query}**: `{cache_str}` \n" + 46 display_text(entries) + 47 else: + 48 sysout(f"\n%RED%-=- Caching is empty! -=-%NC%") + 49 display_text("\n> Hint: Type: '/cache [get|clear|files|enable|ttl] <args>'.") + 50 + 51 @staticmethod + 52 def get(name: str) -> Optional[str]: + 53 """Retrieve a cache entry by its name. + 54 :param name: The name of the cache entry to retrieve. + 55 :return: The value of the cache entry as a string, or None if the entry does not exist. + 56 """ + 57 entry: str = cache.read_reply(name) + 58 return ( + 59 f'%GREEN%{name}%NC% cache(s) is %CYAN%"{entry}"%NC%' + 60 if entry + 61 else f"%YELLOW%'{name}'%NC% was not found in the cache!" + 62 ) + 63 + 64 @staticmethod + 65 def clear(entry: str | int | None = None) -> None: + 66 """Clear a specified cache entry or all cache entries. + 67 :param entry: The cache entry to clear, specified by name or index. If None, all cache entries will be cleared. + 68 """ + 69 deleted: str | None = None + 70 if entry: + 71 if isinstance(entry, int): + 72 if name := sorted(cache.keys)[entry]: + 73 deleted = cache.del_reply(name) + 74 elif isinstance(entry, str): + 75 if name := next((obj for obj in cache.keys if obj == entry), None): + 76 deleted = cache.del_reply(name) + 77 else: + 78 deleted = str(cache.clear_replies()) + 79 text_formatter.cmd_print(f"*{deleted if deleted else 'No'}* cache(s) has been cleared!") + 80 + 81 @staticmethod + 82 def files(cleanup: bool = False, *args: str | int) -> None: + 83 """List all cached files from the AskAI cache directory. + 84 :param cleanup: If True, clean up the listed cache files after displaying them (default is False). + 85 :param args: Specific file names or indices to target for listing or cleanup. + 86 """ + 87 if os.path.exists(CACHE_DIR): + 88 if cleanup: + 89 for arg in args[1 if cleanup else 0 :]: + 90 i_files = Path(f"{CACHE_DIR}").glob(f"{arg}*") + 91 while (cached := next(i_files, None)) and os.path.exists(cached): + 92 f_join = partial(os.path.join, f"{CACHE_DIR}/{arg}") + 93 if os.path.isdir(cached): + 94 set(map(os.remove, map(f_join, os.listdir(cached)))) + 95 text_formatter.cmd_print(f"Folder *{cached}* has been cleared!") + 96 elif os.path.isfile(cached): + 97 os.remove(cached) + 98 text_formatter.cmd_print(f"File **{cached}** was removed!") + 99 else: +100 display_text(f"### Listing cached files from '{CACHE_DIR}':\n\n---\n\n") +101 files, dirs = "", "" +102 for cached in os.listdir(CACHE_DIR): +103 f_cached = os.path.join(CACHE_DIR, cached) +104 if os.path.isdir(f_cached): +105 f_join = partial(os.path.join, f"{CACHE_DIR}/{cached}") +106 f_all = list(map(os.path.getsize, map(f_join, os.listdir(f_cached)))) +107 size, unit = human_readable_bytes(sum(f_all)) +108 dirs += f"- *{cached}* ({len(f_all)} files {size} {unit}) \n" +109 else: +110 size, unit = human_readable_bytes(os.path.getsize(f_cached)) +111 files += f"- **{cached}** ({size} {unit})\n" +112 display_text(f"{dirs}\n{files}") +113 display_text(f"\n> Hint: Type: '/cache files cleanup [globs ...]' to delete cached files.") +114 else: +115 sysout(f"\n%RED%-=- Cache dir {CACHE_DIR} does not exist! -=-%NC%\n")

    @@ -202,85 +209,93 @@

    -
     31class CacheCmd(ABC):
    - 32    """Provides cache command functionalities."""
    - 33
    - 34    @staticmethod
    - 35    def list() -> None:
    - 36        """List all cache entries."""
    - 37        if not configs.is_cache:
    - 38            sysout(f"\n%ORANGE%-=- Cache is disabled! -=-%NC%\n")
    - 39        elif (all_keys := sorted(cache.keys)) and (length := len(all_keys)) > 0:
    - 40            display_text(f"### Listing ALL ({length}) Cached Questions:\n\n---\n\n")
    - 41            entries: str = ""
    - 42            for i, query in enumerate(all_keys, start=1):
    - 43                answer: str | None = cache.read_reply(query)
    - 44                if not answer:
    - 45                    continue
    - 46                cache_str: str = elide_text(answer, 80).replace(os.linesep, "␊")
    - 47                entries += f"{i}. **{query}**: `{cache_str}` \n"
    - 48            display_text(entries)
    - 49        else:
    - 50            sysout(f"\n%RED%-=- Caching is empty! -=-%NC%")
    - 51        display_text("\n> Hint: Type: '/cache [get|clear|files|enable|ttl] <args>'.")
    - 52
    - 53    @staticmethod
    - 54    def get(name: str) -> Optional[str]:
    - 55        """Get cache entry by name."""
    - 56        entry: str = cache.read_reply(name)
    - 57        return (
    - 58            f'%GREEN%{name}%NC% cache(s) is %CYAN%"{entry}"%NC%'
    - 59            if entry
    - 60            else f"%YELLOW%'{name}'%NC% was not found in the cache!"
    - 61        )
    - 62
    - 63    @staticmethod
    - 64    def clear(entry: str | int | None = None) -> None:
    - 65        """Clear a specified, or all, cache entries."""
    - 66        deleted: str | None = None
    - 67        if entry:
    - 68            if isinstance(entry, int):
    - 69                if name := sorted(cache.keys)[entry]:
    - 70                    deleted = cache.del_reply(name)
    - 71            elif isinstance(entry, str):
    - 72                if name := next((obj for obj in cache.keys if obj == entry), None):
    - 73                    deleted = cache.del_reply(name)
    - 74        else:
    - 75            deleted = str(cache.clear_replies())
    - 76        text_formatter.cmd_print(f"*{deleted if deleted else 'No'}* cache(s) has been cleared!")
    - 77
    - 78    @staticmethod
    - 79    def files(cleanup: bool = False, *args: str | int) -> None:
    - 80        """Enlist all cached files (from askai cache dir)."""
    - 81        if os.path.exists(CACHE_DIR):
    - 82            if cleanup:
    - 83                for arg in args[1 if cleanup else 0 :]:
    - 84                    i_files = Path(f"{CACHE_DIR}").glob(f"{arg}*")
    - 85                    while (cached := next(i_files, None)) and os.path.exists(cached):
    - 86                        f_join = partial(os.path.join, f"{CACHE_DIR}/{arg}")
    - 87                        if os.path.isdir(cached):
    - 88                            set(map(os.remove, map(f_join, os.listdir(cached))))
    - 89                            text_formatter.cmd_print(f"Folder *{cached}* has been cleared!")
    - 90                        elif os.path.isfile(cached):
    - 91                            os.remove(cached)
    - 92                            text_formatter.cmd_print(f"File **{cached}** was removed!")
    - 93            else:
    - 94                display_text(f"### Listing cached files from '{CACHE_DIR}':\n\n---\n\n")
    - 95                files, dirs = "", ""
    - 96                for cached in os.listdir(CACHE_DIR):
    - 97                    f_cached = os.path.join(CACHE_DIR, cached)
    - 98                    if os.path.isdir(f_cached):
    - 99                        f_join = partial(os.path.join, f"{CACHE_DIR}/{cached}")
    -100                        f_all = list(map(os.path.getsize, map(f_join, os.listdir(f_cached))))
    -101                        size, unit = human_readable_bytes(sum(f_all))
    -102                        dirs += f"- *{cached}* ({len(f_all)} files {size} {unit}) \n"
    -103                    else:
    -104                        size, unit = human_readable_bytes(os.path.getsize(f_cached))
    -105                        files += f"- **{cached}** ({size} {unit})\n"
    -106                display_text(f"{dirs}\n{files}")
    -107                display_text(f"\n> Hint: Type: '/cache files cleanup [globs ...]' to delete cached files.")
    -108        else:
    -109            sysout(f"\n%RED%-=- Cache dir {CACHE_DIR} does not exist! -=-%NC%\n")
    +            
     30class CacheCmd(ABC):
    + 31    """Provides cache command functionalities."""
    + 32
    + 33    @staticmethod
    + 34    def list() -> None:
    + 35        """List all cache entries."""
    + 36        if not configs.is_cache:
    + 37            sysout(f"\n%ORANGE%-=- Cache is disabled! -=-%NC%\n")
    + 38        elif (all_keys := sorted(cache.keys)) and (length := len(all_keys)) > 0:
    + 39            display_text(f"### Listing ALL ({length}) Cached Questions:\n\n---\n\n")
    + 40            entries: str = ""
    + 41            for i, query in enumerate(all_keys, start=1):
    + 42                answer: str | None = cache.read_reply(query)
    + 43                if not answer:
    + 44                    continue
    + 45                cache_str: str = elide_text(answer, 80).replace(os.linesep, "␊")
    + 46                entries += f"{i}. **{query}**: `{cache_str}` \n"
    + 47            display_text(entries)
    + 48        else:
    + 49            sysout(f"\n%RED%-=- Caching is empty! -=-%NC%")
    + 50        display_text("\n> Hint: Type: '/cache [get|clear|files|enable|ttl] <args>'.")
    + 51
    + 52    @staticmethod
    + 53    def get(name: str) -> Optional[str]:
    + 54        """Retrieve a cache entry by its name.
    + 55        :param name: The name of the cache entry to retrieve.
    + 56        :return: The value of the cache entry as a string, or None if the entry does not exist.
    + 57        """
    + 58        entry: str = cache.read_reply(name)
    + 59        return (
    + 60            f'%GREEN%{name}%NC% cache(s) is %CYAN%"{entry}"%NC%'
    + 61            if entry
    + 62            else f"%YELLOW%'{name}'%NC% was not found in the cache!"
    + 63        )
    + 64
    + 65    @staticmethod
    + 66    def clear(entry: str | int | None = None) -> None:
    + 67        """Clear a specified cache entry or all cache entries.
    + 68        :param entry: The cache entry to clear, specified by name or index. If None, all cache entries will be cleared.
    + 69        """
    + 70        deleted: str | None = None
    + 71        if entry:
    + 72            if isinstance(entry, int):
    + 73                if name := sorted(cache.keys)[entry]:
    + 74                    deleted = cache.del_reply(name)
    + 75            elif isinstance(entry, str):
    + 76                if name := next((obj for obj in cache.keys if obj == entry), None):
    + 77                    deleted = cache.del_reply(name)
    + 78        else:
    + 79            deleted = str(cache.clear_replies())
    + 80        text_formatter.cmd_print(f"*{deleted if deleted else 'No'}* cache(s) has been cleared!")
    + 81
    + 82    @staticmethod
    + 83    def files(cleanup: bool = False, *args: str | int) -> None:
    + 84        """List all cached files from the AskAI cache directory.
    + 85        :param cleanup: If True, clean up the listed cache files after displaying them (default is False).
    + 86        :param args: Specific file names or indices to target for listing or cleanup.
    + 87        """
    + 88        if os.path.exists(CACHE_DIR):
    + 89            if cleanup:
    + 90                for arg in args[1 if cleanup else 0 :]:
    + 91                    i_files = Path(f"{CACHE_DIR}").glob(f"{arg}*")
    + 92                    while (cached := next(i_files, None)) and os.path.exists(cached):
    + 93                        f_join = partial(os.path.join, f"{CACHE_DIR}/{arg}")
    + 94                        if os.path.isdir(cached):
    + 95                            set(map(os.remove, map(f_join, os.listdir(cached))))
    + 96                            text_formatter.cmd_print(f"Folder *{cached}* has been cleared!")
    + 97                        elif os.path.isfile(cached):
    + 98                            os.remove(cached)
    + 99                            text_formatter.cmd_print(f"File **{cached}** was removed!")
    +100            else:
    +101                display_text(f"### Listing cached files from '{CACHE_DIR}':\n\n---\n\n")
    +102                files, dirs = "", ""
    +103                for cached in os.listdir(CACHE_DIR):
    +104                    f_cached = os.path.join(CACHE_DIR, cached)
    +105                    if os.path.isdir(f_cached):
    +106                        f_join = partial(os.path.join, f"{CACHE_DIR}/{cached}")
    +107                        f_all = list(map(os.path.getsize, map(f_join, os.listdir(f_cached))))
    +108                        size, unit = human_readable_bytes(sum(f_all))
    +109                        dirs += f"- *{cached}* ({len(f_all)} files {size} {unit}) \n"
    +110                    else:
    +111                        size, unit = human_readable_bytes(os.path.getsize(f_cached))
    +112                        files += f"- **{cached}** ({size} {unit})\n"
    +113                display_text(f"{dirs}\n{files}")
    +114                display_text(f"\n> Hint: Type: '/cache files cleanup [globs ...]' to delete cached files.")
    +115        else:
    +116            sysout(f"\n%RED%-=- Cache dir {CACHE_DIR} does not exist! -=-%NC%\n")
     
    @@ -300,24 +315,24 @@

    -
    34    @staticmethod
    -35    def list() -> None:
    -36        """List all cache entries."""
    -37        if not configs.is_cache:
    -38            sysout(f"\n%ORANGE%-=- Cache is disabled! -=-%NC%\n")
    -39        elif (all_keys := sorted(cache.keys)) and (length := len(all_keys)) > 0:
    -40            display_text(f"### Listing ALL ({length}) Cached Questions:\n\n---\n\n")
    -41            entries: str = ""
    -42            for i, query in enumerate(all_keys, start=1):
    -43                answer: str | None = cache.read_reply(query)
    -44                if not answer:
    -45                    continue
    -46                cache_str: str = elide_text(answer, 80).replace(os.linesep, "␊")
    -47                entries += f"{i}. **{query}**: `{cache_str}` \n"
    -48            display_text(entries)
    -49        else:
    -50            sysout(f"\n%RED%-=- Caching is empty! -=-%NC%")
    -51        display_text("\n> Hint: Type: '/cache [get|clear|files|enable|ttl] <args>'.")
    +            
    33    @staticmethod
    +34    def list() -> None:
    +35        """List all cache entries."""
    +36        if not configs.is_cache:
    +37            sysout(f"\n%ORANGE%-=- Cache is disabled! -=-%NC%\n")
    +38        elif (all_keys := sorted(cache.keys)) and (length := len(all_keys)) > 0:
    +39            display_text(f"### Listing ALL ({length}) Cached Questions:\n\n---\n\n")
    +40            entries: str = ""
    +41            for i, query in enumerate(all_keys, start=1):
    +42                answer: str | None = cache.read_reply(query)
    +43                if not answer:
    +44                    continue
    +45                cache_str: str = elide_text(answer, 80).replace(os.linesep, "␊")
    +46                entries += f"{i}. **{query}**: `{cache_str}` \n"
    +47            display_text(entries)
    +48        else:
    +49            sysout(f"\n%RED%-=- Caching is empty! -=-%NC%")
    +50        display_text("\n> Hint: Type: '/cache [get|clear|files|enable|ttl] <args>'.")
     
    @@ -338,19 +353,34 @@

    -
    53    @staticmethod
    -54    def get(name: str) -> Optional[str]:
    -55        """Get cache entry by name."""
    -56        entry: str = cache.read_reply(name)
    -57        return (
    -58            f'%GREEN%{name}%NC% cache(s) is %CYAN%"{entry}"%NC%'
    -59            if entry
    -60            else f"%YELLOW%'{name}'%NC% was not found in the cache!"
    -61        )
    +            
    52    @staticmethod
    +53    def get(name: str) -> Optional[str]:
    +54        """Retrieve a cache entry by its name.
    +55        :param name: The name of the cache entry to retrieve.
    +56        :return: The value of the cache entry as a string, or None if the entry does not exist.
    +57        """
    +58        entry: str = cache.read_reply(name)
    +59        return (
    +60            f'%GREEN%{name}%NC% cache(s) is %CYAN%"{entry}"%NC%'
    +61            if entry
    +62            else f"%YELLOW%'{name}'%NC% was not found in the cache!"
    +63        )
     
    -

    Get cache entry by name.

    +

    Retrieve a cache entry by its name.

    + +
    Parameters
    + +
      +
    • name: The name of the cache entry to retrieve.
    • +
    + +
    Returns
    + +
    +

    The value of the cache entry as a string, or None if the entry does not exist.

    +
    @@ -367,24 +397,32 @@

    -
    63    @staticmethod
    -64    def clear(entry: str | int | None = None) -> None:
    -65        """Clear a specified, or all, cache entries."""
    -66        deleted: str | None = None
    -67        if entry:
    -68            if isinstance(entry, int):
    -69                if name := sorted(cache.keys)[entry]:
    -70                    deleted = cache.del_reply(name)
    -71            elif isinstance(entry, str):
    -72                if name := next((obj for obj in cache.keys if obj == entry), None):
    -73                    deleted = cache.del_reply(name)
    -74        else:
    -75            deleted = str(cache.clear_replies())
    -76        text_formatter.cmd_print(f"*{deleted if deleted else 'No'}* cache(s) has been cleared!")
    +            
    65    @staticmethod
    +66    def clear(entry: str | int | None = None) -> None:
    +67        """Clear a specified cache entry or all cache entries.
    +68        :param entry: The cache entry to clear, specified by name or index. If None, all cache entries will be cleared.
    +69        """
    +70        deleted: str | None = None
    +71        if entry:
    +72            if isinstance(entry, int):
    +73                if name := sorted(cache.keys)[entry]:
    +74                    deleted = cache.del_reply(name)
    +75            elif isinstance(entry, str):
    +76                if name := next((obj for obj in cache.keys if obj == entry), None):
    +77                    deleted = cache.del_reply(name)
    +78        else:
    +79            deleted = str(cache.clear_replies())
    +80        text_formatter.cmd_print(f"*{deleted if deleted else 'No'}* cache(s) has been cleared!")
     
    -

    Clear a specified, or all, cache entries.

    +

    Clear a specified cache entry or all cache entries.

    + +
    Parameters
    + +
      +
    • entry: The cache entry to clear, specified by name or index. If None, all cache entries will be cleared.
    • +
    @@ -401,42 +439,52 @@

    -
     78    @staticmethod
    - 79    def files(cleanup: bool = False, *args: str | int) -> None:
    - 80        """Enlist all cached files (from askai cache dir)."""
    - 81        if os.path.exists(CACHE_DIR):
    - 82            if cleanup:
    - 83                for arg in args[1 if cleanup else 0 :]:
    - 84                    i_files = Path(f"{CACHE_DIR}").glob(f"{arg}*")
    - 85                    while (cached := next(i_files, None)) and os.path.exists(cached):
    - 86                        f_join = partial(os.path.join, f"{CACHE_DIR}/{arg}")
    - 87                        if os.path.isdir(cached):
    - 88                            set(map(os.remove, map(f_join, os.listdir(cached))))
    - 89                            text_formatter.cmd_print(f"Folder *{cached}* has been cleared!")
    - 90                        elif os.path.isfile(cached):
    - 91                            os.remove(cached)
    - 92                            text_formatter.cmd_print(f"File **{cached}** was removed!")
    - 93            else:
    - 94                display_text(f"### Listing cached files from '{CACHE_DIR}':\n\n---\n\n")
    - 95                files, dirs = "", ""
    - 96                for cached in os.listdir(CACHE_DIR):
    - 97                    f_cached = os.path.join(CACHE_DIR, cached)
    - 98                    if os.path.isdir(f_cached):
    - 99                        f_join = partial(os.path.join, f"{CACHE_DIR}/{cached}")
    -100                        f_all = list(map(os.path.getsize, map(f_join, os.listdir(f_cached))))
    -101                        size, unit = human_readable_bytes(sum(f_all))
    -102                        dirs += f"- *{cached}* ({len(f_all)} files {size} {unit}) \n"
    -103                    else:
    -104                        size, unit = human_readable_bytes(os.path.getsize(f_cached))
    -105                        files += f"- **{cached}** ({size} {unit})\n"
    -106                display_text(f"{dirs}\n{files}")
    -107                display_text(f"\n> Hint: Type: '/cache files cleanup [globs ...]' to delete cached files.")
    -108        else:
    -109            sysout(f"\n%RED%-=- Cache dir {CACHE_DIR} does not exist! -=-%NC%\n")
    +            
     82    @staticmethod
    + 83    def files(cleanup: bool = False, *args: str | int) -> None:
    + 84        """List all cached files from the AskAI cache directory.
    + 85        :param cleanup: If True, clean up the listed cache files after displaying them (default is False).
    + 86        :param args: Specific file names or indices to target for listing or cleanup.
    + 87        """
    + 88        if os.path.exists(CACHE_DIR):
    + 89            if cleanup:
    + 90                for arg in args[1 if cleanup else 0 :]:
    + 91                    i_files = Path(f"{CACHE_DIR}").glob(f"{arg}*")
    + 92                    while (cached := next(i_files, None)) and os.path.exists(cached):
    + 93                        f_join = partial(os.path.join, f"{CACHE_DIR}/{arg}")
    + 94                        if os.path.isdir(cached):
    + 95                            set(map(os.remove, map(f_join, os.listdir(cached))))
    + 96                            text_formatter.cmd_print(f"Folder *{cached}* has been cleared!")
    + 97                        elif os.path.isfile(cached):
    + 98                            os.remove(cached)
    + 99                            text_formatter.cmd_print(f"File **{cached}** was removed!")
    +100            else:
    +101                display_text(f"### Listing cached files from '{CACHE_DIR}':\n\n---\n\n")
    +102                files, dirs = "", ""
    +103                for cached in os.listdir(CACHE_DIR):
    +104                    f_cached = os.path.join(CACHE_DIR, cached)
    +105                    if os.path.isdir(f_cached):
    +106                        f_join = partial(os.path.join, f"{CACHE_DIR}/{cached}")
    +107                        f_all = list(map(os.path.getsize, map(f_join, os.listdir(f_cached))))
    +108                        size, unit = human_readable_bytes(sum(f_all))
    +109                        dirs += f"- *{cached}* ({len(f_all)} files {size} {unit}) \n"
    +110                    else:
    +111                        size, unit = human_readable_bytes(os.path.getsize(f_cached))
    +112                        files += f"- **{cached}** ({size} {unit})\n"
    +113                display_text(f"{dirs}\n{files}")
    +114                display_text(f"\n> Hint: Type: '/cache files cleanup [globs ...]' to delete cached files.")
    +115        else:
    +116            sysout(f"\n%RED%-=- Cache dir {CACHE_DIR} does not exist! -=-%NC%\n")
     
    -

    Enlist all cached files (from askai cache dir).

    +

    List all cached files from the AskAI cache directory.

    + +
    Parameters
    + +
      +
    • cleanup: If True, clean up the listed cache files after displaying them (default is False).
    • +
    • args: Specific file names or indices to target for listing or cleanup.
    • +
    diff --git a/docs/api/askai/main/askai/core/commander/commands/camera_cmd.html b/docs/api/askai/main/askai/core/commander/commands/camera_cmd.html index 46283f4f..9b554a3f 100644 --- a/docs/api/askai/main/askai/core/commander/commands/camera_cmd.html +++ b/docs/api/askai/main/askai/core/commander/commands/camera_cmd.html @@ -3,7 +3,7 @@ - + main.askai.core.commander.commands.camera_cmd API documentation @@ -67,72 +67,44 @@

     1from abc import ABC
    - 2from textwrap import dedent
    - 3
    - 4import pause
    - 5from clitt.core.term.cursor import cursor
    - 6from hspylib.core.metaclass.classpath import AnyPath
    - 7from hspylib.core.tools.commons import sysout
    + 2from askai.core.askai_configs import configs
    + 3from askai.core.component.camera import camera
    + 4from askai.core.features.tools.webcam import webcam_capturer, webcam_identifier
    + 5from askai.core.support.text_formatter import text_formatter
    + 6from askai.core.support.utilities import display_text
    + 7from hspylib.core.metaclass.classpath import AnyPath
      8
    - 9from askai.core.askai_configs import configs
    -10from askai.core.component.camera import camera
    -11from askai.core.features.router.tools.terminal import open_command
    -12from askai.core.support.text_formatter import text_formatter
    -13from askai.core.support.utilities import display_text
    -14
    -15
    -16class CameraCmd(ABC):
    -17    """Provides camera command functionalities."""
    -18
    -19    @staticmethod
    -20    def capture(filename: AnyPath = None, detect_faces: bool = True, countdown: int = 3) -> None:
    -21        """Take a photo using hte Webcam.
    -22        :param filename: The filename of the photo taken.
    -23        :param detect_faces: Whether to detect faces on the photo or not.
    -24        :param countdown: A countdown to be displayed before the photo is taken.
    -25        """
    -26
    -27        if photo := camera.capture(filename, countdown):
    -28            text_formatter.cmd_print(f"Photo taken: %GREEN%{photo[0]}%NC%")
    -29            if detect_faces:
    -30                if len(faces := camera.detect_faces(photo[1], filename)) > 0:
    -31                    text_formatter.cmd_print(f"Faces detected: %GREEN%{len(faces[0])}%NC%")
    -32        else:
    -33            text_formatter.cmd_print("%RED%Unable to take photo!%NC%")
    -34
    -35    @staticmethod
    -36    def identify(max_distance: int = configs.max_id_distance) -> None:
    -37        """Identify the person in from of the WebCam.
    -38        :param max_distance: The maximum distance of the face (the lower, the closer to the real face).
    -39        """
    -40        sysout("Look at the camera...", end='')
    -41        pause.seconds(2)
    -42        display_text("GO  ")
    -43        pause.seconds(1)
    -44        cursor.erase_line()
    -45        cursor.erase_line()
    -46        identity: str | None = None
    -47        if photo := camera.identify(max_distance):
    -48            identity = dedent(
    -49                f"""
    -50                >   Person identified:
    -51
    -52                - **Name:** `{photo.name}`
    -53                - **Distance:** `{round(photo.distance, 4):.4f}/{round(max_distance, 4):.4f}`
    -54                - **URI:** `{photo.uri}`
    -55                """)
    -56            open_command(photo.uri)
    -57        cursor.erase_line()
    -58        text_formatter.cmd_print(identity or "%ORANGE%  No identification was possible!%NC%")
    -59
    -60    @staticmethod
    -61    def import_images(pathname: AnyPath = None, detect_faces: bool = True) -> None:
    -62        """Import image files into the image collection.
    -63        :param pathname: The pathname or glob to be imported.
    -64        :param detect_faces: Whether to detect faces on the images or not.
    -65        """
    -66        imports: tuple[int, ...] = camera.import_images(str(pathname), detect_faces)
    -67        text_formatter.cmd_print(f"`Imports:` {imports[0]}  `Faces:` {imports[1]}")
    + 9
    +10class CameraCmd(ABC):
    +11    """Provides camera command functionalities."""
    +12
    +13    @staticmethod
    +14    def capture(filename: AnyPath = None, detect_faces: bool = True) -> None:
    +15        """Take a photo using the webcam.
    +16        :param filename: The filename to save the photo under (optional).
    +17        :param detect_faces: Whether to detect faces in the photo (default is True).
    +18        """
    +19        if photo_description := webcam_capturer(filename, detect_faces):
    +20            display_text(photo_description)
    +21        else:
    +22            text_formatter.cmd_print("%RED%Unable to take photo!%NC%")
    +23
    +24    @staticmethod
    +25    def identify(max_distance: int = configs.max_id_distance) -> None:
    +26        """Identify the person in front of the webcam.
    +27        :param max_distance: The maximum allowable distance for face recognition. A lower value means closer matching
    +28                             to the real face (default is configs.max_id_distance).
    +29        """
    +30        display_text(webcam_identifier(max_distance))
    +31
    +32    @staticmethod
    +33    def import_images(pathname: AnyPath = None, detect_faces: bool = True) -> None:
    +34        """Import image files into the image collection.
    +35        :param pathname: The pathname or glob pattern specifying the images to be imported (optional).
    +36        :param detect_faces: Whether to detect faces in the imported images (default is True).
    +37        """
    +38        imports: tuple[int, ...] = camera.import_images(str(pathname), detect_faces)
    +39        text_formatter.cmd_print(f"`Imports:` {imports[0]}  `Faces:` {imports[1]}")
     
    @@ -148,58 +120,36 @@

    -
    17class CameraCmd(ABC):
    -18    """Provides camera command functionalities."""
    -19
    -20    @staticmethod
    -21    def capture(filename: AnyPath = None, detect_faces: bool = True, countdown: int = 3) -> None:
    -22        """Take a photo using hte Webcam.
    -23        :param filename: The filename of the photo taken.
    -24        :param detect_faces: Whether to detect faces on the photo or not.
    -25        :param countdown: A countdown to be displayed before the photo is taken.
    -26        """
    -27
    -28        if photo := camera.capture(filename, countdown):
    -29            text_formatter.cmd_print(f"Photo taken: %GREEN%{photo[0]}%NC%")
    -30            if detect_faces:
    -31                if len(faces := camera.detect_faces(photo[1], filename)) > 0:
    -32                    text_formatter.cmd_print(f"Faces detected: %GREEN%{len(faces[0])}%NC%")
    -33        else:
    -34            text_formatter.cmd_print("%RED%Unable to take photo!%NC%")
    -35
    -36    @staticmethod
    -37    def identify(max_distance: int = configs.max_id_distance) -> None:
    -38        """Identify the person in from of the WebCam.
    -39        :param max_distance: The maximum distance of the face (the lower, the closer to the real face).
    -40        """
    -41        sysout("Look at the camera...", end='')
    -42        pause.seconds(2)
    -43        display_text("GO  ")
    -44        pause.seconds(1)
    -45        cursor.erase_line()
    -46        cursor.erase_line()
    -47        identity: str | None = None
    -48        if photo := camera.identify(max_distance):
    -49            identity = dedent(
    -50                f"""
    -51                >   Person identified:
    -52
    -53                - **Name:** `{photo.name}`
    -54                - **Distance:** `{round(photo.distance, 4):.4f}/{round(max_distance, 4):.4f}`
    -55                - **URI:** `{photo.uri}`
    -56                """)
    -57            open_command(photo.uri)
    -58        cursor.erase_line()
    -59        text_formatter.cmd_print(identity or "%ORANGE%  No identification was possible!%NC%")
    -60
    -61    @staticmethod
    -62    def import_images(pathname: AnyPath = None, detect_faces: bool = True) -> None:
    -63        """Import image files into the image collection.
    -64        :param pathname: The pathname or glob to be imported.
    -65        :param detect_faces: Whether to detect faces on the images or not.
    -66        """
    -67        imports: tuple[int, ...] = camera.import_images(str(pathname), detect_faces)
    -68        text_formatter.cmd_print(f"`Imports:` {imports[0]}  `Faces:` {imports[1]}")
    +            
    11class CameraCmd(ABC):
    +12    """Provides camera command functionalities."""
    +13
    +14    @staticmethod
    +15    def capture(filename: AnyPath = None, detect_faces: bool = True) -> None:
    +16        """Take a photo using the webcam.
    +17        :param filename: The filename to save the photo under (optional).
    +18        :param detect_faces: Whether to detect faces in the photo (default is True).
    +19        """
    +20        if photo_description := webcam_capturer(filename, detect_faces):
    +21            display_text(photo_description)
    +22        else:
    +23            text_formatter.cmd_print("%RED%Unable to take photo!%NC%")
    +24
    +25    @staticmethod
    +26    def identify(max_distance: int = configs.max_id_distance) -> None:
    +27        """Identify the person in front of the webcam.
    +28        :param max_distance: The maximum allowable distance for face recognition. A lower value means closer matching
    +29                             to the real face (default is configs.max_id_distance).
    +30        """
    +31        display_text(webcam_identifier(max_distance))
    +32
    +33    @staticmethod
    +34    def import_images(pathname: AnyPath = None, detect_faces: bool = True) -> None:
    +35        """Import image files into the image collection.
    +36        :param pathname: The pathname or glob pattern specifying the images to be imported (optional).
    +37        :param detect_faces: Whether to detect faces in the imported images (default is True).
    +38        """
    +39        imports: tuple[int, ...] = camera.import_images(str(pathname), detect_faces)
    +40        text_formatter.cmd_print(f"`Imports:` {imports[0]}  `Faces:` {imports[1]}")
     
    @@ -213,38 +163,32 @@

    @staticmethod
    def - capture( filename: Union[pathlib.Path, str, NoneType] = None, detect_faces: bool = True, countdown: int = 3) -> None: + capture( filename: Union[pathlib.Path, str, NoneType] = None, detect_faces: bool = True) -> None:

    -
    20    @staticmethod
    -21    def capture(filename: AnyPath = None, detect_faces: bool = True, countdown: int = 3) -> None:
    -22        """Take a photo using hte Webcam.
    -23        :param filename: The filename of the photo taken.
    -24        :param detect_faces: Whether to detect faces on the photo or not.
    -25        :param countdown: A countdown to be displayed before the photo is taken.
    -26        """
    -27
    -28        if photo := camera.capture(filename, countdown):
    -29            text_formatter.cmd_print(f"Photo taken: %GREEN%{photo[0]}%NC%")
    -30            if detect_faces:
    -31                if len(faces := camera.detect_faces(photo[1], filename)) > 0:
    -32                    text_formatter.cmd_print(f"Faces detected: %GREEN%{len(faces[0])}%NC%")
    -33        else:
    -34            text_formatter.cmd_print("%RED%Unable to take photo!%NC%")
    +            
    14    @staticmethod
    +15    def capture(filename: AnyPath = None, detect_faces: bool = True) -> None:
    +16        """Take a photo using the webcam.
    +17        :param filename: The filename to save the photo under (optional).
    +18        :param detect_faces: Whether to detect faces in the photo (default is True).
    +19        """
    +20        if photo_description := webcam_capturer(filename, detect_faces):
    +21            display_text(photo_description)
    +22        else:
    +23            text_formatter.cmd_print("%RED%Unable to take photo!%NC%")
     
    -

    Take a photo using hte Webcam.

    +

    Take a photo using the webcam.

    Parameters
      -
    • filename: The filename of the photo taken.
    • -
    • detect_faces: Whether to detect faces on the photo or not.
    • -
    • countdown: A countdown to be displayed before the photo is taken.
    • +
    • filename: The filename to save the photo under (optional).
    • +
    • detect_faces: Whether to detect faces in the photo (default is True).
    @@ -262,39 +206,23 @@
    Parameters
    -
    36    @staticmethod
    -37    def identify(max_distance: int = configs.max_id_distance) -> None:
    -38        """Identify the person in from of the WebCam.
    -39        :param max_distance: The maximum distance of the face (the lower, the closer to the real face).
    -40        """
    -41        sysout("Look at the camera...", end='')
    -42        pause.seconds(2)
    -43        display_text("GO  ")
    -44        pause.seconds(1)
    -45        cursor.erase_line()
    -46        cursor.erase_line()
    -47        identity: str | None = None
    -48        if photo := camera.identify(max_distance):
    -49            identity = dedent(
    -50                f"""
    -51                >   Person identified:
    -52
    -53                - **Name:** `{photo.name}`
    -54                - **Distance:** `{round(photo.distance, 4):.4f}/{round(max_distance, 4):.4f}`
    -55                - **URI:** `{photo.uri}`
    -56                """)
    -57            open_command(photo.uri)
    -58        cursor.erase_line()
    -59        text_formatter.cmd_print(identity or "%ORANGE%  No identification was possible!%NC%")
    +            
    25    @staticmethod
    +26    def identify(max_distance: int = configs.max_id_distance) -> None:
    +27        """Identify the person in front of the webcam.
    +28        :param max_distance: The maximum allowable distance for face recognition. A lower value means closer matching
    +29                             to the real face (default is configs.max_id_distance).
    +30        """
    +31        display_text(webcam_identifier(max_distance))
     
    -

    Identify the person in from of the WebCam.

    +

    Identify the person in front of the webcam.

    Parameters
      -
    • max_distance: The maximum distance of the face (the lower, the closer to the real face).
    • +
    • max_distance: The maximum allowable distance for face recognition. A lower value means closer matching +to the real face (default is configs.max_id_distance).
    @@ -312,14 +240,14 @@
    Parameters
    -
    61    @staticmethod
    -62    def import_images(pathname: AnyPath = None, detect_faces: bool = True) -> None:
    -63        """Import image files into the image collection.
    -64        :param pathname: The pathname or glob to be imported.
    -65        :param detect_faces: Whether to detect faces on the images or not.
    -66        """
    -67        imports: tuple[int, ...] = camera.import_images(str(pathname), detect_faces)
    -68        text_formatter.cmd_print(f"`Imports:` {imports[0]}  `Faces:` {imports[1]}")
    +            
    33    @staticmethod
    +34    def import_images(pathname: AnyPath = None, detect_faces: bool = True) -> None:
    +35        """Import image files into the image collection.
    +36        :param pathname: The pathname or glob pattern specifying the images to be imported (optional).
    +37        :param detect_faces: Whether to detect faces in the imported images (default is True).
    +38        """
    +39        imports: tuple[int, ...] = camera.import_images(str(pathname), detect_faces)
    +40        text_formatter.cmd_print(f"`Imports:` {imports[0]}  `Faces:` {imports[1]}")
     
    @@ -328,8 +256,8 @@
    Parameters
    Parameters
      -
    • pathname: The pathname or glob to be imported.
    • -
    • detect_faces: Whether to detect faces on the images or not.
    • +
    • pathname: The pathname or glob pattern specifying the images to be imported (optional).
    • +
    • detect_faces: Whether to detect faces in the imported images (default is True).
    diff --git a/docs/api/askai/main/askai/core/commander/commands/general_cmd.html b/docs/api/askai/main/askai/core/commander/commands/general_cmd.html index 4cd5f91d..86516409 100644 --- a/docs/api/askai/main/askai/core/commander/commands/general_cmd.html +++ b/docs/api/askai/main/askai/core/commander/commands/general_cmd.html @@ -3,7 +3,7 @@ - + main.askai.core.commander.commands.general_cmd API documentation @@ -97,81 +97,80 @@

    13 Copyright (c) 2024, HomeSetup 14""" 15from abc import ABC -16 -17from askai.core.askai_messages import AskAiMessages -18from askai.core.askai_settings import settings -19from askai.core.component.summarizer import summarizer -20from askai.core.support.shared_instances import shared -21from askai.core.support.text_formatter import text_formatter -22from askai.core.support.utilities import display_text -23from askai.language.language import Language -24from clitt.core.term.terminal import Terminal -25from hspylib.core.config.path_object import PathObject -26from hspylib.modules.application.exit_status import ExitStatus -27 -28import locale -29import os.path +16from askai.core.askai_messages import AskAiMessages +17from askai.core.askai_settings import settings +18from askai.core.component.summarizer import summarizer +19from askai.core.support.shared_instances import shared +20from askai.core.support.text_formatter import text_formatter +21from askai.core.support.utilities import display_text +22from askai.language.language import Language +23from clitt.core.term.terminal import Terminal +24from hspylib.core.config.path_object import PathObject +25from hspylib.modules.application.exit_status import ExitStatus +26 +27import locale +28import os.path +29 30 -31 -32class GeneralCmd(ABC): -33 """Provides general command functionalities.""" -34 -35 @staticmethod -36 def execute(cmd_line: str | None = None) -> None: -37 """Execute a terminal command. -38 :param cmd_line The command line to execute. -39 """ -40 output, exit_code = Terminal.INSTANCE.shell_exec(cmd_line, shell=True) -41 if exit_code == ExitStatus.SUCCESS: -42 text_formatter.cmd_print(output) -43 else: -44 display_text(f"\n%RED%-=- Command `{cmd_line}` failed to execute: Code ({exit_code}) -=-%NC%") -45 -46 @staticmethod -47 def summarize(folder: str, glob: str) -> None: -48 """Generate a summarization of the folder contents. -49 :param folder: The base folder of the summarization. -50 :param glob: The glob pattern or file of the summarization. -51 """ -52 sum_dir: PathObject = PathObject.of(folder) -53 if os.path.exists(sum_dir.abs_dir): -54 if summarizer.generate(sum_dir.abs_dir, glob): -55 text_formatter.cmd_print(f"Summarization complete. Folder: *{folder}* Glob: *{glob}* !") -56 else: -57 display_text(f"\n%RED%-=- Failed to summarize. Folder: {folder} Glob: {glob} ! -=-%NC%") -58 else: -59 display_text(f"\n%RED%-=- Folder '{folder}' does not exist! -=-%NC%") -60 -61 @staticmethod -62 def idiom(locale_str: str) -> None: -63 """Set the application language. -64 :param locale_str: The locale string. -65 """ -66 try: -67 if locale_str and (language := Language.of_locale(locale_str)): -68 locale.setlocale(locale.LC_ALL, (language.idiom, language.encoding.val)) -69 settings.put("askai.preferred.language", language.idiom) -70 text_formatter.cmd_print(f"Locale changed to: {language}") -71 else: -72 language = Language.of_locale(locale.getlocale(locale.LC_ALL)) -73 text_formatter.cmd_print(f"Current locale: {language}") -74 except (ValueError, TypeError) as err: -75 display_text(f"\n%RED%-=- Failed to set idiom: '{str(err)}'! -=-%NC%") -76 -77 @staticmethod -78 def app_info() -> None: -79 """Display some useful application information.""" -80 display_text(shared.app_info) -81 -82 @staticmethod -83 def translate(from_lang: Language, to_lang: Language, *texts: str) -> None: -84 """Translate a text from the source language to the target language. -85 :param from_lang: The source idiom. -86 :param to_lang: The target idiom. -87 :param texts: The texts to be translated. -88 """ -89 translator = AskAiMessages.get_translator(from_lang, to_lang) -90 list(map(display_text, map(translator.translate, texts))) +31class GeneralCmd(ABC): +32 """Provides general command functionalities.""" +33 +34 @staticmethod +35 def execute(cmd_line: str | None = None) -> None: +36 """Execute a terminal command. +37 :param cmd_line: The command line to execute (optional). +38 """ +39 output, err_out, exit_code = Terminal.INSTANCE.shell_exec(cmd_line, shell=True) +40 if exit_code == ExitStatus.SUCCESS: +41 text_formatter.cmd_print(output) +42 else: +43 display_text(f"\n%RED%Command `{cmd_line}` failed. Error({err_out}) Code ({exit_code})%NC%") +44 +45 @staticmethod +46 def summarize(folder: str, glob: str) -> None: +47 """Generate a summarization of files and folder contents. +48 :param folder: The base folder from which the summarization will be generated. +49 :param glob: The glob pattern specifying which files or folders to include in the summarization. +50 """ +51 sum_dir: PathObject = PathObject.of(folder) +52 if os.path.exists(sum_dir.abs_dir): +53 if summarizer.generate(sum_dir.abs_dir, glob): +54 text_formatter.cmd_print(f"Summarization complete. Folder: *{folder}* Glob: *{glob}* !") +55 else: +56 display_text(f"\n%RED%-=- Failed to summarize. Folder: {folder} Glob: {glob} ! -=-%NC%") +57 else: +58 display_text(f"\n%RED%-=- Folder '{folder}' does not exist! -=-%NC%") +59 +60 @staticmethod +61 def idiom(locale_str: str) -> None: +62 """Set the application language. +63 :param locale_str: The locale string. +64 """ +65 try: +66 if locale_str and (language := Language.of_locale(locale_str)): +67 locale.setlocale(locale.LC_ALL, (language.idiom, language.encoding.val)) +68 settings.put("askai.preferred.language", language.idiom) +69 text_formatter.cmd_print(f"Locale changed to: {language}") +70 else: +71 language = Language.of_locale(locale.getlocale(locale.LC_ALL)) +72 text_formatter.cmd_print(f"Current locale: {language}") +73 except (ValueError, TypeError) as err: +74 display_text(f"\n%RED%-=- Failed to set idiom: '{str(err)}'! -=-%NC%") +75 +76 @staticmethod +77 def app_info() -> None: +78 """Display some useful application information.""" +79 display_text(shared.app_info, markdown=False) +80 +81 @staticmethod +82 def translate(from_lang: Language, to_lang: Language, *texts: str) -> None: +83 """Translate text from the source language to the target language. +84 :param from_lang: The source language. +85 :param to_lang: The target language. +86 :param texts: The texts to be translated. +87 """ +88 translator = AskAiMessages.get_translator(from_lang, to_lang) +89 list(map(display_text, map(translator.translate, texts)))

    @@ -187,65 +186,65 @@

    -
    33class GeneralCmd(ABC):
    -34    """Provides general command functionalities."""
    -35
    -36    @staticmethod
    -37    def execute(cmd_line: str | None = None) -> None:
    -38        """Execute a terminal command.
    -39        :param cmd_line The command line to execute.
    -40        """
    -41        output, exit_code = Terminal.INSTANCE.shell_exec(cmd_line, shell=True)
    -42        if exit_code == ExitStatus.SUCCESS:
    -43            text_formatter.cmd_print(output)
    -44        else:
    -45            display_text(f"\n%RED%-=- Command `{cmd_line}` failed to execute: Code ({exit_code}) -=-%NC%")
    -46
    -47    @staticmethod
    -48    def summarize(folder: str, glob: str) -> None:
    -49        """Generate a summarization of the folder contents.
    -50        :param folder: The base folder of the summarization.
    -51        :param glob: The glob pattern or file of the summarization.
    -52        """
    -53        sum_dir: PathObject = PathObject.of(folder)
    -54        if os.path.exists(sum_dir.abs_dir):
    -55            if summarizer.generate(sum_dir.abs_dir, glob):
    -56                text_formatter.cmd_print(f"Summarization complete. Folder: *{folder}*  Glob: *{glob}* !")
    -57            else:
    -58                display_text(f"\n%RED%-=- Failed to summarize. Folder: {folder}  Glob: {glob} ! -=-%NC%")
    -59        else:
    -60            display_text(f"\n%RED%-=- Folder '{folder}' does not exist! -=-%NC%")
    -61
    -62    @staticmethod
    -63    def idiom(locale_str: str) -> None:
    -64        """Set the application language.
    -65        :param locale_str: The locale string.
    -66        """
    -67        try:
    -68            if locale_str and (language := Language.of_locale(locale_str)):
    -69                locale.setlocale(locale.LC_ALL, (language.idiom, language.encoding.val))
    -70                settings.put("askai.preferred.language", language.idiom)
    -71                text_formatter.cmd_print(f"Locale changed to: {language}")
    -72            else:
    -73                language = Language.of_locale(locale.getlocale(locale.LC_ALL))
    -74                text_formatter.cmd_print(f"Current locale: {language}")
    -75        except (ValueError, TypeError) as err:
    -76            display_text(f"\n%RED%-=- Failed to set idiom: '{str(err)}'! -=-%NC%")
    -77
    -78    @staticmethod
    -79    def app_info() -> None:
    -80        """Display some useful application information."""
    -81        display_text(shared.app_info)
    -82
    -83    @staticmethod
    -84    def translate(from_lang: Language, to_lang: Language, *texts: str) -> None:
    -85        """Translate a text from the source language to the target language.
    -86        :param from_lang: The source idiom.
    -87        :param to_lang: The target idiom.
    -88        :param texts: The texts to be translated.
    -89        """
    -90        translator = AskAiMessages.get_translator(from_lang, to_lang)
    -91        list(map(display_text, map(translator.translate, texts)))
    +            
    32class GeneralCmd(ABC):
    +33    """Provides general command functionalities."""
    +34
    +35    @staticmethod
    +36    def execute(cmd_line: str | None = None) -> None:
    +37        """Execute a terminal command.
    +38        :param cmd_line: The command line to execute (optional).
    +39        """
    +40        output, err_out, exit_code = Terminal.INSTANCE.shell_exec(cmd_line, shell=True)
    +41        if exit_code == ExitStatus.SUCCESS:
    +42            text_formatter.cmd_print(output)
    +43        else:
    +44            display_text(f"\n%RED%Command `{cmd_line}` failed. Error({err_out})  Code ({exit_code})%NC%")
    +45
    +46    @staticmethod
    +47    def summarize(folder: str, glob: str) -> None:
    +48        """Generate a summarization of files and folder contents.
    +49        :param folder: The base folder from which the summarization will be generated.
    +50        :param glob: The glob pattern specifying which files or folders to include in the summarization.
    +51        """
    +52        sum_dir: PathObject = PathObject.of(folder)
    +53        if os.path.exists(sum_dir.abs_dir):
    +54            if summarizer.generate(sum_dir.abs_dir, glob):
    +55                text_formatter.cmd_print(f"Summarization complete. Folder: *{folder}*  Glob: *{glob}* !")
    +56            else:
    +57                display_text(f"\n%RED%-=- Failed to summarize. Folder: {folder}  Glob: {glob} ! -=-%NC%")
    +58        else:
    +59            display_text(f"\n%RED%-=- Folder '{folder}' does not exist! -=-%NC%")
    +60
    +61    @staticmethod
    +62    def idiom(locale_str: str) -> None:
    +63        """Set the application language.
    +64        :param locale_str: The locale string.
    +65        """
    +66        try:
    +67            if locale_str and (language := Language.of_locale(locale_str)):
    +68                locale.setlocale(locale.LC_ALL, (language.idiom, language.encoding.val))
    +69                settings.put("askai.preferred.language", language.idiom)
    +70                text_formatter.cmd_print(f"Locale changed to: {language}")
    +71            else:
    +72                language = Language.of_locale(locale.getlocale(locale.LC_ALL))
    +73                text_formatter.cmd_print(f"Current locale: {language}")
    +74        except (ValueError, TypeError) as err:
    +75            display_text(f"\n%RED%-=- Failed to set idiom: '{str(err)}'! -=-%NC%")
    +76
    +77    @staticmethod
    +78    def app_info() -> None:
    +79        """Display some useful application information."""
    +80        display_text(shared.app_info, markdown=False)
    +81
    +82    @staticmethod
    +83    def translate(from_lang: Language, to_lang: Language, *texts: str) -> None:
    +84        """Translate text from the source language to the target language.
    +85        :param from_lang: The source language.
    +86        :param to_lang: The target language.
    +87        :param texts: The texts to be translated.
    +88        """
    +89        translator = AskAiMessages.get_translator(from_lang, to_lang)
    +90        list(map(display_text, map(translator.translate, texts)))
     
    @@ -265,21 +264,26 @@

    -
    36    @staticmethod
    -37    def execute(cmd_line: str | None = None) -> None:
    -38        """Execute a terminal command.
    -39        :param cmd_line The command line to execute.
    -40        """
    -41        output, exit_code = Terminal.INSTANCE.shell_exec(cmd_line, shell=True)
    -42        if exit_code == ExitStatus.SUCCESS:
    -43            text_formatter.cmd_print(output)
    -44        else:
    -45            display_text(f"\n%RED%-=- Command `{cmd_line}` failed to execute: Code ({exit_code}) -=-%NC%")
    +            
    35    @staticmethod
    +36    def execute(cmd_line: str | None = None) -> None:
    +37        """Execute a terminal command.
    +38        :param cmd_line: The command line to execute (optional).
    +39        """
    +40        output, err_out, exit_code = Terminal.INSTANCE.shell_exec(cmd_line, shell=True)
    +41        if exit_code == ExitStatus.SUCCESS:
    +42            text_formatter.cmd_print(output)
    +43        else:
    +44            display_text(f"\n%RED%Command `{cmd_line}` failed. Error({err_out})  Code ({exit_code})%NC%")
     
    -

    Execute a terminal command. -:param cmd_line The command line to execute.

    +

    Execute a terminal command.

    + +
    Parameters
    + +
      +
    • cmd_line: The command line to execute (optional).
    • +
    @@ -296,30 +300,30 @@

    -
    47    @staticmethod
    -48    def summarize(folder: str, glob: str) -> None:
    -49        """Generate a summarization of the folder contents.
    -50        :param folder: The base folder of the summarization.
    -51        :param glob: The glob pattern or file of the summarization.
    -52        """
    -53        sum_dir: PathObject = PathObject.of(folder)
    -54        if os.path.exists(sum_dir.abs_dir):
    -55            if summarizer.generate(sum_dir.abs_dir, glob):
    -56                text_formatter.cmd_print(f"Summarization complete. Folder: *{folder}*  Glob: *{glob}* !")
    -57            else:
    -58                display_text(f"\n%RED%-=- Failed to summarize. Folder: {folder}  Glob: {glob} ! -=-%NC%")
    -59        else:
    -60            display_text(f"\n%RED%-=- Folder '{folder}' does not exist! -=-%NC%")
    +            
    46    @staticmethod
    +47    def summarize(folder: str, glob: str) -> None:
    +48        """Generate a summarization of files and folder contents.
    +49        :param folder: The base folder from which the summarization will be generated.
    +50        :param glob: The glob pattern specifying which files or folders to include in the summarization.
    +51        """
    +52        sum_dir: PathObject = PathObject.of(folder)
    +53        if os.path.exists(sum_dir.abs_dir):
    +54            if summarizer.generate(sum_dir.abs_dir, glob):
    +55                text_formatter.cmd_print(f"Summarization complete. Folder: *{folder}*  Glob: *{glob}* !")
    +56            else:
    +57                display_text(f"\n%RED%-=- Failed to summarize. Folder: {folder}  Glob: {glob} ! -=-%NC%")
    +58        else:
    +59            display_text(f"\n%RED%-=- Folder '{folder}' does not exist! -=-%NC%")
     
    -

    Generate a summarization of the folder contents.

    +

    Generate a summarization of files and folder contents.

    Parameters
      -
    • folder: The base folder of the summarization.
    • -
    • glob: The glob pattern or file of the summarization.
    • +
    • folder: The base folder from which the summarization will be generated.
    • +
    • glob: The glob pattern specifying which files or folders to include in the summarization.
    @@ -337,21 +341,21 @@
    Parameters
    -
    62    @staticmethod
    -63    def idiom(locale_str: str) -> None:
    -64        """Set the application language.
    -65        :param locale_str: The locale string.
    -66        """
    -67        try:
    -68            if locale_str and (language := Language.of_locale(locale_str)):
    -69                locale.setlocale(locale.LC_ALL, (language.idiom, language.encoding.val))
    -70                settings.put("askai.preferred.language", language.idiom)
    -71                text_formatter.cmd_print(f"Locale changed to: {language}")
    -72            else:
    -73                language = Language.of_locale(locale.getlocale(locale.LC_ALL))
    -74                text_formatter.cmd_print(f"Current locale: {language}")
    -75        except (ValueError, TypeError) as err:
    -76            display_text(f"\n%RED%-=- Failed to set idiom: '{str(err)}'! -=-%NC%")
    +            
    61    @staticmethod
    +62    def idiom(locale_str: str) -> None:
    +63        """Set the application language.
    +64        :param locale_str: The locale string.
    +65        """
    +66        try:
    +67            if locale_str and (language := Language.of_locale(locale_str)):
    +68                locale.setlocale(locale.LC_ALL, (language.idiom, language.encoding.val))
    +69                settings.put("askai.preferred.language", language.idiom)
    +70                text_formatter.cmd_print(f"Locale changed to: {language}")
    +71            else:
    +72                language = Language.of_locale(locale.getlocale(locale.LC_ALL))
    +73                text_formatter.cmd_print(f"Current locale: {language}")
    +74        except (ValueError, TypeError) as err:
    +75            display_text(f"\n%RED%-=- Failed to set idiom: '{str(err)}'! -=-%NC%")
     
    @@ -378,10 +382,10 @@
    Parameters
    -
    78    @staticmethod
    -79    def app_info() -> None:
    -80        """Display some useful application information."""
    -81        display_text(shared.app_info)
    +            
    77    @staticmethod
    +78    def app_info() -> None:
    +79        """Display some useful application information."""
    +80        display_text(shared.app_info, markdown=False)
     
    @@ -402,25 +406,25 @@
    Parameters
    -
    83    @staticmethod
    -84    def translate(from_lang: Language, to_lang: Language, *texts: str) -> None:
    -85        """Translate a text from the source language to the target language.
    -86        :param from_lang: The source idiom.
    -87        :param to_lang: The target idiom.
    -88        :param texts: The texts to be translated.
    -89        """
    -90        translator = AskAiMessages.get_translator(from_lang, to_lang)
    -91        list(map(display_text, map(translator.translate, texts)))
    +            
    82    @staticmethod
    +83    def translate(from_lang: Language, to_lang: Language, *texts: str) -> None:
    +84        """Translate text from the source language to the target language.
    +85        :param from_lang: The source language.
    +86        :param to_lang: The target language.
    +87        :param texts: The texts to be translated.
    +88        """
    +89        translator = AskAiMessages.get_translator(from_lang, to_lang)
    +90        list(map(display_text, map(translator.translate, texts)))
     
    -

    Translate a text from the source language to the target language.

    +

    Translate text from the source language to the target language.

    Parameters
      -
    • from_lang: The source idiom.
    • -
    • to_lang: The target idiom.
    • +
    • from_lang: The source language.
    • +
    • to_lang: The target language.
    • texts: The texts to be translated.
    diff --git a/docs/api/askai/main/askai/core/commander/commands/history_cmd.html b/docs/api/askai/main/askai/core/commander/commands/history_cmd.html index edb3b32c..ddbd32af 100644 --- a/docs/api/askai/main/askai/core/commander/commands/history_cmd.html +++ b/docs/api/askai/main/askai/core/commander/commands/history_cmd.html @@ -3,7 +3,7 @@ - + main.askai.core.commander.commands.history_cmd API documentation @@ -113,7 +113,7 @@

    29 30 @staticmethod 31 def context_list() -> None: -32 """List the chat context window entries.""" +32 """List the entries in the chat context window.""" 33 34 if (all_context := shared.context) and (length := len(all_context)) > 0: 35 display_text(f"### Listing ALL ({length}) Chat Contexts:\n\n---\n\n") @@ -121,7 +121,7 @@

    37 ctx, ctx_val = c[0], c[1] 38 display_text( 39 f"- {ctx} ({len(ctx_val)}/{all_context.max_context_size} " -40 f"tk [{all_context.context_length(ctx)}/{all_context.token_limit}]) \n" +40 f"tk [{all_context.length(ctx)}/{all_context.token_limit}]) \n" 41 + indent( 42 "\n".join( 43 [ @@ -139,7 +139,7 @@

    55 @staticmethod 56 def context_forget(context: str | None = None) -> None: 57 """Forget entries pushed to the chat context. -58 :param context: The context key; or none to forget all context. +58 :param context: The context key to forget, or None to forget all context entries. 59 """ 60 if context := context if context != "ALL" else None: 61 shared.context.clear(*(re.split(r"[;,|]", context.upper()))) @@ -149,13 +149,13 @@

    65 66 @staticmethod 67 def context_copy(name: str | None = None) -> None: -68 """Copy a context entry to the clipboard -69 :param name: The context name. +68 """Copy a context entry to the clipboard. +69 :param name: The name of the context entry to copy. If None, the default context will be copied. 70 """ 71 if (name := name.upper()) in shared.context.keys: -72 if (ctx := str(shared.context.flat(name.upper()))) \ -73 and (stripped_role := re.sub(r'^((system|human|assistant):\s*)', '', ctx, -74 flags=re.MULTILINE | re.IGNORECASE)): +72 if (ctx := str(shared.context.flat(name.upper()))) and ( +73 stripped_role := re.sub(r"^((system|human|assistant):\s*)", "", ctx, flags=re.MULTILINE | re.IGNORECASE) +74 ): 75 pyperclip.copy(stripped_role) 76 text_formatter.cmd_print(f"`{name}` copied to the clipboard!") 77 else: @@ -199,7 +199,7 @@

    30 31 @staticmethod 32 def context_list() -> None: -33 """List the chat context window entries.""" +33 """List the entries in the chat context window.""" 34 35 if (all_context := shared.context) and (length := len(all_context)) > 0: 36 display_text(f"### Listing ALL ({length}) Chat Contexts:\n\n---\n\n") @@ -207,7 +207,7 @@

    38 ctx, ctx_val = c[0], c[1] 39 display_text( 40 f"- {ctx} ({len(ctx_val)}/{all_context.max_context_size} " -41 f"tk [{all_context.context_length(ctx)}/{all_context.token_limit}]) \n" +41 f"tk [{all_context.length(ctx)}/{all_context.token_limit}]) \n" 42 + indent( 43 "\n".join( 44 [ @@ -225,7 +225,7 @@

    56 @staticmethod 57 def context_forget(context: str | None = None) -> None: 58 """Forget entries pushed to the chat context. -59 :param context: The context key; or none to forget all context. +59 :param context: The context key to forget, or None to forget all context entries. 60 """ 61 if context := context if context != "ALL" else None: 62 shared.context.clear(*(re.split(r"[;,|]", context.upper()))) @@ -235,13 +235,13 @@

    66 67 @staticmethod 68 def context_copy(name: str | None = None) -> None: -69 """Copy a context entry to the clipboard -70 :param name: The context name. +69 """Copy a context entry to the clipboard. +70 :param name: The name of the context entry to copy. If None, the default context will be copied. 71 """ 72 if (name := name.upper()) in shared.context.keys: -73 if (ctx := str(shared.context.flat(name.upper()))) \ -74 and (stripped_role := re.sub(r'^((system|human|assistant):\s*)', '', ctx, -75 flags=re.MULTILINE | re.IGNORECASE)): +73 if (ctx := str(shared.context.flat(name.upper()))) and ( +74 stripped_role := re.sub(r"^((system|human|assistant):\s*)", "", ctx, flags=re.MULTILINE | re.IGNORECASE) +75 ): 76 pyperclip.copy(stripped_role) 77 text_formatter.cmd_print(f"`{name}` copied to the clipboard!") 78 else: @@ -286,7 +286,7 @@

    31    @staticmethod
     32    def context_list() -> None:
    -33        """List the chat context window entries."""
    +33        """List the entries in the chat context window."""
     34
     35        if (all_context := shared.context) and (length := len(all_context)) > 0:
     36            display_text(f"### Listing ALL ({length}) Chat Contexts:\n\n---\n\n")
    @@ -294,7 +294,7 @@ 

    38 ctx, ctx_val = c[0], c[1] 39 display_text( 40 f"- {ctx} ({len(ctx_val)}/{all_context.max_context_size} " -41 f"tk [{all_context.context_length(ctx)}/{all_context.token_limit}]) \n" +41 f"tk [{all_context.length(ctx)}/{all_context.token_limit}]) \n" 42 + indent( 43 "\n".join( 44 [ @@ -311,7 +311,7 @@

    -

    List the chat context window entries.

    +

    List the entries in the chat context window.

    @@ -331,7 +331,7 @@

    56    @staticmethod
     57    def context_forget(context: str | None = None) -> None:
     58        """Forget entries pushed to the chat context.
    -59        :param context: The context key; or none to forget all context.
    +59        :param context: The context key to forget, or None to forget all context entries.
     60        """
     61        if context := context if context != "ALL" else None:
     62            shared.context.clear(*(re.split(r"[;,|]", context.upper())))
    @@ -346,7 +346,7 @@ 

    Parameters
      -
    • context: The context key; or none to forget all context.
    • +
    • context: The context key to forget, or None to forget all context entries.
    @@ -366,13 +366,13 @@

    Parameters
    67    @staticmethod
     68    def context_copy(name: str | None = None) -> None:
    -69        """Copy a context entry to the clipboard
    -70        :param name: The context name.
    +69        """Copy a context entry to the clipboard.
    +70        :param name: The name of the context entry to copy. If None, the default context will be copied.
     71        """
     72        if (name := name.upper()) in shared.context.keys:
    -73            if (ctx := str(shared.context.flat(name.upper()))) \
    -74                and (stripped_role := re.sub(r'^((system|human|assistant):\s*)', '', ctx,
    -75                                             flags=re.MULTILINE | re.IGNORECASE)):
    +73            if (ctx := str(shared.context.flat(name.upper()))) and (
    +74                stripped_role := re.sub(r"^((system|human|assistant):\s*)", "", ctx, flags=re.MULTILINE | re.IGNORECASE)
    +75            ):
     76                pyperclip.copy(stripped_role)
     77                text_formatter.cmd_print(f"`{name}` copied to the clipboard!")
     78            else:
    @@ -382,12 +382,12 @@ 
    Parameters
    -

    Copy a context entry to the clipboard

    +

    Copy a context entry to the clipboard.

    Parameters
      -
    • name: The context name.
    • +
    • name: The name of the context entry to copy. If None, the default context will be copied.
    diff --git a/docs/api/askai/main/askai/core/commander/commands/settings_cmd.html b/docs/api/askai/main/askai/core/commander/commands/settings_cmd.html index f58f0acb..56bbd7f2 100644 --- a/docs/api/askai/main/askai/core/commander/commands/settings_cmd.html +++ b/docs/api/askai/main/askai/core/commander/commands/settings_cmd.html @@ -3,7 +3,7 @@ - + main.askai.core.commander.commands.settings_cmd API documentation @@ -95,8 +95,8 @@

    14""" 15 16from abc import ABC -17from askai.core.askai_settings import settings -18from askai.core.component.recorder import recorder +17from askai.core.askai_configs import configs +18from askai.core.askai_settings import settings 19from askai.core.support.text_formatter import text_formatter 20from askai.core.support.utilities import display_text 21from hspylib.core.tools.commons import sysout @@ -118,31 +118,47 @@

    37 38 @staticmethod 39 def set(name: str, value: Any) -> None: -40 """Set a setting value.""" -41 all_settings = settings.settings.search() -42 if name.isdecimal() and 0 <= int(name) <= len(all_settings): -43 name = all_settings[int(name)].name -44 if settings[name]: -45 settings.put(name, value if value not in ["''", '""'] else None) -46 text_formatter.cmd_print(f"Setting `{name}` changed to %GREEN%{value}%NC%") -47 else: -48 text_formatter.cmd_print(f"%RED%Setting: '{name}' was not found!%NC%") -49 -50 @staticmethod -51 def get(key: str) -> Optional[SettingsEntry]: -52 """Get the setting specified by key.""" -53 if ss := settings[key]: -54 text_formatter.cmd_print(f"%WHITE%Name: %BLUE%{ss.name}\t%WHITE%Value: %GREEN%{ss.value}%NC%") -55 return ss -56 text_formatter.cmd_print(f"%RED%Setting: '{key}' was not found!%NC%") -57 return None -58 -59 @staticmethod -60 def reset() -> None: -61 """Reset settings to the defaults.""" -62 settings.defaults() -63 # Include the current audio input. -64 settings.put("askai.recorder.devices", recorder.input_device[1] or "") +40 """Set a setting value. +41 :param name: The name of the setting to update or create. +42 :param value: The value to assign to the setting. +43 """ +44 all_settings = settings.settings.search() +45 if name.isdecimal() and 0 <= int(name) <= len(all_settings): +46 name = all_settings[int(name)].name +47 if settings[name]: +48 settings.put(name, value if value not in ["''", '""'] else None) +49 text_formatter.cmd_print(f"Setting `{name}` changed to %GREEN%{value}%NC%") +50 else: +51 text_formatter.cmd_print(f"%RED%Setting: '{name}' was not found!%NC%") +52 +53 @staticmethod +54 def get(key: str) -> Optional[SettingsEntry]: +55 """Retrieve the setting specified by the key. +56 :param key: The key of the setting to retrieve. +57 :return: The corresponding SettingsEntry if found, otherwise None. +58 """ +59 if ss := settings[key]: +60 text_formatter.cmd_print(f"%WHITE%Name: %BLUE%{ss.name}\t%WHITE%Value: %GREEN%{ss.value}%NC%") +61 return ss +62 text_formatter.cmd_print(f"%RED%Setting: '{key}' was not found!%NC%") +63 return None +64 +65 @staticmethod +66 def reset() -> None: +67 """Reset all settings to their default values.""" +68 # Command arguments settings must be kept as it was. +69 is_interactive: bool = settings.get_bool("askai.interactive.enabled") +70 is_speak: bool = settings.get_bool("askai.speak.enabled") +71 is_debug: bool = settings.get_bool("askai.debug.enabled") +72 is_cache: bool = settings.get_bool("askai.cache.enabled") +73 settings.defaults() +74 configs.clear_devices() +75 # Put back the command argument settings. +76 settings.put("askai.interactive.enabled", is_interactive) +77 settings.put("askai.speak.enabled", is_speak) +78 settings.put("askai.debug.enabled", is_debug) +79 settings.put("askai.cache.enabled", is_cache) +80 text_formatter.cmd_print(f"%GREEN%Factory settings reset!%NC%")

    @@ -172,31 +188,47 @@

    38 39 @staticmethod 40 def set(name: str, value: Any) -> None: -41 """Set a setting value.""" -42 all_settings = settings.settings.search() -43 if name.isdecimal() and 0 <= int(name) <= len(all_settings): -44 name = all_settings[int(name)].name -45 if settings[name]: -46 settings.put(name, value if value not in ["''", '""'] else None) -47 text_formatter.cmd_print(f"Setting `{name}` changed to %GREEN%{value}%NC%") -48 else: -49 text_formatter.cmd_print(f"%RED%Setting: '{name}' was not found!%NC%") -50 -51 @staticmethod -52 def get(key: str) -> Optional[SettingsEntry]: -53 """Get the setting specified by key.""" -54 if ss := settings[key]: -55 text_formatter.cmd_print(f"%WHITE%Name: %BLUE%{ss.name}\t%WHITE%Value: %GREEN%{ss.value}%NC%") -56 return ss -57 text_formatter.cmd_print(f"%RED%Setting: '{key}' was not found!%NC%") -58 return None -59 -60 @staticmethod -61 def reset() -> None: -62 """Reset settings to the defaults.""" -63 settings.defaults() -64 # Include the current audio input. -65 settings.put("askai.recorder.devices", recorder.input_device[1] or "") +41 """Set a setting value. +42 :param name: The name of the setting to update or create. +43 :param value: The value to assign to the setting. +44 """ +45 all_settings = settings.settings.search() +46 if name.isdecimal() and 0 <= int(name) <= len(all_settings): +47 name = all_settings[int(name)].name +48 if settings[name]: +49 settings.put(name, value if value not in ["''", '""'] else None) +50 text_formatter.cmd_print(f"Setting `{name}` changed to %GREEN%{value}%NC%") +51 else: +52 text_formatter.cmd_print(f"%RED%Setting: '{name}' was not found!%NC%") +53 +54 @staticmethod +55 def get(key: str) -> Optional[SettingsEntry]: +56 """Retrieve the setting specified by the key. +57 :param key: The key of the setting to retrieve. +58 :return: The corresponding SettingsEntry if found, otherwise None. +59 """ +60 if ss := settings[key]: +61 text_formatter.cmd_print(f"%WHITE%Name: %BLUE%{ss.name}\t%WHITE%Value: %GREEN%{ss.value}%NC%") +62 return ss +63 text_formatter.cmd_print(f"%RED%Setting: '{key}' was not found!%NC%") +64 return None +65 +66 @staticmethod +67 def reset() -> None: +68 """Reset all settings to their default values.""" +69 # Command arguments settings must be kept as it was. +70 is_interactive: bool = settings.get_bool("askai.interactive.enabled") +71 is_speak: bool = settings.get_bool("askai.speak.enabled") +72 is_debug: bool = settings.get_bool("askai.debug.enabled") +73 is_cache: bool = settings.get_bool("askai.cache.enabled") +74 settings.defaults() +75 configs.clear_devices() +76 # Put back the command argument settings. +77 settings.put("askai.interactive.enabled", is_interactive) +78 settings.put("askai.speak.enabled", is_speak) +79 settings.put("askai.debug.enabled", is_debug) +80 settings.put("askai.cache.enabled", is_cache) +81 text_formatter.cmd_print(f"%GREEN%Factory settings reset!%NC%")

    @@ -246,19 +278,29 @@

    39    @staticmethod
     40    def set(name: str, value: Any) -> None:
    -41        """Set a setting value."""
    -42        all_settings = settings.settings.search()
    -43        if name.isdecimal() and 0 <= int(name) <= len(all_settings):
    -44            name = all_settings[int(name)].name
    -45        if settings[name]:
    -46            settings.put(name, value if value not in ["''", '""'] else None)
    -47            text_formatter.cmd_print(f"Setting `{name}` changed to %GREEN%{value}%NC%")
    -48        else:
    -49            text_formatter.cmd_print(f"%RED%Setting: '{name}' was not found!%NC%")
    +41        """Set a setting value.
    +42        :param name: The name of the setting to update or create.
    +43        :param value: The value to assign to the setting.
    +44        """
    +45        all_settings = settings.settings.search()
    +46        if name.isdecimal() and 0 <= int(name) <= len(all_settings):
    +47            name = all_settings[int(name)].name
    +48        if settings[name]:
    +49            settings.put(name, value if value not in ["''", '""'] else None)
    +50            text_formatter.cmd_print(f"Setting `{name}` changed to %GREEN%{value}%NC%")
    +51        else:
    +52            text_formatter.cmd_print(f"%RED%Setting: '{name}' was not found!%NC%")
     

    Set a setting value.

    + +
    Parameters
    + +
      +
    • name: The name of the setting to update or create.
    • +
    • value: The value to assign to the setting.
    • +
    @@ -275,18 +317,33 @@

    -
    51    @staticmethod
    -52    def get(key: str) -> Optional[SettingsEntry]:
    -53        """Get the setting specified by key."""
    -54        if ss := settings[key]:
    -55            text_formatter.cmd_print(f"%WHITE%Name: %BLUE%{ss.name}\t%WHITE%Value: %GREEN%{ss.value}%NC%")
    -56            return ss
    -57        text_formatter.cmd_print(f"%RED%Setting: '{key}' was not found!%NC%")
    -58        return None
    +            
    54    @staticmethod
    +55    def get(key: str) -> Optional[SettingsEntry]:
    +56        """Retrieve the setting specified by the key.
    +57        :param key: The key of the setting to retrieve.
    +58        :return: The corresponding SettingsEntry if found, otherwise None.
    +59        """
    +60        if ss := settings[key]:
    +61            text_formatter.cmd_print(f"%WHITE%Name: %BLUE%{ss.name}\t%WHITE%Value: %GREEN%{ss.value}%NC%")
    +62            return ss
    +63        text_formatter.cmd_print(f"%RED%Setting: '{key}' was not found!%NC%")
    +64        return None
     
    -

    Get the setting specified by key.

    +

    Retrieve the setting specified by the key.

    + +
    Parameters
    + +
      +
    • key: The key of the setting to retrieve.
    • +
    + +
    Returns
    + +
    +

    The corresponding SettingsEntry if found, otherwise None.

    +
    @@ -303,16 +360,26 @@

    -
    60    @staticmethod
    -61    def reset() -> None:
    -62        """Reset settings to the defaults."""
    -63        settings.defaults()
    -64        # Include the current audio input.
    -65        settings.put("askai.recorder.devices", recorder.input_device[1] or "")
    +            
    66    @staticmethod
    +67    def reset() -> None:
    +68        """Reset all settings to their default values."""
    +69        # Command arguments settings must be kept as it was.
    +70        is_interactive: bool = settings.get_bool("askai.interactive.enabled")
    +71        is_speak: bool = settings.get_bool("askai.speak.enabled")
    +72        is_debug: bool = settings.get_bool("askai.debug.enabled")
    +73        is_cache: bool = settings.get_bool("askai.cache.enabled")
    +74        settings.defaults()
    +75        configs.clear_devices()
    +76        # Put back the command argument settings.
    +77        settings.put("askai.interactive.enabled", is_interactive)
    +78        settings.put("askai.speak.enabled", is_speak)
    +79        settings.put("askai.debug.enabled", is_debug)
    +80        settings.put("askai.cache.enabled", is_cache)
    +81        text_formatter.cmd_print(f"%GREEN%Factory settings reset!%NC%")
     
    -

    Reset settings to the defaults.

    +

    Reset all settings to their default values.

    diff --git a/docs/api/askai/main/askai/core/commander/commands/tts_stt_cmd.html b/docs/api/askai/main/askai/core/commander/commands/tts_stt_cmd.html index da402a7c..4820b327 100644 --- a/docs/api/askai/main/askai/core/commander/commands/tts_stt_cmd.html +++ b/docs/api/askai/main/askai/core/commander/commands/tts_stt_cmd.html @@ -3,7 +3,7 @@ - + main.askai.core.commander.commands.tts_stt_cmd API documentation @@ -106,102 +106,128 @@

    16from askai.core.askai_configs import configs 17from askai.core.askai_settings import settings 18from askai.core.component.audio_player import player - 19from askai.core.component.recorder import recorder + 19from askai.core.component.recorder import InputDevice, recorder 20from askai.core.support.shared_instances import shared 21from askai.core.support.text_formatter import text_formatter 22from askai.core.support.utilities import copy_file - 23from pathlib import Path - 24 - 25import os - 26 - 27 - 28class TtsSttCmd(ABC): - 29 """Provides Speech-to-Text (STT) and Text-to-Speech (TTS) command functionalities.""" - 30 - 31 @staticmethod - 32 def voice_list() -> None: - 33 """List all available voices for the current engine/model.""" - 34 all_voices = shared.engine.voices() - 35 str_voices = "\n".join([f"{i}. {v.title()}" for i, v in enumerate(all_voices)]) - 36 text_formatter.cmd_print( - 37 f"Available `{shared.engine.configs().stt_model}` voices: \n" - 38 f"\n{str_voices}\n> Hint: Type: '/voices set \\<number|voice_name\\>' to select a voice. " - 39 f"To hear a sample use: '/voices play \\<number|voice_name\\>'" - 40 ) - 41 - 42 @staticmethod - 43 def voice_set(name: str | int | None = None) -> None: - 44 """Set the specified engine's voice.""" - 45 all_voices = shared.engine.voices() - 46 if name.isdecimal() and 0 <= int(name) <= len(all_voices): - 47 name = all_voices[int(name)] - 48 if name in all_voices: - 49 settings.put("openai.text.to.speech.voice", name) - 50 shared.engine.configs().tts_voice = name - 51 text_formatter.cmd_print(f"`Speech-To-Text` voice changed to %GREEN%{name.title()}%NC%") - 52 else: - 53 text_formatter.cmd_print(f"%RED%Invalid voice: '{name}'%NC%") - 54 - 55 @staticmethod - 56 def voice_play(name: str | int | None = None) -> None: - 57 """Play a sample using the specified voice.""" - 58 all_voices = shared.engine.voices() - 59 ai_name = shared.engine.ai_name().lower().replace("engine", "") - 60 if name.isdecimal() and 0 <= int(name) <= len(all_voices): - 61 name = all_voices[int(name)] - 62 if name in all_voices: - 63 text_formatter.cmd_print(f"Sampling voice `{name}` …") - 64 player.play_sfx(f"voices/{ai_name}-{name}-sample") - 65 else: - 66 text_formatter.cmd_print(f"%RED%Invalid voice: '{name}'%NC%") - 67 - 68 @staticmethod - 69 def tempo(speed: int | None = None) -> None: - 70 """Set the playing speed of the speech.""" - 71 if not speed: - 72 settings.get("askai.text.to.speech.tempo") - 73 elif 1 <= speed <= 3: - 74 settings.put("askai.text.to.speech.tempo", speed) - 75 configs.tempo = speed - 76 tempo_str: str = "Normal" if speed == 1 else ("Fast" if speed == 2 else "Ultra") - 77 text_formatter.cmd_print(f"`Speech-To-Text` **tempo** changed to %GREEN%{tempo_str} ({speed})%NC%") - 78 else: - 79 text_formatter.cmd_print(f"%RED%Invalid tempo value: '{speed}'. Please choose between [1..3].%NC%") - 80 - 81 @staticmethod - 82 def device_list() -> None: - 83 """List the available audio input devices.""" - 84 all_devices = recorder.devices - 85 str_devices = "\n".join([f"{d[0]}. {d[1]}" for d in all_devices]) - 86 text_formatter.cmd_print( - 87 f"Current audio input device: {recorder.input_device} \n" - 88 f"\nAvailable audio input devices: \n" - 89 f"\n{str_devices}\n> Hint: Type: '/devices set \\<number\\>' to select a device." - 90 ) - 91 - 92 @staticmethod - 93 def device_set(name: str | int | None = None) -> None: - 94 """Set the current audio input device.""" - 95 all_devices = recorder.devices - 96 if name.isdecimal() and 0 <= int(name) <= len(all_devices): - 97 name = all_devices[int(name)][1] - 98 if device := next((dev for dev in all_devices if dev[1] == name), None): - 99 if recorder.set_device(device): -100 text_formatter.cmd_print(f"`Text-To-Speech` device changed to %GREEN%{device[1]}%NC%") -101 else: -102 text_formatter.cmd_print(f"%RED%Device: '{name}' failed to initialize!%NC%") -103 else: -104 text_formatter.cmd_print(f"%RED%Invalid audio input device: '{name}'%NC%") -105 -106 @staticmethod -107 def tts(text: str, dest_dir: str = os.getcwd(), playback: bool = True) -> None: -108 """Convert a text to speech.""" -109 if (audio_path := shared.engine.text_to_speech(text, stream=False, playback=playback)) and audio_path.exists(): -110 if dest_dir and ((dest_path := Path(dest_dir)) and dest_path.exists()): -111 audio_path = copy_file(audio_path, dest_dir) -112 text_formatter.cmd_print(f"File %GREEN%'{audio_path}' was successfully saved!%NC%") -113 else: -114 text_formatter.cmd_print(f"%RED%Unable to convert text to file !%NC%") + 23from clitt.core.tui.mselect.mselect import mselect + 24from pathlib import Path + 25 + 26import os + 27import pause + 28 + 29 + 30class TtsSttCmd(ABC): + 31 """Provides Speech-to-Text (STT) and Text-to-Speech (TTS) command functionalities.""" + 32 + 33 @staticmethod + 34 def voice_list() -> None: + 35 """List all available voices for the current engine/model.""" + 36 all_voices = shared.engine.voices() + 37 str_voices = "\n".join([f"{i}. {v.title()}" for i, v in enumerate(all_voices)]) + 38 text_formatter.cmd_print( + 39 f"Available `{shared.engine.configs().stt_model}` voices: \n" + 40 f"\n{str_voices}\n> Hint: Type: '/voices set \\<number|voice_name\\>' to select a voice. " + 41 f"To hear a sample use: '/voices play \\<number|voice_name\\>'" + 42 ) + 43 + 44 @staticmethod + 45 def voice_set(name_or_index: str | int | None = None) -> None: + 46 """Set the specified voice for the current engine. + 47 :param name_or_index: The name or index of the voice to set. If None, the default voice will be used. + 48 """ + 49 all_voices = shared.engine.voices() + 50 if name_or_index.isdecimal() and 0 <= int(name_or_index) <= len(all_voices): + 51 name_or_index = all_voices[int(name_or_index)] + 52 if name_or_index in all_voices: + 53 settings.put("openai.text.to.speech.voice", name_or_index) + 54 shared.engine.configs().tts_voice = name_or_index + 55 text_formatter.cmd_print(f"`Speech-To-Text` voice changed to %GREEN%{name_or_index.title()}%NC%") + 56 else: + 57 text_formatter.cmd_print(f"%RED%Invalid voice: '{name_or_index}'%NC%") + 58 + 59 @staticmethod + 60 def voice_play(name_or_index: str | int | None = None) -> None: + 61 """Play a sample using the specified voice. + 62 :param name_or_index: The name or index of the voice to use for the sample. If None, the default voice will be used. + 63 """ + 64 all_voices = shared.engine.voices() + 65 ai_name = shared.engine.ai_name().lower().replace("engine", "") + 66 if name_or_index.isdecimal() and 0 <= int(name_or_index) <= len(all_voices): + 67 name_or_index = all_voices[int(name_or_index)] + 68 if name_or_index in all_voices: + 69 text_formatter.cmd_print(f"Sampling voice `{name_or_index}` …") + 70 player.play_sfx(f"voices/{ai_name}-{name_or_index}-sample") + 71 else: + 72 text_formatter.cmd_print(f"%RED%Invalid voice: '{name_or_index}'%NC%") + 73 + 74 @staticmethod + 75 def tempo(speed: int | None = None) -> None: + 76 """Set the playing speed of the speech. + 77 :param speed: The speed to set for speech playback. If None, the default speed will be used. + 78 """ + 79 if not speed: + 80 settings.get("askai.text.to.speech.tempo") + 81 elif 1 <= speed <= 3: + 82 settings.put("askai.text.to.speech.tempo", speed) + 83 configs.tempo = speed + 84 tempo_str: str = "Normal" if speed == 1 else ("Fast" if speed == 2 else "Ultra") + 85 text_formatter.cmd_print(f"`Speech-To-Text` **tempo** changed to %GREEN%{tempo_str} ({speed})%NC%") + 86 else: + 87 text_formatter.cmd_print(f"%RED%Invalid tempo value: '{speed}'. Please choose between [1..3].%NC%") + 88 + 89 @staticmethod + 90 def device_list() -> None: + 91 """List the available audio input devices.""" + 92 all_devices = recorder.devices + 93 str_devices = "\n".join([f"{d[0]}. {d[1]}" for d in all_devices]) + 94 text_formatter.cmd_print( + 95 f"Current audio input device: {recorder.input_device} \n" + 96 f"\nAvailable audio input devices: \n" + 97 f"\n{str_devices}\n> Hint: Type: '/devices set \\<number\\>' to select a device." + 98 ) + 99 +100 @staticmethod +101 def device_set(name_or_index: str | int | None = None) -> None: +102 """Set the current audio input device. +103 :param name_or_index: The name or index of the audio input device to set. If None, the default device will be +104 used. +105 """ +106 device: InputDevice | None = None +107 all_devices = recorder.devices +108 +109 def _set_device(_device) -> bool: +110 if recorder.set_device(_device): +111 text_formatter.cmd_print(f"`Text-To-Speech` device changed to %GREEN%{_device}%NC%") +112 return True +113 text_formatter.cmd_print(f"%HOM%%ED2%Error: '{_device}' is not an Audio Input device!%NC%") +114 all_devices.remove(_device) +115 pause.seconds(2) +116 return False +117 +118 if not name_or_index: +119 device: InputDevice = mselect( +120 all_devices, f"{'-=' * 40}%EOL%AskAI::Select the Audio Input device%EOL%{'=-' * 40}%EOL%" +121 ) +122 elif name_or_index.isdecimal() and 0 <= int(name_or_index) <= len(all_devices): +123 name_or_index = all_devices[int(name_or_index)][1] +124 device = next((dev for dev in all_devices if dev[1] == name_or_index), None) +125 if not (device and _set_device(device)): +126 text_formatter.cmd_print(f"%RED%Invalid audio input device: '{name_or_index}'%NC%") +127 +128 @staticmethod +129 def tts(text: str, dest_dir: str = os.getcwd(), playback: bool = True) -> None: +130 """Convert text to speech and optionally play it back. +131 :param text: The text to be converted to speech. +132 :param dest_dir: The directory where the audio file will be saved (default is the current working directory). +133 :param playback: Whether to play back the generated speech after conversion (default is True). +134 """ +135 if (audio_path := shared.engine.text_to_speech(text, stream=False, playback=playback)) and audio_path.exists(): +136 if dest_dir and ((dest_path := Path(dest_dir)) and dest_path.exists()): +137 audio_path = copy_file(audio_path, dest_dir) +138 text_formatter.cmd_print(f"File %GREEN%'{audio_path}' was successfully saved!%NC%") +139 else: +140 text_formatter.cmd_print(f"%RED%Unable to convert text to file !%NC%")

    @@ -217,93 +243,117 @@

    -
     29class TtsSttCmd(ABC):
    - 30    """Provides Speech-to-Text (STT) and Text-to-Speech (TTS) command functionalities."""
    - 31
    - 32    @staticmethod
    - 33    def voice_list() -> None:
    - 34        """List all available voices for the current engine/model."""
    - 35        all_voices = shared.engine.voices()
    - 36        str_voices = "\n".join([f"{i}. {v.title()}" for i, v in enumerate(all_voices)])
    - 37        text_formatter.cmd_print(
    - 38            f"Available `{shared.engine.configs().stt_model}` voices: \n"
    - 39            f"\n{str_voices}\n> Hint: Type: '/voices set \\<number|voice_name\\>' to select a voice. "
    - 40            f"To hear a sample use: '/voices play \\<number|voice_name\\>'"
    - 41        )
    - 42
    - 43    @staticmethod
    - 44    def voice_set(name: str | int | None = None) -> None:
    - 45        """Set the specified engine's voice."""
    - 46        all_voices = shared.engine.voices()
    - 47        if name.isdecimal() and 0 <= int(name) <= len(all_voices):
    - 48            name = all_voices[int(name)]
    - 49        if name in all_voices:
    - 50            settings.put("openai.text.to.speech.voice", name)
    - 51            shared.engine.configs().tts_voice = name
    - 52            text_formatter.cmd_print(f"`Speech-To-Text` voice changed to %GREEN%{name.title()}%NC%")
    - 53        else:
    - 54            text_formatter.cmd_print(f"%RED%Invalid voice: '{name}'%NC%")
    - 55
    - 56    @staticmethod
    - 57    def voice_play(name: str | int | None = None) -> None:
    - 58        """Play a sample using the specified voice."""
    - 59        all_voices = shared.engine.voices()
    - 60        ai_name = shared.engine.ai_name().lower().replace("engine", "")
    - 61        if name.isdecimal() and 0 <= int(name) <= len(all_voices):
    - 62            name = all_voices[int(name)]
    - 63        if name in all_voices:
    - 64            text_formatter.cmd_print(f"Sampling voice `{name}` …")
    - 65            player.play_sfx(f"voices/{ai_name}-{name}-sample")
    - 66        else:
    - 67            text_formatter.cmd_print(f"%RED%Invalid voice: '{name}'%NC%")
    - 68
    - 69    @staticmethod
    - 70    def tempo(speed: int | None = None) -> None:
    - 71        """Set the playing speed of the speech."""
    - 72        if not speed:
    - 73            settings.get("askai.text.to.speech.tempo")
    - 74        elif 1 <= speed <= 3:
    - 75            settings.put("askai.text.to.speech.tempo", speed)
    - 76            configs.tempo = speed
    - 77            tempo_str: str = "Normal" if speed == 1 else ("Fast" if speed == 2 else "Ultra")
    - 78            text_formatter.cmd_print(f"`Speech-To-Text` **tempo** changed to %GREEN%{tempo_str} ({speed})%NC%")
    - 79        else:
    - 80            text_formatter.cmd_print(f"%RED%Invalid tempo value: '{speed}'. Please choose between [1..3].%NC%")
    - 81
    - 82    @staticmethod
    - 83    def device_list() -> None:
    - 84        """List the available audio input devices."""
    - 85        all_devices = recorder.devices
    - 86        str_devices = "\n".join([f"{d[0]}. {d[1]}" for d in all_devices])
    - 87        text_formatter.cmd_print(
    - 88            f"Current audio input device: {recorder.input_device} \n"
    - 89            f"\nAvailable audio input devices: \n"
    - 90            f"\n{str_devices}\n> Hint: Type: '/devices set \\<number\\>' to select a device."
    - 91        )
    - 92
    - 93    @staticmethod
    - 94    def device_set(name: str | int | None = None) -> None:
    - 95        """Set the current audio input device."""
    - 96        all_devices = recorder.devices
    - 97        if name.isdecimal() and 0 <= int(name) <= len(all_devices):
    - 98            name = all_devices[int(name)][1]
    - 99        if device := next((dev for dev in all_devices if dev[1] == name), None):
    -100            if recorder.set_device(device):
    -101                text_formatter.cmd_print(f"`Text-To-Speech` device changed to %GREEN%{device[1]}%NC%")
    -102            else:
    -103                text_formatter.cmd_print(f"%RED%Device: '{name}' failed to initialize!%NC%")
    -104        else:
    -105            text_formatter.cmd_print(f"%RED%Invalid audio input device: '{name}'%NC%")
    -106
    -107    @staticmethod
    -108    def tts(text: str, dest_dir: str = os.getcwd(), playback: bool = True) -> None:
    -109        """Convert a text to speech."""
    -110        if (audio_path := shared.engine.text_to_speech(text, stream=False, playback=playback)) and audio_path.exists():
    -111            if dest_dir and ((dest_path := Path(dest_dir)) and dest_path.exists()):
    -112                audio_path = copy_file(audio_path, dest_dir)
    -113            text_formatter.cmd_print(f"File %GREEN%'{audio_path}' was successfully saved!%NC%")
    -114        else:
    -115            text_formatter.cmd_print(f"%RED%Unable to convert text to file !%NC%")
    +            
     31class TtsSttCmd(ABC):
    + 32    """Provides Speech-to-Text (STT) and Text-to-Speech (TTS) command functionalities."""
    + 33
    + 34    @staticmethod
    + 35    def voice_list() -> None:
    + 36        """List all available voices for the current engine/model."""
    + 37        all_voices = shared.engine.voices()
    + 38        str_voices = "\n".join([f"{i}. {v.title()}" for i, v in enumerate(all_voices)])
    + 39        text_formatter.cmd_print(
    + 40            f"Available `{shared.engine.configs().stt_model}` voices: \n"
    + 41            f"\n{str_voices}\n> Hint: Type: '/voices set \\<number|voice_name\\>' to select a voice. "
    + 42            f"To hear a sample use: '/voices play \\<number|voice_name\\>'"
    + 43        )
    + 44
    + 45    @staticmethod
    + 46    def voice_set(name_or_index: str | int | None = None) -> None:
    + 47        """Set the specified voice for the current engine.
    + 48        :param name_or_index: The name or index of the voice to set. If None, the default voice will be used.
    + 49        """
    + 50        all_voices = shared.engine.voices()
    + 51        if name_or_index.isdecimal() and 0 <= int(name_or_index) <= len(all_voices):
    + 52            name_or_index = all_voices[int(name_or_index)]
    + 53        if name_or_index in all_voices:
    + 54            settings.put("openai.text.to.speech.voice", name_or_index)
    + 55            shared.engine.configs().tts_voice = name_or_index
    + 56            text_formatter.cmd_print(f"`Speech-To-Text` voice changed to %GREEN%{name_or_index.title()}%NC%")
    + 57        else:
    + 58            text_formatter.cmd_print(f"%RED%Invalid voice: '{name_or_index}'%NC%")
    + 59
    + 60    @staticmethod
    + 61    def voice_play(name_or_index: str | int | None = None) -> None:
    + 62        """Play a sample using the specified voice.
    + 63        :param name_or_index: The name or index of the voice to use for the sample. If None, the default voice will be used.
    + 64        """
    + 65        all_voices = shared.engine.voices()
    + 66        ai_name = shared.engine.ai_name().lower().replace("engine", "")
    + 67        if name_or_index.isdecimal() and 0 <= int(name_or_index) <= len(all_voices):
    + 68            name_or_index = all_voices[int(name_or_index)]
    + 69        if name_or_index in all_voices:
    + 70            text_formatter.cmd_print(f"Sampling voice `{name_or_index}` …")
    + 71            player.play_sfx(f"voices/{ai_name}-{name_or_index}-sample")
    + 72        else:
    + 73            text_formatter.cmd_print(f"%RED%Invalid voice: '{name_or_index}'%NC%")
    + 74
    + 75    @staticmethod
    + 76    def tempo(speed: int | None = None) -> None:
    + 77        """Set the playing speed of the speech.
    + 78        :param speed: The speed to set for speech playback. If None, the default speed will be used.
    + 79        """
    + 80        if not speed:
    + 81            settings.get("askai.text.to.speech.tempo")
    + 82        elif 1 <= speed <= 3:
    + 83            settings.put("askai.text.to.speech.tempo", speed)
    + 84            configs.tempo = speed
    + 85            tempo_str: str = "Normal" if speed == 1 else ("Fast" if speed == 2 else "Ultra")
    + 86            text_formatter.cmd_print(f"`Speech-To-Text` **tempo** changed to %GREEN%{tempo_str} ({speed})%NC%")
    + 87        else:
    + 88            text_formatter.cmd_print(f"%RED%Invalid tempo value: '{speed}'. Please choose between [1..3].%NC%")
    + 89
    + 90    @staticmethod
    + 91    def device_list() -> None:
    + 92        """List the available audio input devices."""
    + 93        all_devices = recorder.devices
    + 94        str_devices = "\n".join([f"{d[0]}. {d[1]}" for d in all_devices])
    + 95        text_formatter.cmd_print(
    + 96            f"Current audio input device: {recorder.input_device} \n"
    + 97            f"\nAvailable audio input devices: \n"
    + 98            f"\n{str_devices}\n> Hint: Type: '/devices set \\<number\\>' to select a device."
    + 99        )
    +100
    +101    @staticmethod
    +102    def device_set(name_or_index: str | int | None = None) -> None:
    +103        """Set the current audio input device.
    +104        :param name_or_index: The name or index of the audio input device to set. If None, the default device will be
    +105                              used.
    +106        """
    +107        device: InputDevice | None = None
    +108        all_devices = recorder.devices
    +109
    +110        def _set_device(_device) -> bool:
    +111            if recorder.set_device(_device):
    +112                text_formatter.cmd_print(f"`Text-To-Speech` device changed to %GREEN%{_device}%NC%")
    +113                return True
    +114            text_formatter.cmd_print(f"%HOM%%ED2%Error: '{_device}' is not an Audio Input device!%NC%")
    +115            all_devices.remove(_device)
    +116            pause.seconds(2)
    +117            return False
    +118
    +119        if not name_or_index:
    +120            device: InputDevice = mselect(
    +121                all_devices, f"{'-=' * 40}%EOL%AskAI::Select the Audio Input device%EOL%{'=-' * 40}%EOL%"
    +122            )
    +123        elif name_or_index.isdecimal() and 0 <= int(name_or_index) <= len(all_devices):
    +124            name_or_index = all_devices[int(name_or_index)][1]
    +125            device = next((dev for dev in all_devices if dev[1] == name_or_index), None)
    +126        if not (device and _set_device(device)):
    +127            text_formatter.cmd_print(f"%RED%Invalid audio input device: '{name_or_index}'%NC%")
    +128
    +129    @staticmethod
    +130    def tts(text: str, dest_dir: str = os.getcwd(), playback: bool = True) -> None:
    +131        """Convert text to speech and optionally play it back.
    +132        :param text: The text to be converted to speech.
    +133        :param dest_dir: The directory where the audio file will be saved (default is the current working directory).
    +134        :param playback: Whether to play back the generated speech after conversion (default is True).
    +135        """
    +136        if (audio_path := shared.engine.text_to_speech(text, stream=False, playback=playback)) and audio_path.exists():
    +137            if dest_dir and ((dest_path := Path(dest_dir)) and dest_path.exists()):
    +138                audio_path = copy_file(audio_path, dest_dir)
    +139            text_formatter.cmd_print(f"File %GREEN%'{audio_path}' was successfully saved!%NC%")
    +140        else:
    +141            text_formatter.cmd_print(f"%RED%Unable to convert text to file !%NC%")
     
    @@ -323,16 +373,16 @@

    -
    32    @staticmethod
    -33    def voice_list() -> None:
    -34        """List all available voices for the current engine/model."""
    -35        all_voices = shared.engine.voices()
    -36        str_voices = "\n".join([f"{i}. {v.title()}" for i, v in enumerate(all_voices)])
    -37        text_formatter.cmd_print(
    -38            f"Available `{shared.engine.configs().stt_model}` voices: \n"
    -39            f"\n{str_voices}\n> Hint: Type: '/voices set \\<number|voice_name\\>' to select a voice. "
    -40            f"To hear a sample use: '/voices play \\<number|voice_name\\>'"
    -41        )
    +            
    34    @staticmethod
    +35    def voice_list() -> None:
    +36        """List all available voices for the current engine/model."""
    +37        all_voices = shared.engine.voices()
    +38        str_voices = "\n".join([f"{i}. {v.title()}" for i, v in enumerate(all_voices)])
    +39        text_formatter.cmd_print(
    +40            f"Available `{shared.engine.configs().stt_model}` voices: \n"
    +41            f"\n{str_voices}\n> Hint: Type: '/voices set \\<number|voice_name\\>' to select a voice. "
    +42            f"To hear a sample use: '/voices play \\<number|voice_name\\>'"
    +43        )
     
    @@ -347,28 +397,36 @@

    @staticmethod
    def - voice_set(name: str | int | None = None) -> None: + voice_set(name_or_index: str | int | None = None) -> None:

    -
    43    @staticmethod
    -44    def voice_set(name: str | int | None = None) -> None:
    -45        """Set the specified engine's voice."""
    -46        all_voices = shared.engine.voices()
    -47        if name.isdecimal() and 0 <= int(name) <= len(all_voices):
    -48            name = all_voices[int(name)]
    -49        if name in all_voices:
    -50            settings.put("openai.text.to.speech.voice", name)
    -51            shared.engine.configs().tts_voice = name
    -52            text_formatter.cmd_print(f"`Speech-To-Text` voice changed to %GREEN%{name.title()}%NC%")
    -53        else:
    -54            text_formatter.cmd_print(f"%RED%Invalid voice: '{name}'%NC%")
    +            
    45    @staticmethod
    +46    def voice_set(name_or_index: str | int | None = None) -> None:
    +47        """Set the specified voice for the current engine.
    +48        :param name_or_index: The name or index of the voice to set. If None, the default voice will be used.
    +49        """
    +50        all_voices = shared.engine.voices()
    +51        if name_or_index.isdecimal() and 0 <= int(name_or_index) <= len(all_voices):
    +52            name_or_index = all_voices[int(name_or_index)]
    +53        if name_or_index in all_voices:
    +54            settings.put("openai.text.to.speech.voice", name_or_index)
    +55            shared.engine.configs().tts_voice = name_or_index
    +56            text_formatter.cmd_print(f"`Speech-To-Text` voice changed to %GREEN%{name_or_index.title()}%NC%")
    +57        else:
    +58            text_formatter.cmd_print(f"%RED%Invalid voice: '{name_or_index}'%NC%")
     
    -

    Set the specified engine's voice.

    +

    Set the specified voice for the current engine.

    + +
    Parameters
    + +
      +
    • name_or_index: The name or index of the voice to set. If None, the default voice will be used.
    • +
    @@ -379,28 +437,36 @@

    @staticmethod
    def - voice_play(name: str | int | None = None) -> None: + voice_play(name_or_index: str | int | None = None) -> None:

    -
    56    @staticmethod
    -57    def voice_play(name: str | int | None = None) -> None:
    -58        """Play a sample using the specified voice."""
    -59        all_voices = shared.engine.voices()
    -60        ai_name = shared.engine.ai_name().lower().replace("engine", "")
    -61        if name.isdecimal() and 0 <= int(name) <= len(all_voices):
    -62            name = all_voices[int(name)]
    -63        if name in all_voices:
    -64            text_formatter.cmd_print(f"Sampling voice `{name}` …")
    -65            player.play_sfx(f"voices/{ai_name}-{name}-sample")
    -66        else:
    -67            text_formatter.cmd_print(f"%RED%Invalid voice: '{name}'%NC%")
    +            
    60    @staticmethod
    +61    def voice_play(name_or_index: str | int | None = None) -> None:
    +62        """Play a sample using the specified voice.
    +63        :param name_or_index: The name or index of the voice to use for the sample. If None, the default voice will be used.
    +64        """
    +65        all_voices = shared.engine.voices()
    +66        ai_name = shared.engine.ai_name().lower().replace("engine", "")
    +67        if name_or_index.isdecimal() and 0 <= int(name_or_index) <= len(all_voices):
    +68            name_or_index = all_voices[int(name_or_index)]
    +69        if name_or_index in all_voices:
    +70            text_formatter.cmd_print(f"Sampling voice `{name_or_index}` …")
    +71            player.play_sfx(f"voices/{ai_name}-{name_or_index}-sample")
    +72        else:
    +73            text_formatter.cmd_print(f"%RED%Invalid voice: '{name_or_index}'%NC%")
     

    Play a sample using the specified voice.

    + +
    Parameters
    + +
      +
    • name_or_index: The name or index of the voice to use for the sample. If None, the default voice will be used.
    • +
    @@ -417,22 +483,30 @@

    -
    69    @staticmethod
    -70    def tempo(speed: int | None = None) -> None:
    -71        """Set the playing speed of the speech."""
    -72        if not speed:
    -73            settings.get("askai.text.to.speech.tempo")
    -74        elif 1 <= speed <= 3:
    -75            settings.put("askai.text.to.speech.tempo", speed)
    -76            configs.tempo = speed
    -77            tempo_str: str = "Normal" if speed == 1 else ("Fast" if speed == 2 else "Ultra")
    -78            text_formatter.cmd_print(f"`Speech-To-Text` **tempo** changed to %GREEN%{tempo_str} ({speed})%NC%")
    -79        else:
    -80            text_formatter.cmd_print(f"%RED%Invalid tempo value: '{speed}'. Please choose between [1..3].%NC%")
    +            
    75    @staticmethod
    +76    def tempo(speed: int | None = None) -> None:
    +77        """Set the playing speed of the speech.
    +78        :param speed: The speed to set for speech playback. If None, the default speed will be used.
    +79        """
    +80        if not speed:
    +81            settings.get("askai.text.to.speech.tempo")
    +82        elif 1 <= speed <= 3:
    +83            settings.put("askai.text.to.speech.tempo", speed)
    +84            configs.tempo = speed
    +85            tempo_str: str = "Normal" if speed == 1 else ("Fast" if speed == 2 else "Ultra")
    +86            text_formatter.cmd_print(f"`Speech-To-Text` **tempo** changed to %GREEN%{tempo_str} ({speed})%NC%")
    +87        else:
    +88            text_formatter.cmd_print(f"%RED%Invalid tempo value: '{speed}'. Please choose between [1..3].%NC%")
     

    Set the playing speed of the speech.

    + +
    Parameters
    + +
      +
    • speed: The speed to set for speech playback. If None, the default speed will be used.
    • +
    @@ -449,16 +523,16 @@

    -
    82    @staticmethod
    -83    def device_list() -> None:
    -84        """List the available audio input devices."""
    -85        all_devices = recorder.devices
    -86        str_devices = "\n".join([f"{d[0]}. {d[1]}" for d in all_devices])
    -87        text_formatter.cmd_print(
    -88            f"Current audio input device: {recorder.input_device} \n"
    -89            f"\nAvailable audio input devices: \n"
    -90            f"\n{str_devices}\n> Hint: Type: '/devices set \\<number\\>' to select a device."
    -91        )
    +            
    90    @staticmethod
    +91    def device_list() -> None:
    +92        """List the available audio input devices."""
    +93        all_devices = recorder.devices
    +94        str_devices = "\n".join([f"{d[0]}. {d[1]}" for d in all_devices])
    +95        text_formatter.cmd_print(
    +96            f"Current audio input device: {recorder.input_device} \n"
    +97            f"\nAvailable audio input devices: \n"
    +98            f"\n{str_devices}\n> Hint: Type: '/devices set \\<number\\>' to select a device."
    +99        )
     
    @@ -473,29 +547,50 @@

    @staticmethod
    def - device_set(name: str | int | None = None) -> None: + device_set(name_or_index: str | int | None = None) -> None:

    -
     93    @staticmethod
    - 94    def device_set(name: str | int | None = None) -> None:
    - 95        """Set the current audio input device."""
    - 96        all_devices = recorder.devices
    - 97        if name.isdecimal() and 0 <= int(name) <= len(all_devices):
    - 98            name = all_devices[int(name)][1]
    - 99        if device := next((dev for dev in all_devices if dev[1] == name), None):
    -100            if recorder.set_device(device):
    -101                text_formatter.cmd_print(f"`Text-To-Speech` device changed to %GREEN%{device[1]}%NC%")
    -102            else:
    -103                text_formatter.cmd_print(f"%RED%Device: '{name}' failed to initialize!%NC%")
    -104        else:
    -105            text_formatter.cmd_print(f"%RED%Invalid audio input device: '{name}'%NC%")
    +            
    101    @staticmethod
    +102    def device_set(name_or_index: str | int | None = None) -> None:
    +103        """Set the current audio input device.
    +104        :param name_or_index: The name or index of the audio input device to set. If None, the default device will be
    +105                              used.
    +106        """
    +107        device: InputDevice | None = None
    +108        all_devices = recorder.devices
    +109
    +110        def _set_device(_device) -> bool:
    +111            if recorder.set_device(_device):
    +112                text_formatter.cmd_print(f"`Text-To-Speech` device changed to %GREEN%{_device}%NC%")
    +113                return True
    +114            text_formatter.cmd_print(f"%HOM%%ED2%Error: '{_device}' is not an Audio Input device!%NC%")
    +115            all_devices.remove(_device)
    +116            pause.seconds(2)
    +117            return False
    +118
    +119        if not name_or_index:
    +120            device: InputDevice = mselect(
    +121                all_devices, f"{'-=' * 40}%EOL%AskAI::Select the Audio Input device%EOL%{'=-' * 40}%EOL%"
    +122            )
    +123        elif name_or_index.isdecimal() and 0 <= int(name_or_index) <= len(all_devices):
    +124            name_or_index = all_devices[int(name_or_index)][1]
    +125            device = next((dev for dev in all_devices if dev[1] == name_or_index), None)
    +126        if not (device and _set_device(device)):
    +127            text_formatter.cmd_print(f"%RED%Invalid audio input device: '{name_or_index}'%NC%")
     

    Set the current audio input device.

    + +
    Parameters
    + +
      +
    • name_or_index: The name or index of the audio input device to set. If None, the default device will be +used.
    • +
    @@ -512,19 +607,31 @@

    -
    107    @staticmethod
    -108    def tts(text: str, dest_dir: str = os.getcwd(), playback: bool = True) -> None:
    -109        """Convert a text to speech."""
    -110        if (audio_path := shared.engine.text_to_speech(text, stream=False, playback=playback)) and audio_path.exists():
    -111            if dest_dir and ((dest_path := Path(dest_dir)) and dest_path.exists()):
    -112                audio_path = copy_file(audio_path, dest_dir)
    -113            text_formatter.cmd_print(f"File %GREEN%'{audio_path}' was successfully saved!%NC%")
    -114        else:
    -115            text_formatter.cmd_print(f"%RED%Unable to convert text to file !%NC%")
    +            
    129    @staticmethod
    +130    def tts(text: str, dest_dir: str = os.getcwd(), playback: bool = True) -> None:
    +131        """Convert text to speech and optionally play it back.
    +132        :param text: The text to be converted to speech.
    +133        :param dest_dir: The directory where the audio file will be saved (default is the current working directory).
    +134        :param playback: Whether to play back the generated speech after conversion (default is True).
    +135        """
    +136        if (audio_path := shared.engine.text_to_speech(text, stream=False, playback=playback)) and audio_path.exists():
    +137            if dest_dir and ((dest_path := Path(dest_dir)) and dest_path.exists()):
    +138                audio_path = copy_file(audio_path, dest_dir)
    +139            text_formatter.cmd_print(f"File %GREEN%'{audio_path}' was successfully saved!%NC%")
    +140        else:
    +141            text_formatter.cmd_print(f"%RED%Unable to convert text to file !%NC%")
     
    -

    Convert a text to speech.

    +

    Convert text to speech and optionally play it back.

    + +
    Parameters
    + +
      +
    • text: The text to be converted to speech.
    • +
    • dest_dir: The directory where the audio file will be saved (default is the current working directory).
    • +
    • playback: Whether to play back the generated speech after conversion (default is True).
    • +
    diff --git a/docs/api/askai/main/askai/core/component.html b/docs/api/askai/main/askai/core/component.html index c7eec615..b04daebf 100644 --- a/docs/api/askai/main/askai/core/component.html +++ b/docs/api/askai/main/askai/core/component.html @@ -3,7 +3,7 @@ - + main.askai.core.component API documentation @@ -38,6 +38,7 @@

    Submodules

  • recorder
  • scheduler
  • summarizer
  • +
  • text_streamer
  • @@ -63,7 +64,7 @@

     1# _*_ coding: utf-8 _*_
      2#
    - 3# hspylib-askai v1.0.11
    + 3# hspylib-askai v1.0.13
      4#
      5# Package: main.askai.core.component
      6"""Package initialization."""
    @@ -77,9 +78,10 @@ 

    14 'internet_service', 15 'recorder', 16 'scheduler', -17 'summarizer' -18] -19__version__ = '1.0.11' +17 'summarizer', +18 'text_streamer' +19] +20__version__ = '1.0.13'

    diff --git a/docs/api/askai/main/askai/core/component/audio_player.html b/docs/api/askai/main/askai/core/component/audio_player.html index 8d7d06c8..14c81281 100644 --- a/docs/api/askai/main/askai/core/component/audio_player.html +++ b/docs/api/askai/main/askai/core/component/audio_player.html @@ -3,7 +3,7 @@ - + main.askai.core.component.audio_player API documentation @@ -88,104 +88,112 @@

    -
     1#!/usr/bin/env python3
    - 2# -*- coding: utf-8 -*-
    - 3
    - 4"""
    - 5   @project: HsPyLib-AskAI
    - 6   @package: askai.core.component
    - 7      @file: audio_player.py
    - 8   @created: Wed, 22 Feb 2024
    - 9    @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior
    -10      @site: https://github.com/yorevs/askai
    -11   @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
    -12
    -13   Copyright (c) 2024, HomeSetup
    -14"""
    -15from askai.__classpath__ import classpath
    -16from clitt.core.term.terminal import Terminal
    -17from functools import lru_cache
    -18from hspylib.core.metaclass.singleton import Singleton
    -19from hspylib.core.preconditions import check_argument
    -20from hspylib.core.tools.commons import file_is_not_empty
    -21from hspylib.core.tools.text_tools import ensure_endswith
    -22from hspylib.modules.application.exit_status import ExitStatus
    -23from pathlib import Path
    -24from shutil import which
    -25from typing import Literal
    -26
    -27import logging as log
    -28import time
    -29
    -30
    -31class AudioPlayer(metaclass=Singleton):
    -32    """Provide an interface to play audio using the default speaker device."""
    -33
    -34    INSTANCE: "AudioPlayer"
    -35
    -36    # Sound effects directory.
    -37    SFX_DIR = str(classpath.resource_path()) + "/assets/sound-fx"
    -38
    -39    @staticmethod
    -40    def play_audio_file(path_to_audio_file: str | Path, tempo: int = 1) -> bool:
    -41        """Play the specified mp3 file using ffplay (ffmpeg) application.
    -42        :param path_to_audio_file: the path to the mp3 file to be played.
    -43        :param tempo: the playing speed.
    -44        """
    -45        if file_is_not_empty(str(path_to_audio_file)):
    -46            try:
    -47                out, code = Terminal.shell_exec(
    -48                    f'ffplay -af "atempo={tempo}" -v 0 -nodisp -autoexit {path_to_audio_file}'
    -49                )
    -50                return code == ExitStatus.SUCCESS
    -51            except FileNotFoundError:
    -52                log.error("Audio file was not found: %s !", path_to_audio_file)
    -53
    -54        return False
    -55
    -56    def __init__(self):
    -57        check_argument(which("ffplay") is not None, "ffmpeg::ffplay is required to play audio")
    -58
    -59    @lru_cache
    -60    def start_delay(self) -> float:
    -61        """Determine the amount of delay before start streaming the text."""
    -62        log.debug("Determining the start delay")
    -63        sample_duration_sec = 1.75  # We know the length
    -64        started = time.time()
    -65        self.play_sfx("sample.mp3")
    -66        delay = max(0.0, time.time() - started - sample_duration_sec)
    -67        log.debug("Detected a play delay of %s seconds", delay)
    -68
    -69        return delay
    -70
    -71    @lru_cache
    -72    def audio_length(self, path_to_audio_file: str) -> float:
    -73        check_argument(which("ffprobe") and file_is_not_empty(path_to_audio_file))
    -74        ret: float = 0.0
    -75        try:
    -76            ret, code = Terminal.shell_exec(
    -77                f'ffprobe -i {path_to_audio_file} -show_entries format=duration -v quiet -of csv="p=0"'
    -78            )
    -79            return float(ret) if code == ExitStatus.SUCCESS else 0.0
    -80        except FileNotFoundError:
    -81            log.error("Audio file was not found: %s !", path_to_audio_file)
    -82        except TypeError:
    -83            log.error("Could not determine audio duration !")
    -84
    -85        return ret
    -86
    -87    def play_sfx(self, filename: str, file_ext: Literal[".mp3", ".wav", ".m4a"] = ".mp3") -> bool:
    -88        """Play a sound effect audio file.
    -89        :param filename: The filename of the sound effect.
    -90        :param file_ext: the file extension.
    -91        """
    -92        filename = f"{self.SFX_DIR}/{ensure_endswith(filename, file_ext)}"
    -93        check_argument(file_is_not_empty(filename), f"Sound effects file does not exist: {filename}")
    -94
    -95        return self.play_audio_file(filename)
    -96
    -97
    -98assert (player := AudioPlayer().INSTANCE) is not None
    +                        
      1#!/usr/bin/env python3
    +  2# -*- coding: utf-8 -*-
    +  3
    +  4"""
    +  5   @project: HsPyLib-AskAI
    +  6   @package: askai.core.component
    +  7      @file: audio_player.py
    +  8   @created: Wed, 22 Feb 2024
    +  9    @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior
    + 10      @site: https://github.com/yorevs/askai
    + 11   @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
    + 12
    + 13   Copyright (c) 2024, HomeSetup
    + 14"""
    + 15from askai.__classpath__ import classpath
    + 16from clitt.core.term.terminal import Terminal
    + 17from functools import lru_cache
    + 18from hspylib.core.metaclass.singleton import Singleton
    + 19from hspylib.core.preconditions import check_argument
    + 20from hspylib.core.tools.commons import file_is_not_empty
    + 21from hspylib.core.tools.text_tools import ensure_endswith
    + 22from hspylib.modules.application.exit_status import ExitStatus
    + 23from pathlib import Path
    + 24from shutil import which
    + 25from typing import Literal
    + 26
    + 27import logging as log
    + 28import time
    + 29
    + 30
    + 31class AudioPlayer(metaclass=Singleton):
    + 32    """Provide an interface for playing audio through the default speaker device. This class allows for the playback of
    + 33    audio files using the system's default audio output device.
    + 34    """
    + 35
    + 36    INSTANCE: "AudioPlayer"
    + 37
    + 38    # Sound effects directory.
    + 39    SFX_DIR = str(classpath.resource_path()) + "/sound-fx"
    + 40
    + 41    @staticmethod
    + 42    def play_audio_file(path_to_audio_file: str | Path, tempo: int = 1) -> bool:
    + 43        """Play the specified audio file using the ffplay (ffmpeg) application.
    + 44        :param path_to_audio_file: The path to the audio file (e.g., MP3) to be played.
    + 45        :param tempo: The playback speed (default is 1).
    + 46        :return: True if the audio file is played successfully, otherwise False.
    + 47        """
    + 48        if file_is_not_empty(str(path_to_audio_file)):
    + 49            try:
    + 50                _, _, code = Terminal.shell_exec(
    + 51                    f'ffplay -af "atempo={tempo}" -v 0 -nodisp -autoexit {path_to_audio_file}'
    + 52                )
    + 53                return code == ExitStatus.SUCCESS
    + 54            except FileNotFoundError:
    + 55                log.error("Audio file was not found: %s !", path_to_audio_file)
    + 56
    + 57        return False
    + 58
    + 59    def __init__(self):
    + 60        check_argument(which("ffplay") is not None, "ffmpeg::ffplay is required to play audio")
    + 61
    + 62    @lru_cache
    + 63    def start_delay(self) -> float:
    + 64        """Determine the amount of delay before start streaming the text."""
    + 65        log.debug("Determining the start delay")
    + 66        sample_duration_sec = 1.75  # We know the length
    + 67        started = time.time()
    + 68        self.play_sfx("sample.mp3")
    + 69        delay = max(0.0, time.time() - started - sample_duration_sec)
    + 70        log.debug("Detected a play delay of %s seconds", delay)
    + 71
    + 72        return delay
    + 73
    + 74    @lru_cache
    + 75    def audio_length(self, path_to_audio_file: str) -> float:
    + 76        """Determine the length of an audio file using ffmpeg.
    + 77        :param path_to_audio_file: The path to the audio file whose length is to be determined.
    + 78        :return: The length of the audio file in seconds as a float.
    + 79        """
    + 80        check_argument(which("ffprobe") and file_is_not_empty(path_to_audio_file))
    + 81        out: float = 0.0
    + 82        try:
    + 83            out, _, code = Terminal.shell_exec(
    + 84                f'ffprobe -i {path_to_audio_file} -show_entries format=duration -v quiet -of csv="p=0"'
    + 85            )
    + 86            return float(out) if code == ExitStatus.SUCCESS else 0.0
    + 87        except FileNotFoundError:
    + 88            log.error("Audio file was not found: %s !", path_to_audio_file)
    + 89        except TypeError:
    + 90            log.error("Could not determine audio duration !")
    + 91
    + 92        return out
    + 93
    + 94    def play_sfx(self, filename: str, file_ext: Literal[".mp3", ".wav", ".m4a"] = ".mp3") -> bool:
    + 95        """Play a sound effect audio file.
    + 96        :param filename: The name of the sound effect file (without the extension).
    + 97        :param file_ext: The file extension of the sound effect (default is ".mp3").
    + 98        :return: True if the sound effect is played successfully, otherwise False.
    + 99        """
    +100        filename = f"{self.SFX_DIR}/{ensure_endswith(filename, file_ext)}"
    +101        check_argument(file_is_not_empty(filename), f"Sound effects file does not exist: {filename}")
    +102
    +103        return self.play_audio_file(filename)
    +104
    +105
    +106assert (player := AudioPlayer().INSTANCE) is not None
     
    @@ -201,75 +209,84 @@

    -
    32class AudioPlayer(metaclass=Singleton):
    -33    """Provide an interface to play audio using the default speaker device."""
    -34
    -35    INSTANCE: "AudioPlayer"
    -36
    -37    # Sound effects directory.
    -38    SFX_DIR = str(classpath.resource_path()) + "/assets/sound-fx"
    -39
    -40    @staticmethod
    -41    def play_audio_file(path_to_audio_file: str | Path, tempo: int = 1) -> bool:
    -42        """Play the specified mp3 file using ffplay (ffmpeg) application.
    -43        :param path_to_audio_file: the path to the mp3 file to be played.
    -44        :param tempo: the playing speed.
    -45        """
    -46        if file_is_not_empty(str(path_to_audio_file)):
    -47            try:
    -48                out, code = Terminal.shell_exec(
    -49                    f'ffplay -af "atempo={tempo}" -v 0 -nodisp -autoexit {path_to_audio_file}'
    -50                )
    -51                return code == ExitStatus.SUCCESS
    -52            except FileNotFoundError:
    -53                log.error("Audio file was not found: %s !", path_to_audio_file)
    -54
    -55        return False
    -56
    -57    def __init__(self):
    -58        check_argument(which("ffplay") is not None, "ffmpeg::ffplay is required to play audio")
    -59
    -60    @lru_cache
    -61    def start_delay(self) -> float:
    -62        """Determine the amount of delay before start streaming the text."""
    -63        log.debug("Determining the start delay")
    -64        sample_duration_sec = 1.75  # We know the length
    -65        started = time.time()
    -66        self.play_sfx("sample.mp3")
    -67        delay = max(0.0, time.time() - started - sample_duration_sec)
    -68        log.debug("Detected a play delay of %s seconds", delay)
    -69
    -70        return delay
    -71
    -72    @lru_cache
    -73    def audio_length(self, path_to_audio_file: str) -> float:
    -74        check_argument(which("ffprobe") and file_is_not_empty(path_to_audio_file))
    -75        ret: float = 0.0
    -76        try:
    -77            ret, code = Terminal.shell_exec(
    -78                f'ffprobe -i {path_to_audio_file} -show_entries format=duration -v quiet -of csv="p=0"'
    -79            )
    -80            return float(ret) if code == ExitStatus.SUCCESS else 0.0
    -81        except FileNotFoundError:
    -82            log.error("Audio file was not found: %s !", path_to_audio_file)
    -83        except TypeError:
    -84            log.error("Could not determine audio duration !")
    -85
    -86        return ret
    -87
    -88    def play_sfx(self, filename: str, file_ext: Literal[".mp3", ".wav", ".m4a"] = ".mp3") -> bool:
    -89        """Play a sound effect audio file.
    -90        :param filename: The filename of the sound effect.
    -91        :param file_ext: the file extension.
    -92        """
    -93        filename = f"{self.SFX_DIR}/{ensure_endswith(filename, file_ext)}"
    -94        check_argument(file_is_not_empty(filename), f"Sound effects file does not exist: {filename}")
    -95
    -96        return self.play_audio_file(filename)
    +            
     32class AudioPlayer(metaclass=Singleton):
    + 33    """Provide an interface for playing audio through the default speaker device. This class allows for the playback of
    + 34    audio files using the system's default audio output device.
    + 35    """
    + 36
    + 37    INSTANCE: "AudioPlayer"
    + 38
    + 39    # Sound effects directory.
    + 40    SFX_DIR = str(classpath.resource_path()) + "/sound-fx"
    + 41
    + 42    @staticmethod
    + 43    def play_audio_file(path_to_audio_file: str | Path, tempo: int = 1) -> bool:
    + 44        """Play the specified audio file using the ffplay (ffmpeg) application.
    + 45        :param path_to_audio_file: The path to the audio file (e.g., MP3) to be played.
    + 46        :param tempo: The playback speed (default is 1).
    + 47        :return: True if the audio file is played successfully, otherwise False.
    + 48        """
    + 49        if file_is_not_empty(str(path_to_audio_file)):
    + 50            try:
    + 51                _, _, code = Terminal.shell_exec(
    + 52                    f'ffplay -af "atempo={tempo}" -v 0 -nodisp -autoexit {path_to_audio_file}'
    + 53                )
    + 54                return code == ExitStatus.SUCCESS
    + 55            except FileNotFoundError:
    + 56                log.error("Audio file was not found: %s !", path_to_audio_file)
    + 57
    + 58        return False
    + 59
    + 60    def __init__(self):
    + 61        check_argument(which("ffplay") is not None, "ffmpeg::ffplay is required to play audio")
    + 62
    + 63    @lru_cache
    + 64    def start_delay(self) -> float:
    + 65        """Determine the amount of delay before start streaming the text."""
    + 66        log.debug("Determining the start delay")
    + 67        sample_duration_sec = 1.75  # We know the length
    + 68        started = time.time()
    + 69        self.play_sfx("sample.mp3")
    + 70        delay = max(0.0, time.time() - started - sample_duration_sec)
    + 71        log.debug("Detected a play delay of %s seconds", delay)
    + 72
    + 73        return delay
    + 74
    + 75    @lru_cache
    + 76    def audio_length(self, path_to_audio_file: str) -> float:
    + 77        """Determine the length of an audio file using ffmpeg.
    + 78        :param path_to_audio_file: The path to the audio file whose length is to be determined.
    + 79        :return: The length of the audio file in seconds as a float.
    + 80        """
    + 81        check_argument(which("ffprobe") and file_is_not_empty(path_to_audio_file))
    + 82        out: float = 0.0
    + 83        try:
    + 84            out, _, code = Terminal.shell_exec(
    + 85                f'ffprobe -i {path_to_audio_file} -show_entries format=duration -v quiet -of csv="p=0"'
    + 86            )
    + 87            return float(out) if code == ExitStatus.SUCCESS else 0.0
    + 88        except FileNotFoundError:
    + 89            log.error("Audio file was not found: %s !", path_to_audio_file)
    + 90        except TypeError:
    + 91            log.error("Could not determine audio duration !")
    + 92
    + 93        return out
    + 94
    + 95    def play_sfx(self, filename: str, file_ext: Literal[".mp3", ".wav", ".m4a"] = ".mp3") -> bool:
    + 96        """Play a sound effect audio file.
    + 97        :param filename: The name of the sound effect file (without the extension).
    + 98        :param file_ext: The file extension of the sound effect (default is ".mp3").
    + 99        :return: True if the sound effect is played successfully, otherwise False.
    +100        """
    +101        filename = f"{self.SFX_DIR}/{ensure_endswith(filename, file_ext)}"
    +102        check_argument(file_is_not_empty(filename), f"Sound effects file does not exist: {filename}")
    +103
    +104        return self.play_audio_file(filename)
     
    -

    Provide an interface to play audio using the default speaker device.

    +

    Provide an interface for playing audio through the default speaker device. This class allows for the playback of +audio files using the system's default audio output device.

    @@ -318,7 +335,7 @@

    SFX_DIR = -'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources/assets/sound-fx' +'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources/sound-fx'
    @@ -339,33 +356,40 @@

    -
    40    @staticmethod
    -41    def play_audio_file(path_to_audio_file: str | Path, tempo: int = 1) -> bool:
    -42        """Play the specified mp3 file using ffplay (ffmpeg) application.
    -43        :param path_to_audio_file: the path to the mp3 file to be played.
    -44        :param tempo: the playing speed.
    -45        """
    -46        if file_is_not_empty(str(path_to_audio_file)):
    -47            try:
    -48                out, code = Terminal.shell_exec(
    -49                    f'ffplay -af "atempo={tempo}" -v 0 -nodisp -autoexit {path_to_audio_file}'
    -50                )
    -51                return code == ExitStatus.SUCCESS
    -52            except FileNotFoundError:
    -53                log.error("Audio file was not found: %s !", path_to_audio_file)
    -54
    -55        return False
    +            
    42    @staticmethod
    +43    def play_audio_file(path_to_audio_file: str | Path, tempo: int = 1) -> bool:
    +44        """Play the specified audio file using the ffplay (ffmpeg) application.
    +45        :param path_to_audio_file: The path to the audio file (e.g., MP3) to be played.
    +46        :param tempo: The playback speed (default is 1).
    +47        :return: True if the audio file is played successfully, otherwise False.
    +48        """
    +49        if file_is_not_empty(str(path_to_audio_file)):
    +50            try:
    +51                _, _, code = Terminal.shell_exec(
    +52                    f'ffplay -af "atempo={tempo}" -v 0 -nodisp -autoexit {path_to_audio_file}'
    +53                )
    +54                return code == ExitStatus.SUCCESS
    +55            except FileNotFoundError:
    +56                log.error("Audio file was not found: %s !", path_to_audio_file)
    +57
    +58        return False
     
    -

    Play the specified mp3 file using ffplay (ffmpeg) application.

    +

    Play the specified audio file using the ffplay (ffmpeg) application.

    Parameters
      -
    • path_to_audio_file: the path to the mp3 file to be played.
    • -
    • tempo: the playing speed.
    • +
    • path_to_audio_file: The path to the audio file (e.g., MP3) to be played.
    • +
    • tempo: The playback speed (default is 1).
    + +
    Returns
    + +
    +

    True if the audio file is played successfully, otherwise False.

    +
    @@ -382,17 +406,17 @@
    Parameters
    -
    60    @lru_cache
    -61    def start_delay(self) -> float:
    -62        """Determine the amount of delay before start streaming the text."""
    -63        log.debug("Determining the start delay")
    -64        sample_duration_sec = 1.75  # We know the length
    -65        started = time.time()
    -66        self.play_sfx("sample.mp3")
    -67        delay = max(0.0, time.time() - started - sample_duration_sec)
    -68        log.debug("Detected a play delay of %s seconds", delay)
    -69
    -70        return delay
    +            
    63    @lru_cache
    +64    def start_delay(self) -> float:
    +65        """Determine the amount of delay before start streaming the text."""
    +66        log.debug("Determining the start delay")
    +67        sample_duration_sec = 1.75  # We know the length
    +68        started = time.time()
    +69        self.play_sfx("sample.mp3")
    +70        delay = max(0.0, time.time() - started - sample_duration_sec)
    +71        log.debug("Detected a play delay of %s seconds", delay)
    +72
    +73        return delay
     
    @@ -413,25 +437,43 @@
    Parameters
    -
    72    @lru_cache
    -73    def audio_length(self, path_to_audio_file: str) -> float:
    -74        check_argument(which("ffprobe") and file_is_not_empty(path_to_audio_file))
    -75        ret: float = 0.0
    -76        try:
    -77            ret, code = Terminal.shell_exec(
    -78                f'ffprobe -i {path_to_audio_file} -show_entries format=duration -v quiet -of csv="p=0"'
    -79            )
    -80            return float(ret) if code == ExitStatus.SUCCESS else 0.0
    -81        except FileNotFoundError:
    -82            log.error("Audio file was not found: %s !", path_to_audio_file)
    -83        except TypeError:
    -84            log.error("Could not determine audio duration !")
    -85
    -86        return ret
    +            
    75    @lru_cache
    +76    def audio_length(self, path_to_audio_file: str) -> float:
    +77        """Determine the length of an audio file using ffmpeg.
    +78        :param path_to_audio_file: The path to the audio file whose length is to be determined.
    +79        :return: The length of the audio file in seconds as a float.
    +80        """
    +81        check_argument(which("ffprobe") and file_is_not_empty(path_to_audio_file))
    +82        out: float = 0.0
    +83        try:
    +84            out, _, code = Terminal.shell_exec(
    +85                f'ffprobe -i {path_to_audio_file} -show_entries format=duration -v quiet -of csv="p=0"'
    +86            )
    +87            return float(out) if code == ExitStatus.SUCCESS else 0.0
    +88        except FileNotFoundError:
    +89            log.error("Audio file was not found: %s !", path_to_audio_file)
    +90        except TypeError:
    +91            log.error("Could not determine audio duration !")
    +92
    +93        return out
     
    - +

    Determine the length of an audio file using ffmpeg.

    + +
    Parameters
    + +
      +
    • path_to_audio_file: The path to the audio file whose length is to be determined.
    • +
    + +
    Returns
    + +
    +

    The length of the audio file in seconds as a float.

    +
    +
    +
    @@ -445,15 +487,16 @@
    Parameters
    -
    88    def play_sfx(self, filename: str, file_ext: Literal[".mp3", ".wav", ".m4a"] = ".mp3") -> bool:
    -89        """Play a sound effect audio file.
    -90        :param filename: The filename of the sound effect.
    -91        :param file_ext: the file extension.
    -92        """
    -93        filename = f"{self.SFX_DIR}/{ensure_endswith(filename, file_ext)}"
    -94        check_argument(file_is_not_empty(filename), f"Sound effects file does not exist: {filename}")
    -95
    -96        return self.play_audio_file(filename)
    +            
     95    def play_sfx(self, filename: str, file_ext: Literal[".mp3", ".wav", ".m4a"] = ".mp3") -> bool:
    + 96        """Play a sound effect audio file.
    + 97        :param filename: The name of the sound effect file (without the extension).
    + 98        :param file_ext: The file extension of the sound effect (default is ".mp3").
    + 99        :return: True if the sound effect is played successfully, otherwise False.
    +100        """
    +101        filename = f"{self.SFX_DIR}/{ensure_endswith(filename, file_ext)}"
    +102        check_argument(file_is_not_empty(filename), f"Sound effects file does not exist: {filename}")
    +103
    +104        return self.play_audio_file(filename)
     
    @@ -462,9 +505,15 @@
    Parameters
    Parameters
      -
    • filename: The filename of the sound effect.
    • -
    • file_ext: the file extension.
    • +
    • filename: The name of the sound effect file (without the extension).
    • +
    • file_ext: The file extension of the sound effect (default is ".mp3").
    + +
    Returns
    + +
    +

    True if the sound effect is played successfully, otherwise False.

    +
    diff --git a/docs/api/askai/main/askai/core/component/cache_service.html b/docs/api/askai/main/askai/core/component/cache_service.html index 03c9b5fe..f37b9f2c 100644 --- a/docs/api/askai/main/askai/core/component/cache_service.html +++ b/docs/api/askai/main/askai/core/component/cache_service.html @@ -3,7 +3,7 @@ - + main.askai.core.component.cache_service API documentation @@ -177,89 +177,89 @@

    12 13 Copyright (c) 2024, HomeSetup 14""" - 15import re - 16from collections import namedtuple - 17from pathlib import Path - 18from typing import Optional, Tuple - 19 - 20from clitt.core.tui.line_input.keyboard_input import KeyboardInput - 21from hspylib.core.metaclass.singleton import Singleton - 22from hspylib.core.tools.commons import file_is_not_empty - 23from hspylib.core.tools.text_tools import hash_text - 24from hspylib.modules.cache.ttl_cache import TTLCache + 15from askai.core.askai_configs import configs + 16from askai.core.askai_settings import ASKAI_DIR + 17from clitt.core.tui.line_input.keyboard_input import KeyboardInput + 18from collections import namedtuple + 19from hspylib.core.metaclass.singleton import Singleton + 20from hspylib.core.tools.commons import file_is_not_empty + 21from hspylib.core.tools.text_tools import hash_text + 22from hspylib.modules.cache.ttl_cache import TTLCache + 23from pathlib import Path + 24from typing import Optional 25 - 26from askai.core.askai_configs import configs - 27from askai.core.askai_settings import ASKAI_DIR - 28 - 29# AskAI cache root directory. - 30CACHE_DIR: Path = Path(f"{ASKAI_DIR}/cache") - 31 - 32# Settings directory. - 33SETTINGS_DIR: Path = Path(str(ASKAI_DIR) + "/settings") - 34if not SETTINGS_DIR.exists(): - 35 SETTINGS_DIR.mkdir(parents=True, exist_ok=True) - 36 - 37# Transcribed audio cache directory. - 38AUDIO_DIR: Path = Path(str(CACHE_DIR) + "/audio") - 39if not AUDIO_DIR.exists(): - 40 AUDIO_DIR.mkdir(parents=True, exist_ok=True) - 41 - 42# Camera photo shots cache directory. - 43PICTURE_DIR: Path = Path(str(CACHE_DIR) + "/pictures") - 44if not PICTURE_DIR.exists(): - 45 PICTURE_DIR.mkdir(parents=True, exist_ok=True) - 46 - 47# Camera photo shots cache directory. - 48PHOTO_DIR: Path = Path(str(PICTURE_DIR) + "/photos") - 49if not PHOTO_DIR.exists(): - 50 PHOTO_DIR.mkdir(parents=True, exist_ok=True) - 51 - 52# Detected faces cache directory. - 53FACE_DIR: Path = Path(str(PICTURE_DIR) + "/faces") - 54if not FACE_DIR.exists(): - 55 FACE_DIR.mkdir(parents=True, exist_ok=True) - 56 - 57# Imported image files cache directory. - 58IMG_IMPORTS_DIR: Path = Path(str(PICTURE_DIR) + "/imports") - 59if not IMG_IMPORTS_DIR.exists(): - 60 IMG_IMPORTS_DIR.mkdir(parents=True, exist_ok=True) - 61 - 62# AI Generation cache directory. - 63GEN_AI_DIR: Path = Path(str(CACHE_DIR) + "/generated") - 64if not GEN_AI_DIR.exists(): - 65 GEN_AI_DIR.mkdir(parents=True, exist_ok=True) - 66 - 67# Voice recordings cache directory. - 68REC_DIR: Path = Path(str(CACHE_DIR) + "/recordings") - 69if not REC_DIR.exists(): - 70 REC_DIR.mkdir(parents=True, exist_ok=True) - 71 - 72# Transcribed audio cache directory. - 73PERSIST_DIR: Path = Path(str(CACHE_DIR) + "/chroma") - 74if not PERSIST_DIR.exists(): - 75 PERSIST_DIR.mkdir(parents=True, exist_ok=True) - 76 - 77# RAG Directory - 78RAG_DIR: Path = Path(str(CACHE_DIR) + "/rag") - 79if not RAG_DIR.exists(): - 80 RAG_DIR.mkdir(parents=True, exist_ok=True) + 26import re + 27 + 28# AskAI cache root directory. + 29CACHE_DIR: Path = Path(f"{ASKAI_DIR}/cache") + 30 + 31# Settings directory. + 32SETTINGS_DIR: Path = Path(str(ASKAI_DIR) + "/settings") + 33if not SETTINGS_DIR.exists(): + 34 SETTINGS_DIR.mkdir(parents=True, exist_ok=True) + 35 + 36# Transcribed audio cache directory. + 37AUDIO_DIR: Path = Path(str(CACHE_DIR) + "/audio") + 38if not AUDIO_DIR.exists(): + 39 AUDIO_DIR.mkdir(parents=True, exist_ok=True) + 40 + 41# Camera photo shots cache directory. + 42PICTURE_DIR: Path = Path(str(CACHE_DIR) + "/pictures") + 43if not PICTURE_DIR.exists(): + 44 PICTURE_DIR.mkdir(parents=True, exist_ok=True) + 45 + 46# Camera photo shots cache directory. + 47PHOTO_DIR: Path = Path(str(PICTURE_DIR) + "/photos") + 48if not PHOTO_DIR.exists(): + 49 PHOTO_DIR.mkdir(parents=True, exist_ok=True) + 50 + 51# Detected faces cache directory. + 52FACE_DIR: Path = Path(str(PICTURE_DIR) + "/faces") + 53if not FACE_DIR.exists(): + 54 FACE_DIR.mkdir(parents=True, exist_ok=True) + 55 + 56# Imported image files cache directory. + 57IMG_IMPORTS_DIR: Path = Path(str(PICTURE_DIR) + "/imports") + 58if not IMG_IMPORTS_DIR.exists(): + 59 IMG_IMPORTS_DIR.mkdir(parents=True, exist_ok=True) + 60 + 61# AI Generation cache directory. + 62GEN_AI_DIR: Path = Path(str(CACHE_DIR) + "/generated") + 63if not GEN_AI_DIR.exists(): + 64 GEN_AI_DIR.mkdir(parents=True, exist_ok=True) + 65 + 66# Voice recordings cache directory. + 67REC_DIR: Path = Path(str(CACHE_DIR) + "/recordings") + 68if not REC_DIR.exists(): + 69 REC_DIR.mkdir(parents=True, exist_ok=True) + 70 + 71# Transcribed audio cache directory. + 72PERSIST_DIR: Path = Path(str(CACHE_DIR) + "/chroma") + 73if not PERSIST_DIR.exists(): + 74 PERSIST_DIR.mkdir(parents=True, exist_ok=True) + 75 + 76# RAG Directory + 77RAG_DIR: Path = Path(str(CACHE_DIR) + "/rag") + 78if not RAG_DIR.exists(): + 79 RAG_DIR.mkdir(parents=True, exist_ok=True) + 80 81 - 82 - 83CacheEntry = namedtuple("CacheEntry", ["key", "expires"]) + 82CacheEntry = namedtuple("CacheEntry", ["key", "expires"]) + 83 84 - 85 - 86class CacheService(metaclass=Singleton): - 87 """Provide a cache service for previously used queries, audio generation, etc...""" - 88 - 89 INSTANCE: "CacheService" + 85class CacheService(metaclass=Singleton): + 86 """Provide a cache service for previously used queries, audio generation, and other recurring operations. This + 87 class is designed to store and retrieve cached data efficiently, reducing the need for repeated processing and + 88 enhancing performance. + 89 """ 90 - 91 ASKAI_CACHE_KEYS: str = "askai-cache-keys" + 91 INSTANCE: "CacheService" 92 - 93 ASKAI_INPUT_CACHE_KEY: str = "askai-input-history" + 93 ASKAI_CACHE_KEYS: str = "askai-cache-keys" 94 - 95 ASKAI_CONTEXT_KEY: str = "askai-context-key" + 95 ASKAI_INPUT_CACHE_KEY: str = "askai-input-history" 96 - 97 # ASKAI_CONTEXT + 97 ASKAI_CONTEXT_KEY: str = "askai-context-key" 98 99 _TTL_CACHE: TTLCache[str] = TTLCache(ttl_minutes=configs.ttl) 100 @@ -271,81 +271,98 @@

    106 def keys(self) -> set[str]: 107 return self._cache_keys 108 -109 def audio_file_path(self, text: str, voice: str = "onyx", audio_format: str = "mp3") -> Tuple[str, bool]: -110 """Retrieve the hashed audio file path and whether the file exists or not. -111 :param text: Text to be cached. This is the text that the audio is speaking. -112 :param voice: The AI voice used to STT. -113 :param audio_format: The audio file format. -114 """ -115 key = f"{text.strip().lower()}-{hash_text(voice)}" -116 audio_file_path = f"{str(AUDIO_DIR)}/askai-{hash_text(key)}.{audio_format}" -117 return audio_file_path, file_is_not_empty(audio_file_path) -118 -119 def save_reply(self, text: str, reply: str) -> Optional[str]: -120 """Save a AI reply into the TTL cache. -121 :param text: Text to be cached. -122 :param reply: The reply associated to this text. -123 """ -124 if configs.is_cache: -125 key = text.strip().lower() -126 self._TTL_CACHE.save(key, reply) -127 self.keys.add(key) -128 self._TTL_CACHE.save(self.ASKAI_CACHE_KEYS, ",".join(self._cache_keys)) -129 return text -130 return None -131 -132 def read_reply(self, text: str) -> Optional[str]: -133 """Read AI replies from TTL cache. -134 :param text: Text to be cached. -135 """ -136 if configs.is_cache: -137 key = text.strip().lower() -138 return self._TTL_CACHE.read(key) -139 return None -140 -141 def del_reply(self, text: str) -> Optional[str]: -142 """Delete AI replies from TTL cache. -143 :param text: Text to be deleted. -144 """ -145 if configs.is_cache: -146 key = text.strip().lower() -147 self._TTL_CACHE.delete(key) -148 self.keys.remove(key) -149 self._TTL_CACHE.save(self.ASKAI_CACHE_KEYS, ",".join(self._cache_keys)) -150 return text -151 return None -152 -153 def clear_replies(self) -> list[str]: -154 """Clear all cached replies.""" -155 return list(map(self.del_reply, sorted(self.keys))) +109 def audio_file_path(self, text: str, voice: str = "onyx", audio_format: str = "mp3") -> tuple[str, bool]: +110 """Retrieve the hashed audio file path and determine whether the file already exists. +111 :param text: The text that the audio represents. +112 :param voice: The AI voice used for speech synthesis (default is "onyx"). +113 :param audio_format: The audio file format (default is "mp3"). +114 :return: A tuple containing the hashed file path as a string and a boolean indicating if the file exists. +115 """ +116 key = f"{text.strip().lower()}-{hash_text(voice)}" +117 audio_file_path = f"{str(AUDIO_DIR)}/askai-{hash_text(key)}.{audio_format}" +118 return audio_file_path, file_is_not_empty(audio_file_path) +119 +120 def save_reply(self, text: str, reply: str) -> Optional[str]: +121 """Save an AI reply into the TTL (Time-To-Live) cache. +122 :param text: The text to be cached. +123 :param reply: The AI reply associated with this text. +124 :return: The key under which the reply is saved, or None if the save operation fails. +125 """ +126 if configs.is_cache: +127 key = text.strip().lower() +128 self._TTL_CACHE.save(key, reply) +129 self.keys.add(key) +130 self._TTL_CACHE.save(self.ASKAI_CACHE_KEYS, ",".join(self._cache_keys)) +131 return key +132 return None +133 +134 def read_reply(self, text: str) -> Optional[str]: +135 """Retrieve AI replies from the TTL (Time-To-Live) cache. +136 :param text: The text key to look up in the cache. +137 :return: The cached reply associated with the text, or None if not found. +138 """ +139 if configs.is_cache: +140 key = text.strip().lower() +141 return self._TTL_CACHE.read(key) +142 return None +143 +144 def del_reply(self, text: str) -> Optional[str]: +145 """Delete an AI reply from the TTL (Time-To-Live) cache. +146 :param text: The text key whose associated reply is to be deleted from the cache. +147 :return: The deleted reply if it existed, or None if no reply was found. +148 """ +149 if configs.is_cache: +150 key = text.strip().lower() +151 self._TTL_CACHE.delete(key) +152 self.keys.remove(key) +153 self._TTL_CACHE.save(self.ASKAI_CACHE_KEYS, ",".join(self._cache_keys)) +154 return text +155 return None 156 -157 def read_input_history(self) -> list[str]: -158 """Read the input queries from TTL cache.""" -159 hist_str: str = self._TTL_CACHE.read(self.ASKAI_INPUT_CACHE_KEY) -160 return hist_str.split(",") if hist_str else [] -161 -162 def save_input_history(self, history: list[str] = None) -> None: -163 """Save the line input queries into the TTL cache.""" -164 self._TTL_CACHE.save(self.ASKAI_INPUT_CACHE_KEY, ",".join(history or KeyboardInput.history())) -165 -166 def load_input_history(self, predefined: list[str] = None) -> list[str]: -167 """Load the suggester with user input history.""" -168 history = self.read_input_history() -169 if predefined: -170 history.extend(list(filter(lambda c: c not in history, predefined))) -171 return history -172 -173 def save_context(self, context: list[str]) -> None: -174 """Save the Context window entries into the TTL cache.""" -175 self._TTL_CACHE.save(self.ASKAI_CONTEXT_KEY, "%EOL%".join(context)) +157 def clear_replies(self) -> list[str]: +158 """Clear all cached replies. +159 :return: A list of keys for the replies that were deleted from the cache. +160 """ +161 return list(map(self.del_reply, sorted(self.keys))) +162 +163 def read_input_history(self) -> list[str]: +164 """Retrieve line input queries from the TTL (Time-To-Live) cache. +165 :return: A list of input queries stored in the cache. +166 """ +167 hist_str: str = self._TTL_CACHE.read(self.ASKAI_INPUT_CACHE_KEY) +168 return hist_str.split(",") if hist_str else [] +169 +170 def save_input_history(self, history: list[str] = None) -> str: +171 """Save input queries into the TTL (Time-To-Live) cache. +172 :param history: A list of input queries to be saved. If None, the current input history will be saved. +173 :return: The temporary file name of the saved entry. +174 """ +175 return self._TTL_CACHE.save(self.ASKAI_INPUT_CACHE_KEY, ",".join(history or KeyboardInput.history())) 176 -177 def read_context(self) -> list[str]: -178 """Read the Context window entries from the TTL cache.""" -179 ctx_str: str = self._TTL_CACHE.read(self.ASKAI_CONTEXT_KEY) -180 return re.split(r'%EOL%', ctx_str, flags=re.MULTILINE | re.IGNORECASE) if ctx_str else [] -181 -182 -183assert (cache := CacheService().INSTANCE) is not None +177 def load_input_history(self, predefined: list[str] = None) -> list[str]: +178 """Load input queries from the TTL (Time-To-Live) cache extending it with a predefined input history. +179 :param predefined: A list of predefined input queries to be appended to the final list. +180 :return: A list of input queries loaded from the cache. +181 """ +182 history = self.read_input_history() +183 if predefined: +184 history.extend(list(filter(lambda c: c not in history, predefined))) +185 return history +186 +187 def save_context(self, context: list[str]) -> None: +188 """Save the context window entries into the TTL (Time-To-Live) cache. +189 :param context: A list of context entries to be saved. +190 """ +191 self._TTL_CACHE.save(self.ASKAI_CONTEXT_KEY, "%EOL%".join(context)) +192 +193 def read_context(self) -> list[str]: +194 """Read the context window entries from the TTL (Time-To-Live) cache. +195 :return: A list of context entries retrieved from the cache.""" +196 ctx_str: str = self._TTL_CACHE.read(self.ASKAI_CONTEXT_KEY) +197 return re.split(r"%EOL%", ctx_str, flags=re.MULTILINE | re.IGNORECASE) if ctx_str else [] +198 +199 +200assert (cache := CacheService().INSTANCE) is not None

    @@ -558,18 +575,19 @@

    Inherited Members
    -
     87class CacheService(metaclass=Singleton):
    - 88    """Provide a cache service for previously used queries, audio generation, etc..."""
    - 89
    - 90    INSTANCE: "CacheService"
    +            
     86class CacheService(metaclass=Singleton):
    + 87    """Provide a cache service for previously used queries, audio generation, and other recurring operations. This
    + 88    class is designed to store and retrieve cached data efficiently, reducing the need for repeated processing and
    + 89    enhancing performance.
    + 90    """
      91
    - 92    ASKAI_CACHE_KEYS: str = "askai-cache-keys"
    + 92    INSTANCE: "CacheService"
      93
    - 94    ASKAI_INPUT_CACHE_KEY: str = "askai-input-history"
    + 94    ASKAI_CACHE_KEYS: str = "askai-cache-keys"
      95
    - 96    ASKAI_CONTEXT_KEY: str = "askai-context-key"
    + 96    ASKAI_INPUT_CACHE_KEY: str = "askai-input-history"
      97
    - 98    # ASKAI_CONTEXT
    + 98    ASKAI_CONTEXT_KEY: str = "askai-context-key"
      99
     100    _TTL_CACHE: TTLCache[str] = TTLCache(ttl_minutes=configs.ttl)
     101
    @@ -581,82 +599,101 @@ 
    Inherited Members
    107 def keys(self) -> set[str]: 108 return self._cache_keys 109 -110 def audio_file_path(self, text: str, voice: str = "onyx", audio_format: str = "mp3") -> Tuple[str, bool]: -111 """Retrieve the hashed audio file path and whether the file exists or not. -112 :param text: Text to be cached. This is the text that the audio is speaking. -113 :param voice: The AI voice used to STT. -114 :param audio_format: The audio file format. -115 """ -116 key = f"{text.strip().lower()}-{hash_text(voice)}" -117 audio_file_path = f"{str(AUDIO_DIR)}/askai-{hash_text(key)}.{audio_format}" -118 return audio_file_path, file_is_not_empty(audio_file_path) -119 -120 def save_reply(self, text: str, reply: str) -> Optional[str]: -121 """Save a AI reply into the TTL cache. -122 :param text: Text to be cached. -123 :param reply: The reply associated to this text. -124 """ -125 if configs.is_cache: -126 key = text.strip().lower() -127 self._TTL_CACHE.save(key, reply) -128 self.keys.add(key) -129 self._TTL_CACHE.save(self.ASKAI_CACHE_KEYS, ",".join(self._cache_keys)) -130 return text -131 return None -132 -133 def read_reply(self, text: str) -> Optional[str]: -134 """Read AI replies from TTL cache. -135 :param text: Text to be cached. -136 """ -137 if configs.is_cache: -138 key = text.strip().lower() -139 return self._TTL_CACHE.read(key) -140 return None -141 -142 def del_reply(self, text: str) -> Optional[str]: -143 """Delete AI replies from TTL cache. -144 :param text: Text to be deleted. -145 """ -146 if configs.is_cache: -147 key = text.strip().lower() -148 self._TTL_CACHE.delete(key) -149 self.keys.remove(key) -150 self._TTL_CACHE.save(self.ASKAI_CACHE_KEYS, ",".join(self._cache_keys)) -151 return text -152 return None -153 -154 def clear_replies(self) -> list[str]: -155 """Clear all cached replies.""" -156 return list(map(self.del_reply, sorted(self.keys))) +110 def audio_file_path(self, text: str, voice: str = "onyx", audio_format: str = "mp3") -> tuple[str, bool]: +111 """Retrieve the hashed audio file path and determine whether the file already exists. +112 :param text: The text that the audio represents. +113 :param voice: The AI voice used for speech synthesis (default is "onyx"). +114 :param audio_format: The audio file format (default is "mp3"). +115 :return: A tuple containing the hashed file path as a string and a boolean indicating if the file exists. +116 """ +117 key = f"{text.strip().lower()}-{hash_text(voice)}" +118 audio_file_path = f"{str(AUDIO_DIR)}/askai-{hash_text(key)}.{audio_format}" +119 return audio_file_path, file_is_not_empty(audio_file_path) +120 +121 def save_reply(self, text: str, reply: str) -> Optional[str]: +122 """Save an AI reply into the TTL (Time-To-Live) cache. +123 :param text: The text to be cached. +124 :param reply: The AI reply associated with this text. +125 :return: The key under which the reply is saved, or None if the save operation fails. +126 """ +127 if configs.is_cache: +128 key = text.strip().lower() +129 self._TTL_CACHE.save(key, reply) +130 self.keys.add(key) +131 self._TTL_CACHE.save(self.ASKAI_CACHE_KEYS, ",".join(self._cache_keys)) +132 return key +133 return None +134 +135 def read_reply(self, text: str) -> Optional[str]: +136 """Retrieve AI replies from the TTL (Time-To-Live) cache. +137 :param text: The text key to look up in the cache. +138 :return: The cached reply associated with the text, or None if not found. +139 """ +140 if configs.is_cache: +141 key = text.strip().lower() +142 return self._TTL_CACHE.read(key) +143 return None +144 +145 def del_reply(self, text: str) -> Optional[str]: +146 """Delete an AI reply from the TTL (Time-To-Live) cache. +147 :param text: The text key whose associated reply is to be deleted from the cache. +148 :return: The deleted reply if it existed, or None if no reply was found. +149 """ +150 if configs.is_cache: +151 key = text.strip().lower() +152 self._TTL_CACHE.delete(key) +153 self.keys.remove(key) +154 self._TTL_CACHE.save(self.ASKAI_CACHE_KEYS, ",".join(self._cache_keys)) +155 return text +156 return None 157 -158 def read_input_history(self) -> list[str]: -159 """Read the input queries from TTL cache.""" -160 hist_str: str = self._TTL_CACHE.read(self.ASKAI_INPUT_CACHE_KEY) -161 return hist_str.split(",") if hist_str else [] -162 -163 def save_input_history(self, history: list[str] = None) -> None: -164 """Save the line input queries into the TTL cache.""" -165 self._TTL_CACHE.save(self.ASKAI_INPUT_CACHE_KEY, ",".join(history or KeyboardInput.history())) -166 -167 def load_input_history(self, predefined: list[str] = None) -> list[str]: -168 """Load the suggester with user input history.""" -169 history = self.read_input_history() -170 if predefined: -171 history.extend(list(filter(lambda c: c not in history, predefined))) -172 return history -173 -174 def save_context(self, context: list[str]) -> None: -175 """Save the Context window entries into the TTL cache.""" -176 self._TTL_CACHE.save(self.ASKAI_CONTEXT_KEY, "%EOL%".join(context)) +158 def clear_replies(self) -> list[str]: +159 """Clear all cached replies. +160 :return: A list of keys for the replies that were deleted from the cache. +161 """ +162 return list(map(self.del_reply, sorted(self.keys))) +163 +164 def read_input_history(self) -> list[str]: +165 """Retrieve line input queries from the TTL (Time-To-Live) cache. +166 :return: A list of input queries stored in the cache. +167 """ +168 hist_str: str = self._TTL_CACHE.read(self.ASKAI_INPUT_CACHE_KEY) +169 return hist_str.split(",") if hist_str else [] +170 +171 def save_input_history(self, history: list[str] = None) -> str: +172 """Save input queries into the TTL (Time-To-Live) cache. +173 :param history: A list of input queries to be saved. If None, the current input history will be saved. +174 :return: The temporary file name of the saved entry. +175 """ +176 return self._TTL_CACHE.save(self.ASKAI_INPUT_CACHE_KEY, ",".join(history or KeyboardInput.history())) 177 -178 def read_context(self) -> list[str]: -179 """Read the Context window entries from the TTL cache.""" -180 ctx_str: str = self._TTL_CACHE.read(self.ASKAI_CONTEXT_KEY) -181 return re.split(r'%EOL%', ctx_str, flags=re.MULTILINE | re.IGNORECASE) if ctx_str else [] +178 def load_input_history(self, predefined: list[str] = None) -> list[str]: +179 """Load input queries from the TTL (Time-To-Live) cache extending it with a predefined input history. +180 :param predefined: A list of predefined input queries to be appended to the final list. +181 :return: A list of input queries loaded from the cache. +182 """ +183 history = self.read_input_history() +184 if predefined: +185 history.extend(list(filter(lambda c: c not in history, predefined))) +186 return history +187 +188 def save_context(self, context: list[str]) -> None: +189 """Save the context window entries into the TTL (Time-To-Live) cache. +190 :param context: A list of context entries to be saved. +191 """ +192 self._TTL_CACHE.save(self.ASKAI_CONTEXT_KEY, "%EOL%".join(context)) +193 +194 def read_context(self) -> list[str]: +195 """Read the context window entries from the TTL (Time-To-Live) cache. +196 :return: A list of context entries retrieved from the cache.""" +197 ctx_str: str = self._TTL_CACHE.read(self.ASKAI_CONTEXT_KEY) +198 return re.split(r"%EOL%", ctx_str, flags=re.MULTILINE | re.IGNORECASE) if ctx_str else []
    -

    Provide a cache service for previously used queries, audio generation, etc...

    +

    Provide a cache service for previously used queries, audio generation, and other recurring operations. This +class is designed to store and retrieve cached data efficiently, reducing the need for repeated processing and +enhancing performance.

    @@ -761,33 +798,40 @@
    Inherited Members
    def - audio_file_path( self, text: str, voice: str = 'onyx', audio_format: str = 'mp3') -> Tuple[str, bool]: + audio_file_path( self, text: str, voice: str = 'onyx', audio_format: str = 'mp3') -> tuple[str, bool]:
    -
    110    def audio_file_path(self, text: str, voice: str = "onyx", audio_format: str = "mp3") -> Tuple[str, bool]:
    -111        """Retrieve the hashed audio file path and whether the file exists or not.
    -112        :param text: Text to be cached. This is the text that the audio is speaking.
    -113        :param voice: The AI voice used to STT.
    -114        :param audio_format: The audio file format.
    -115        """
    -116        key = f"{text.strip().lower()}-{hash_text(voice)}"
    -117        audio_file_path = f"{str(AUDIO_DIR)}/askai-{hash_text(key)}.{audio_format}"
    -118        return audio_file_path, file_is_not_empty(audio_file_path)
    +            
    110    def audio_file_path(self, text: str, voice: str = "onyx", audio_format: str = "mp3") -> tuple[str, bool]:
    +111        """Retrieve the hashed audio file path and determine whether the file already exists.
    +112        :param text: The text that the audio represents.
    +113        :param voice: The AI voice used for speech synthesis (default is "onyx").
    +114        :param audio_format: The audio file format (default is "mp3").
    +115        :return: A tuple containing the hashed file path as a string and a boolean indicating if the file exists.
    +116        """
    +117        key = f"{text.strip().lower()}-{hash_text(voice)}"
    +118        audio_file_path = f"{str(AUDIO_DIR)}/askai-{hash_text(key)}.{audio_format}"
    +119        return audio_file_path, file_is_not_empty(audio_file_path)
     
    -

    Retrieve the hashed audio file path and whether the file exists or not.

    +

    Retrieve the hashed audio file path and determine whether the file already exists.

    Parameters
      -
    • text: Text to be cached. This is the text that the audio is speaking.
    • -
    • voice: The AI voice used to STT.
    • -
    • audio_format: The audio file format.
    • +
    • text: The text that the audio represents.
    • +
    • voice: The AI voice used for speech synthesis (default is "onyx").
    • +
    • audio_format: The audio file format (default is "mp3").
    + +
    Returns
    + +
    +

    A tuple containing the hashed file path as a string and a boolean indicating if the file exists.

    +
    @@ -803,29 +847,36 @@
    Parameters
    -
    120    def save_reply(self, text: str, reply: str) -> Optional[str]:
    -121        """Save a AI reply into the TTL cache.
    -122        :param text: Text to be cached.
    -123        :param reply: The reply associated to this text.
    -124        """
    -125        if configs.is_cache:
    -126            key = text.strip().lower()
    -127            self._TTL_CACHE.save(key, reply)
    -128            self.keys.add(key)
    -129            self._TTL_CACHE.save(self.ASKAI_CACHE_KEYS, ",".join(self._cache_keys))
    -130            return text
    -131        return None
    +            
    121    def save_reply(self, text: str, reply: str) -> Optional[str]:
    +122        """Save an AI reply into the TTL (Time-To-Live) cache.
    +123        :param text: The text to be cached.
    +124        :param reply: The AI reply associated with this text.
    +125        :return: The key under which the reply is saved, or None if the save operation fails.
    +126        """
    +127        if configs.is_cache:
    +128            key = text.strip().lower()
    +129            self._TTL_CACHE.save(key, reply)
    +130            self.keys.add(key)
    +131            self._TTL_CACHE.save(self.ASKAI_CACHE_KEYS, ",".join(self._cache_keys))
    +132            return key
    +133        return None
     
    -

    Save a AI reply into the TTL cache.

    +

    Save an AI reply into the TTL (Time-To-Live) cache.

    Parameters
      -
    • text: Text to be cached.
    • -
    • reply: The reply associated to this text.
    • +
    • text: The text to be cached.
    • +
    • reply: The AI reply associated with this text.
    + +
    Returns
    + +
    +

    The key under which the reply is saved, or None if the save operation fails.

    +
    @@ -841,24 +892,31 @@
    Parameters
    -
    133    def read_reply(self, text: str) -> Optional[str]:
    -134        """Read AI replies from TTL cache.
    -135        :param text: Text to be cached.
    -136        """
    -137        if configs.is_cache:
    -138            key = text.strip().lower()
    -139            return self._TTL_CACHE.read(key)
    -140        return None
    +            
    135    def read_reply(self, text: str) -> Optional[str]:
    +136        """Retrieve AI replies from the TTL (Time-To-Live) cache.
    +137        :param text: The text key to look up in the cache.
    +138        :return: The cached reply associated with the text, or None if not found.
    +139        """
    +140        if configs.is_cache:
    +141            key = text.strip().lower()
    +142            return self._TTL_CACHE.read(key)
    +143        return None
     
    -

    Read AI replies from TTL cache.

    +

    Retrieve AI replies from the TTL (Time-To-Live) cache.

    Parameters
      -
    • text: Text to be cached.
    • +
    • text: The text key to look up in the cache.
    + +
    Returns
    + +
    +

    The cached reply associated with the text, or None if not found.

    +
    @@ -874,27 +932,34 @@
    Parameters
    -
    142    def del_reply(self, text: str) -> Optional[str]:
    -143        """Delete AI replies from TTL cache.
    -144        :param text: Text to be deleted.
    -145        """
    -146        if configs.is_cache:
    -147            key = text.strip().lower()
    -148            self._TTL_CACHE.delete(key)
    -149            self.keys.remove(key)
    -150            self._TTL_CACHE.save(self.ASKAI_CACHE_KEYS, ",".join(self._cache_keys))
    -151            return text
    -152        return None
    +            
    145    def del_reply(self, text: str) -> Optional[str]:
    +146        """Delete an AI reply from the TTL (Time-To-Live) cache.
    +147        :param text: The text key whose associated reply is to be deleted from the cache.
    +148        :return: The deleted reply if it existed, or None if no reply was found.
    +149        """
    +150        if configs.is_cache:
    +151            key = text.strip().lower()
    +152            self._TTL_CACHE.delete(key)
    +153            self.keys.remove(key)
    +154            self._TTL_CACHE.save(self.ASKAI_CACHE_KEYS, ",".join(self._cache_keys))
    +155            return text
    +156        return None
     
    -

    Delete AI replies from TTL cache.

    +

    Delete an AI reply from the TTL (Time-To-Live) cache.

    Parameters
      -
    • text: Text to be deleted.
    • +
    • text: The text key whose associated reply is to be deleted from the cache.
    + +
    Returns
    + +
    +

    The deleted reply if it existed, or None if no reply was found.

    +
    @@ -910,13 +975,21 @@
    Parameters
    -
    154    def clear_replies(self) -> list[str]:
    -155        """Clear all cached replies."""
    -156        return list(map(self.del_reply, sorted(self.keys)))
    +            
    158    def clear_replies(self) -> list[str]:
    +159        """Clear all cached replies.
    +160        :return: A list of keys for the replies that were deleted from the cache.
    +161        """
    +162        return list(map(self.del_reply, sorted(self.keys)))
     

    Clear all cached replies.

    + +
    Returns
    + +
    +

    A list of keys for the replies that were deleted from the cache.

    +
    @@ -932,14 +1005,22 @@
    Parameters
    -
    158    def read_input_history(self) -> list[str]:
    -159        """Read the input queries from TTL cache."""
    -160        hist_str: str = self._TTL_CACHE.read(self.ASKAI_INPUT_CACHE_KEY)
    -161        return hist_str.split(",") if hist_str else []
    +            
    164    def read_input_history(self) -> list[str]:
    +165        """Retrieve line input queries from the TTL (Time-To-Live) cache.
    +166        :return: A list of input queries stored in the cache.
    +167        """
    +168        hist_str: str = self._TTL_CACHE.read(self.ASKAI_INPUT_CACHE_KEY)
    +169        return hist_str.split(",") if hist_str else []
     
    -

    Read the input queries from TTL cache.

    +

    Retrieve line input queries from the TTL (Time-To-Live) cache.

    + +
    Returns
    + +
    +

    A list of input queries stored in the cache.

    +
    @@ -949,19 +1030,34 @@
    Parameters
    def - save_input_history(self, history: list[str] = None) -> None: + save_input_history(self, history: list[str] = None) -> str:
    -
    163    def save_input_history(self, history: list[str] = None) -> None:
    -164        """Save the line input queries into the TTL cache."""
    -165        self._TTL_CACHE.save(self.ASKAI_INPUT_CACHE_KEY, ",".join(history or KeyboardInput.history()))
    +            
    171    def save_input_history(self, history: list[str] = None) -> str:
    +172        """Save input queries into the TTL (Time-To-Live) cache.
    +173        :param history: A list of input queries to be saved. If None, the current input history will be saved.
    +174        :return: The temporary file name of the saved entry.
    +175        """
    +176        return self._TTL_CACHE.save(self.ASKAI_INPUT_CACHE_KEY, ",".join(history or KeyboardInput.history()))
     
    -

    Save the line input queries into the TTL cache.

    +

    Save input queries into the TTL (Time-To-Live) cache.

    + +
    Parameters
    + +
      +
    • history: A list of input queries to be saved. If None, the current input history will be saved.
    • +
    + +
    Returns
    + +
    +

    The temporary file name of the saved entry.

    +
    @@ -977,16 +1073,31 @@
    Parameters
    -
    167    def load_input_history(self, predefined: list[str] = None) -> list[str]:
    -168        """Load the suggester with user input history."""
    -169        history = self.read_input_history()
    -170        if predefined:
    -171            history.extend(list(filter(lambda c: c not in history, predefined)))
    -172        return history
    +            
    178    def load_input_history(self, predefined: list[str] = None) -> list[str]:
    +179        """Load input queries from the TTL (Time-To-Live) cache extending it with a predefined input history.
    +180        :param predefined: A list of predefined input queries to be appended to the final list.
    +181        :return: A list of input queries loaded from the cache.
    +182        """
    +183        history = self.read_input_history()
    +184        if predefined:
    +185            history.extend(list(filter(lambda c: c not in history, predefined)))
    +186        return history
     
    -

    Load the suggester with user input history.

    +

    Load input queries from the TTL (Time-To-Live) cache extending it with a predefined input history.

    + +
    Parameters
    + +
      +
    • predefined: A list of predefined input queries to be appended to the final list.
    • +
    + +
    Returns
    + +
    +

    A list of input queries loaded from the cache.

    +
    @@ -1002,13 +1113,21 @@
    Parameters
    -
    174    def save_context(self, context: list[str]) -> None:
    -175        """Save the Context window entries into the TTL cache."""
    -176        self._TTL_CACHE.save(self.ASKAI_CONTEXT_KEY, "%EOL%".join(context))
    +            
    188    def save_context(self, context: list[str]) -> None:
    +189        """Save the context window entries into the TTL (Time-To-Live) cache.
    +190        :param context: A list of context entries to be saved.
    +191        """
    +192        self._TTL_CACHE.save(self.ASKAI_CONTEXT_KEY, "%EOL%".join(context))
     
    -

    Save the Context window entries into the TTL cache.

    +

    Save the context window entries into the TTL (Time-To-Live) cache.

    + +
    Parameters
    + +
      +
    • context: A list of context entries to be saved.
    • +
    @@ -1024,14 +1143,21 @@
    Parameters
    -
    178    def read_context(self) -> list[str]:
    -179        """Read the Context window entries from the TTL cache."""
    -180        ctx_str: str = self._TTL_CACHE.read(self.ASKAI_CONTEXT_KEY)
    -181        return re.split(r'%EOL%', ctx_str, flags=re.MULTILINE | re.IGNORECASE) if ctx_str else []
    +            
    194    def read_context(self) -> list[str]:
    +195        """Read the context window entries from the TTL (Time-To-Live) cache.
    +196        :return: A list of context entries retrieved from the cache."""
    +197        ctx_str: str = self._TTL_CACHE.read(self.ASKAI_CONTEXT_KEY)
    +198        return re.split(r"%EOL%", ctx_str, flags=re.MULTILINE | re.IGNORECASE) if ctx_str else []
     
    -

    Read the Context window entries from the TTL cache.

    +

    Read the context window entries from the TTL (Time-To-Live) cache.

    + +
    Returns
    + +
    +

    A list of context entries retrieved from the cache.

    +
    diff --git a/docs/api/askai/main/askai/core/component/camera.html b/docs/api/askai/main/askai/core/component/camera.html index c3567608..05ae2351 100644 --- a/docs/api/askai/main/askai/core/component/camera.html +++ b/docs/api/askai/main/askai/core/component/camera.html @@ -3,7 +3,7 @@ - + main.askai.core.component.camera API documentation @@ -114,219 +114,246 @@

    12 13 Copyright (c) 2024, HomeSetup 14""" - 15import atexit - 16import glob - 17import logging as log - 18import os.path - 19import shutil - 20from os.path import basename - 21from pathlib import Path - 22from typing import Optional, TypeAlias - 23 - 24import cv2 - 25import pause - 26from hspylib.core.metaclass.classpath import AnyPath - 27from hspylib.core.metaclass.singleton import Singleton - 28from hspylib.core.tools.text_tools import hash_text - 29from hspylib.core.zoned_datetime import now_ms - 30from retry import retry - 31from torchvision.datasets.folder import is_image_file - 32 - 33from askai.__classpath__ import classpath - 34from askai.core.askai_configs import configs - 35from askai.core.askai_events import events - 36from askai.core.askai_messages import msg - 37from askai.core.component.cache_service import FACE_DIR, PHOTO_DIR, IMG_IMPORTS_DIR - 38from askai.core.component.image_store import ImageData, ImageFile, ImageMetadata, store - 39from askai.core.features.router.tools.vision import image_captioner - 40from askai.core.support.utilities import build_img_path - 41from askai.exception.exceptions import WebCamInitializationFailure, CameraAccessFailure - 42 - 43InputDevice: TypeAlias = tuple[int, str] - 44 + 15from askai.__classpath__ import classpath + 16from askai.core.askai_configs import configs + 17from askai.core.askai_events import events + 18from askai.core.askai_messages import msg + 19from askai.core.component.audio_player import player + 20from askai.core.component.cache_service import FACE_DIR, IMG_IMPORTS_DIR, PHOTO_DIR + 21from askai.core.component.image_store import ImageData, ImageFile, ImageMetadata, store + 22from askai.core.features.tools.vision import image_captioner, parse_caption + 23from askai.core.model.ai_reply import AIReply + 24from askai.core.model.image_result import ImageResult + 25from askai.core.support.utilities import build_img_path + 26from askai.exception.exceptions import CameraAccessFailure, WebCamInitializationFailure + 27from hspylib.core.metaclass.classpath import AnyPath + 28from hspylib.core.metaclass.singleton import Singleton + 29from hspylib.core.tools.dict_tools import get_or_default + 30from hspylib.core.tools.text_tools import hash_text + 31from hspylib.core.zoned_datetime import now_ms + 32from os.path import basename + 33from pathlib import Path + 34from retry import retry + 35from torchvision.datasets.folder import is_image_file + 36from typing import Optional, TypeAlias + 37 + 38import atexit + 39import cv2 + 40import glob + 41import logging as log + 42import os.path + 43import pause + 44import shutil 45 - 46class Camera(metaclass=Singleton): - 47 """Provide an interface to capture WebCam photos.""" + 46InputDevice: TypeAlias = tuple[int, str] + 47 48 - 49 INSTANCE: "Camera" - 50 - 51 RESOURCE_DIR: Path = classpath.resource_path() - 52 - 53 ALG: str = configs.face_detect_alg - 54 - 55 @staticmethod - 56 def _countdown(count: int) -> None: - 57 """Display a countdown before taking a photo.""" - 58 if i := count: - 59 events.reply.emit(message=msg.smile(i)) - 60 while i: - 61 events.reply.emit(message=msg.smile(i), erase_last=True) - 62 pause.seconds(1) - 63 i -= 1 - 64 - 65 def __init__(self): - 66 self._cam = None - 67 self._alg_path: Path = Path.joinpath(self.RESOURCE_DIR, self.ALG) - 68 self._face_classifier = cv2.CascadeClassifier(str(self._alg_path)) - 69 - 70 @retry(tries=3, backoff=1, delay=1) - 71 def initialize(self) -> None: - 72 """Initialize the camera device.""" - 73 if not self._cam or not self._cam.isOpened(): - 74 self._cam = cv2.VideoCapture(0) - 75 ret, img = self._cam.read() - 76 log.info("Starting the WebCam device") - 77 if not (ret and img is not None): - 78 raise WebCamInitializationFailure("Failed to initialize the WebCam device !") - 79 else: - 80 atexit.register(self.shutdown) - 81 - 82 @retry(tries=3, backoff=1, delay=1) - 83 def shutdown(self) -> None: - 84 """Shutdown the camera device.""" - 85 if self._cam and self._cam.isOpened(): - 86 log.info("Shutting down the WebCam device") - 87 self._cam.release() - 88 - 89 def capture( - 90 self, - 91 filename: AnyPath, - 92 countdown: int = 3, - 93 with_caption: bool = True, - 94 store_image: bool = True - 95 ) -> Optional[tuple[ImageFile, ImageData]]: - 96 """Capture a WebCam frame (take a photo).""" - 97 - 98 self.initialize() - 99 -100 if not self._cam.isOpened(): -101 events.reply_error.emit(message=msg.camera_not_open()) -102 return None -103 -104 self._countdown(countdown) -105 -106 ret, photo = self._cam.read() -107 if not ret: -108 raise CameraAccessFailure("Failed to take a photo from WebCam!") + 49class Camera(metaclass=Singleton): + 50 """Provide an interface to interact with the webcam. This class offers methods for controlling and accessing the + 51 webcam's functionality, ensuring that only one instance interacts with the hardware at a time. + 52 """ + 53 + 54 INSTANCE: "Camera" + 55 + 56 RESOURCE_DIR: Path = classpath.resource_path() + 57 + 58 # Face-Detection algorithm to be used + 59 ALG: str = configs.face_detect_alg + 60 + 61 @staticmethod + 62 def _countdown(count: int) -> None: + 63 """Display a countdown before taking a photo. + 64 :param count: The number of seconds for the countdown. + 65 """ + 66 if i := count: + 67 events.reply.emit(reply=AIReply.mute(msg.smile(i))) + 68 while (i := (i - 1)) >= 0: + 69 player.play_sfx("click") + 70 pause.seconds(1) + 71 events.reply.emit(reply=AIReply.mute(msg.smile(i)), erase_last=True) + 72 player.play_sfx("camera-shutter") + 73 events.reply.emit(reply=AIReply.mute(" !!!Click!!!"), erase_last=True) + 74 + 75 def __init__(self): + 76 self._cam = None + 77 self._alg_path: Path = Path.joinpath(self.RESOURCE_DIR, self.ALG) + 78 self._face_classifier = cv2.CascadeClassifier(str(self._alg_path)) + 79 + 80 @retry(tries=3, backoff=1, delay=1) + 81 def initialize(self) -> None: + 82 """Initialize the camera device.""" + 83 if not self._cam or not self._cam.isOpened(): + 84 self._cam = cv2.VideoCapture(0) + 85 ret, img = self._cam.read() + 86 log.info("Starting the WebCam device") + 87 if not (ret and img is not None): + 88 raise WebCamInitializationFailure("Failed to initialize the WebCam device !") + 89 else: + 90 atexit.register(self.shutdown) + 91 + 92 @retry(tries=3, backoff=1, delay=1) + 93 def shutdown(self) -> None: + 94 """Shutdown the camera device.""" + 95 if self._cam and self._cam.isOpened(): + 96 log.info("Shutting down the WebCam device") + 97 self._cam.release() + 98 + 99 def capture( +100 self, filename: AnyPath, countdown: int = 3, with_caption: bool = True, store_image: bool = True +101 ) -> Optional[tuple[ImageFile, ImageData]]: +102 """Capture a webcam frame (take a photo). +103 :param filename: The file name for the capturing image. +104 :param countdown: The number of seconds for the countdown before capturing the photo (default is 3). +105 :param with_caption: Whether to generate a caption for the captured image (default is True). +106 :param store_image: Whether to save the captured image to the image store (default is True). +107 :return: A tuple containing the image file and image data, or None if the capture fails. +108 """ 109 -110 filename: str = filename or str(now_ms()) -111 final_path: str = build_img_path(PHOTO_DIR, str(filename), '-PHOTO.jpg') -112 if final_path and cv2.imwrite(final_path, photo): -113 photo_file = ImageFile( -114 hash_text(basename(final_path)), final_path, store.PHOTO_CATEGORY, -115 image_captioner(final_path) if with_caption else msg.no_caption() -116 ) -117 if store_image: -118 store.store_image(photo_file) -119 events.reply.emit(message=msg.photo_captured(photo_file.img_path), verbosity="debug") -120 return photo_file, photo +110 self.initialize() +111 +112 if not self._cam.isOpened(): +113 events.reply.emit(reply=AIReply.error(msg.camera_not_open())) +114 return None +115 +116 self._countdown(countdown) +117 +118 ret, photo = self._cam.read() +119 if not ret: +120 raise CameraAccessFailure("Failed to take a photo from WebCam!") 121 -122 return None -123 -124 def detect_faces( -125 self, -126 photo: ImageData, -127 filename: AnyPath = None, -128 with_caption: bool = True, -129 store_image: bool = True -130 ) -> tuple[list[ImageFile], list[ImageData]]: -131 """Detect all faces in the photo.""" -132 -133 face_files: list[ImageFile] = [] -134 face_datas: list[ImageData] = [] -135 gray_img = cv2.cvtColor(photo, cv2.COLOR_BGR2GRAY) -136 faces = self._face_classifier.detectMultiScale( -137 gray_img, -138 scaleFactor=configs.scale_factor, -139 minNeighbors=configs.min_neighbors, -140 minSize=configs.min_max_size, -141 maxSize=configs.min_max_size -142 ) -143 log.debug("Detected faces: %d", len(faces)) -144 -145 if len(faces) == 0: -146 return face_files, face_datas -147 -148 for x, y, w, h in faces: -149 cropped_face: ImageData = photo[y: y + h, x: x + w] -150 final_path: str = build_img_path(FACE_DIR, str(filename), f'-FACE-{len(face_files)}.jpg') -151 if final_path and cv2.imwrite(final_path, cropped_face): -152 face_file = ImageFile( -153 hash_text(basename(final_path)), final_path, store.FACE_CATEGORY, -154 image_captioner(final_path) if with_caption else msg.no_caption() -155 ) -156 face_files.append(face_file) -157 face_datas.append(cropped_face) -158 log.debug("Face file successfully saved: '%s' !", final_path) -159 else: -160 raise CameraAccessFailure(f"Failed to save face file: '{final_path}'!") -161 -162 if store_image: -163 store.store_image(*face_files) -164 -165 return face_files, face_datas -166 -167 def identify(self, max_distance: float = configs.max_id_distance) -> Optional[ImageMetadata]: -168 """Identify the person in front of the WebCam.""" -169 -170 _, photo = self.capture("ASKAI-ID", 0, False, False) -171 _ = self.detect_faces(photo, "ASKAI-ID", False, False) -172 result = list(filter(lambda p: p.distance <= max_distance, store.search_face(photo))) -173 -174 return next(iter(result), None) -175 -176 def import_images( -177 self, -178 pathname: AnyPath, -179 detect_faces: bool = False, -180 with_caption: bool = True, -181 store_image: bool = True -182 ) -> tuple[int, ...]: -183 """Import image files into the image collection.""" -184 -185 img_datas: list[ImageData] = [] -186 img_files: list[ImageFile] = [] -187 faces: list[ImageFile] = [] -188 -189 def _import_file(src_path: str) -> str: -190 dest_path: str = os.path.join(IMG_IMPORTS_DIR, basename(src_path)) -191 shutil.copyfile(src_path, dest_path) -192 return dest_path -193 -194 def _read_file(img_path: str) -> ImageFile: -195 return ImageFile( -196 hash_text(basename(img_path)), -197 img_path, store.IMPORTS_CATEGORY, -198 image_captioner(img_path) if with_caption else msg.no_caption() -199 ) -200 -201 def _do_import(*img_path: str) -> None: -202 log.debug("Importing images: %s", img_path) -203 import_paths: list[str] = list(map(_import_file, img_path)) -204 list(map(img_files.append, map(_read_file, import_paths))) -205 list(map(img_datas.append, map(cv2.imread, import_paths))) -206 -207 if os.path.isfile(pathname): -208 _do_import(pathname) -209 elif os.path.isdir(pathname): -210 _do_import(*list(filter(is_image_file, glob.glob(os.path.join(pathname, "*.*"))))) -211 else: -212 _do_import(*glob.glob(pathname)) -213 -214 if img_files: -215 if store_image: -216 store.store_image(*img_files) -217 if detect_faces: -218 data: ImageData -219 file: ImageFile -220 for data, file in zip(img_datas, img_files): -221 face_file, _ = self.detect_faces(data, file.img_path, with_caption, store_image) -222 faces.extend(face_file) -223 -224 return len(img_files), len(faces) -225 -226 -227assert (camera := Camera().INSTANCE) is not None +122 filename: str = filename or str(now_ms()) +123 final_path: str = build_img_path(PHOTO_DIR, str(filename), "-PHOTO.jpg") +124 if final_path and cv2.imwrite(final_path, photo): +125 log.debug("WebCam photo taken: %s", final_path) +126 photo_file = ImageFile( +127 hash_text(basename(final_path)), +128 final_path, +129 store.PHOTO_CATEGORY, +130 parse_caption(image_captioner(final_path)) if with_caption else msg.no_caption(), +131 ) +132 if store_image: +133 store.store_image(photo_file) +134 events.reply.emit(reply=AIReply.debug(msg.photo_captured(photo_file.img_path))) +135 return photo_file, photo +136 +137 return None +138 +139 def detect_faces( +140 self, photo: ImageData, filename: AnyPath = None, with_caption: bool = True, store_image: bool = True +141 ) -> tuple[list[ImageFile], list[ImageData]]: +142 """Detect all faces in the provided photo. +143 :param photo: The image data in which to detect faces. +144 :param filename: The file name for the detected face image.(optional). +145 :param with_caption: Whether to generate captions for the detected faces (default is True). +146 :param store_image: Whether to save the processed images to the image store (default is True). +147 :return: A tuple containing a list of image files and a list of image data for the detected faces. +148 """ +149 +150 face_files: list[ImageFile] = [] +151 face_datas: list[ImageData] = [] +152 gray_img = cv2.cvtColor(photo, cv2.COLOR_BGR2GRAY) +153 faces = self._face_classifier.detectMultiScale( +154 gray_img, scaleFactor=configs.scale_factor, minNeighbors=configs.min_neighbors, minSize=configs.min_max_size +155 ) +156 log.debug("Detected faces: %d", len(faces)) +157 +158 if len(faces) == 0: +159 return face_files, face_datas +160 +161 filename: str = filename or str(now_ms()) +162 for x, y, w, h in faces: +163 cropped_face: ImageData = photo[y : y + h, x : x + w] +164 final_path: str = build_img_path(FACE_DIR, str(filename), f"-FACE-{len(face_files)}.jpg") +165 if final_path and cv2.imwrite(final_path, cropped_face): +166 result: ImageResult = ImageResult.of(image_captioner(final_path)) +167 face_file = ImageFile( +168 hash_text(basename(final_path)), +169 final_path, +170 store.FACE_CATEGORY, +171 get_or_default(result.people_description, 0, "<N/A>") if with_caption else msg.no_caption(), +172 ) +173 face_files.append(face_file) +174 face_datas.append(cropped_face) +175 log.debug("Face file successfully saved: '%s' !", final_path) +176 else: +177 raise CameraAccessFailure(f"Failed to save face file: '{final_path}'!") +178 +179 if store_image: +180 store.store_image(*face_files) +181 +182 return face_files, face_datas +183 +184 def identify(self, countdown: int = 0, max_distance: float = configs.max_id_distance) -> Optional[ImageMetadata]: +185 """Identify the person in front of the webcam. +186 :param countdown: The number of seconds for the countdown before capturing an identification the photo +187 (default is 0). +188 :param max_distance: The maximum allowable distance for face recognition accuracy (default is +189 configs.max_id_distance). +190 :return: Metadata about the identified person, or None if identification fails. +191 """ +192 _, photo = self.capture("ASKAI-ID", countdown, False, False) +193 _ = self.detect_faces(photo, "ASKAI-ID", False, False) +194 result = list(filter(lambda p: p.distance <= max_distance, store.find_by_similarity(photo))) +195 id_data: ImageMetadata = next(iter(result), None) +196 log.info("WebCam identification request: %s", id_data or "<No-One>") +197 +198 return id_data +199 +200 def import_images( +201 self, pathname: AnyPath, detect_faces: bool = False, with_caption: bool = True, store_image: bool = True +202 ) -> tuple[int, ...]: +203 """Import image files into the image collection. +204 :param pathname: The path or glob pattern of the images to be imported. +205 :param detect_faces: Whether to detect faces in the imported images (default is False). +206 :param with_caption: Whether to generate captions for the imported images (default is True). +207 :param store_image: Whether to save the processed images to the image store (default is True). +208 :return: A tuple containing the number of images successfully imported, and the number of detected faces. +209 """ +210 +211 img_datas: list[ImageData] = [] +212 img_files: list[ImageFile] = [] +213 faces: list[ImageFile] = [] +214 +215 def _import_file(src_path: str) -> str: +216 dest_path: str = os.path.join(IMG_IMPORTS_DIR, basename(src_path)) +217 shutil.copyfile(src_path, dest_path) +218 return dest_path +219 +220 def _read_file(img_path: str) -> ImageFile: +221 return ImageFile( +222 hash_text(basename(img_path)), +223 img_path, +224 store.IMPORTS_CATEGORY, +225 parse_caption(image_captioner(img_path)), +226 ) +227 +228 def _do_import(*img_path: str) -> None: +229 log.debug("Importing images: %s", img_path) +230 import_paths: list[str] = list(map(_import_file, img_path)) +231 list(map(img_files.append, map(_read_file, import_paths))) +232 list(map(img_datas.append, map(cv2.imread, import_paths))) +233 +234 if os.path.isfile(pathname): +235 _do_import(pathname) +236 elif os.path.isdir(pathname): +237 _do_import(*list(filter(is_image_file, glob.glob(os.path.join(pathname, "*.*"))))) +238 else: +239 _do_import(*glob.glob(pathname)) +240 +241 if img_files: +242 if store_image: +243 store.store_image(*img_files) +244 if detect_faces: +245 data: ImageData +246 file: ImageFile +247 for data, file in zip(img_datas, img_files): +248 face_file, _ = self.detect_faces(data, file.img_path, with_caption, store_image) +249 faces.extend(face_file) +250 +251 return len(img_files), len(faces) +252 +253 +254assert (camera := Camera().INSTANCE) is not None

    @@ -354,189 +381,214 @@

    -
     47class Camera(metaclass=Singleton):
    - 48    """Provide an interface to capture WebCam photos."""
    - 49
    - 50    INSTANCE: "Camera"
    - 51
    - 52    RESOURCE_DIR: Path = classpath.resource_path()
    - 53
    - 54    ALG: str = configs.face_detect_alg
    - 55
    - 56    @staticmethod
    - 57    def _countdown(count: int) -> None:
    - 58        """Display a countdown before taking a photo."""
    - 59        if i := count:
    - 60            events.reply.emit(message=msg.smile(i))
    - 61            while i:
    - 62                events.reply.emit(message=msg.smile(i), erase_last=True)
    - 63                pause.seconds(1)
    - 64                i -= 1
    - 65
    - 66    def __init__(self):
    - 67        self._cam = None
    - 68        self._alg_path: Path = Path.joinpath(self.RESOURCE_DIR, self.ALG)
    - 69        self._face_classifier = cv2.CascadeClassifier(str(self._alg_path))
    - 70
    - 71    @retry(tries=3, backoff=1, delay=1)
    - 72    def initialize(self) -> None:
    - 73        """Initialize the camera device."""
    - 74        if not self._cam or not self._cam.isOpened():
    - 75            self._cam = cv2.VideoCapture(0)
    - 76            ret, img = self._cam.read()
    - 77            log.info("Starting the WebCam device")
    - 78            if not (ret and img is not None):
    - 79                raise WebCamInitializationFailure("Failed to initialize the WebCam device !")
    - 80            else:
    - 81                atexit.register(self.shutdown)
    - 82
    - 83    @retry(tries=3, backoff=1, delay=1)
    - 84    def shutdown(self) -> None:
    - 85        """Shutdown the camera device."""
    - 86        if self._cam and self._cam.isOpened():
    - 87            log.info("Shutting down the WebCam device")
    - 88            self._cam.release()
    - 89
    - 90    def capture(
    - 91        self,
    - 92        filename: AnyPath,
    - 93        countdown: int = 3,
    - 94        with_caption: bool = True,
    - 95        store_image: bool = True
    - 96    ) -> Optional[tuple[ImageFile, ImageData]]:
    - 97        """Capture a WebCam frame (take a photo)."""
    - 98
    - 99        self.initialize()
    -100
    -101        if not self._cam.isOpened():
    -102            events.reply_error.emit(message=msg.camera_not_open())
    -103            return None
    -104
    -105        self._countdown(countdown)
    -106
    -107        ret, photo = self._cam.read()
    -108        if not ret:
    -109            raise CameraAccessFailure("Failed to take a photo from WebCam!")
    +            
     50class Camera(metaclass=Singleton):
    + 51    """Provide an interface to interact with the webcam. This class offers methods for controlling and accessing the
    + 52    webcam's functionality, ensuring that only one instance interacts with the hardware at a time.
    + 53    """
    + 54
    + 55    INSTANCE: "Camera"
    + 56
    + 57    RESOURCE_DIR: Path = classpath.resource_path()
    + 58
    + 59    # Face-Detection algorithm to be used
    + 60    ALG: str = configs.face_detect_alg
    + 61
    + 62    @staticmethod
    + 63    def _countdown(count: int) -> None:
    + 64        """Display a countdown before taking a photo.
    + 65        :param count: The number of seconds for the countdown.
    + 66        """
    + 67        if i := count:
    + 68            events.reply.emit(reply=AIReply.mute(msg.smile(i)))
    + 69            while (i := (i - 1)) >= 0:
    + 70                player.play_sfx("click")
    + 71                pause.seconds(1)
    + 72                events.reply.emit(reply=AIReply.mute(msg.smile(i)), erase_last=True)
    + 73            player.play_sfx("camera-shutter")
    + 74            events.reply.emit(reply=AIReply.mute("  !!!Click!!!"), erase_last=True)
    + 75
    + 76    def __init__(self):
    + 77        self._cam = None
    + 78        self._alg_path: Path = Path.joinpath(self.RESOURCE_DIR, self.ALG)
    + 79        self._face_classifier = cv2.CascadeClassifier(str(self._alg_path))
    + 80
    + 81    @retry(tries=3, backoff=1, delay=1)
    + 82    def initialize(self) -> None:
    + 83        """Initialize the camera device."""
    + 84        if not self._cam or not self._cam.isOpened():
    + 85            self._cam = cv2.VideoCapture(0)
    + 86            ret, img = self._cam.read()
    + 87            log.info("Starting the WebCam device")
    + 88            if not (ret and img is not None):
    + 89                raise WebCamInitializationFailure("Failed to initialize the WebCam device !")
    + 90            else:
    + 91                atexit.register(self.shutdown)
    + 92
    + 93    @retry(tries=3, backoff=1, delay=1)
    + 94    def shutdown(self) -> None:
    + 95        """Shutdown the camera device."""
    + 96        if self._cam and self._cam.isOpened():
    + 97            log.info("Shutting down the WebCam device")
    + 98            self._cam.release()
    + 99
    +100    def capture(
    +101        self, filename: AnyPath, countdown: int = 3, with_caption: bool = True, store_image: bool = True
    +102    ) -> Optional[tuple[ImageFile, ImageData]]:
    +103        """Capture a webcam frame (take a photo).
    +104        :param filename: The file name for the capturing image.
    +105        :param countdown: The number of seconds for the countdown before capturing the photo (default is 3).
    +106        :param with_caption: Whether to generate a caption for the captured image (default is True).
    +107        :param store_image: Whether to save the captured image to the image store (default is True).
    +108        :return: A tuple containing the image file and image data, or None if the capture fails.
    +109        """
     110
    -111        filename: str = filename or str(now_ms())
    -112        final_path: str = build_img_path(PHOTO_DIR, str(filename), '-PHOTO.jpg')
    -113        if final_path and cv2.imwrite(final_path, photo):
    -114            photo_file = ImageFile(
    -115                hash_text(basename(final_path)), final_path, store.PHOTO_CATEGORY,
    -116                image_captioner(final_path) if with_caption else msg.no_caption()
    -117            )
    -118            if store_image:
    -119                store.store_image(photo_file)
    -120            events.reply.emit(message=msg.photo_captured(photo_file.img_path), verbosity="debug")
    -121            return photo_file, photo
    +111        self.initialize()
    +112
    +113        if not self._cam.isOpened():
    +114            events.reply.emit(reply=AIReply.error(msg.camera_not_open()))
    +115            return None
    +116
    +117        self._countdown(countdown)
    +118
    +119        ret, photo = self._cam.read()
    +120        if not ret:
    +121            raise CameraAccessFailure("Failed to take a photo from WebCam!")
     122
    -123        return None
    -124
    -125    def detect_faces(
    -126        self,
    -127        photo: ImageData,
    -128        filename: AnyPath = None,
    -129        with_caption: bool = True,
    -130        store_image: bool = True
    -131    ) -> tuple[list[ImageFile], list[ImageData]]:
    -132        """Detect all faces in the photo."""
    -133
    -134        face_files: list[ImageFile] = []
    -135        face_datas: list[ImageData] = []
    -136        gray_img = cv2.cvtColor(photo, cv2.COLOR_BGR2GRAY)
    -137        faces = self._face_classifier.detectMultiScale(
    -138            gray_img,
    -139            scaleFactor=configs.scale_factor,
    -140            minNeighbors=configs.min_neighbors,
    -141            minSize=configs.min_max_size,
    -142            maxSize=configs.min_max_size
    -143        )
    -144        log.debug("Detected faces: %d", len(faces))
    -145
    -146        if len(faces) == 0:
    -147            return face_files, face_datas
    -148
    -149        for x, y, w, h in faces:
    -150            cropped_face: ImageData = photo[y: y + h, x: x + w]
    -151            final_path: str = build_img_path(FACE_DIR, str(filename), f'-FACE-{len(face_files)}.jpg')
    -152            if final_path and cv2.imwrite(final_path, cropped_face):
    -153                face_file = ImageFile(
    -154                    hash_text(basename(final_path)), final_path, store.FACE_CATEGORY,
    -155                    image_captioner(final_path) if with_caption else msg.no_caption()
    -156                )
    -157                face_files.append(face_file)
    -158                face_datas.append(cropped_face)
    -159                log.debug("Face file successfully saved: '%s' !", final_path)
    -160            else:
    -161                raise CameraAccessFailure(f"Failed to save face file: '{final_path}'!")
    -162
    -163        if store_image:
    -164            store.store_image(*face_files)
    -165
    -166        return face_files, face_datas
    -167
    -168    def identify(self, max_distance: float = configs.max_id_distance) -> Optional[ImageMetadata]:
    -169        """Identify the person in front of the WebCam."""
    -170
    -171        _, photo = self.capture("ASKAI-ID", 0, False, False)
    -172        _ = self.detect_faces(photo, "ASKAI-ID", False, False)
    -173        result = list(filter(lambda p: p.distance <= max_distance, store.search_face(photo)))
    -174
    -175        return next(iter(result), None)
    -176
    -177    def import_images(
    -178        self,
    -179        pathname: AnyPath,
    -180        detect_faces: bool = False,
    -181        with_caption: bool = True,
    -182        store_image: bool = True
    -183    ) -> tuple[int, ...]:
    -184        """Import image files into the image collection."""
    -185
    -186        img_datas: list[ImageData] = []
    -187        img_files: list[ImageFile] = []
    -188        faces: list[ImageFile] = []
    -189
    -190        def _import_file(src_path: str) -> str:
    -191            dest_path: str = os.path.join(IMG_IMPORTS_DIR, basename(src_path))
    -192            shutil.copyfile(src_path, dest_path)
    -193            return dest_path
    -194
    -195        def _read_file(img_path: str) -> ImageFile:
    -196            return ImageFile(
    -197                hash_text(basename(img_path)),
    -198                img_path, store.IMPORTS_CATEGORY,
    -199                image_captioner(img_path) if with_caption else msg.no_caption()
    -200            )
    -201
    -202        def _do_import(*img_path: str) -> None:
    -203            log.debug("Importing images: %s", img_path)
    -204            import_paths: list[str] = list(map(_import_file, img_path))
    -205            list(map(img_files.append, map(_read_file, import_paths)))
    -206            list(map(img_datas.append, map(cv2.imread, import_paths)))
    -207
    -208        if os.path.isfile(pathname):
    -209            _do_import(pathname)
    -210        elif os.path.isdir(pathname):
    -211            _do_import(*list(filter(is_image_file, glob.glob(os.path.join(pathname, "*.*")))))
    -212        else:
    -213            _do_import(*glob.glob(pathname))
    -214
    -215        if img_files:
    -216            if store_image:
    -217                store.store_image(*img_files)
    -218            if detect_faces:
    -219                data: ImageData
    -220                file: ImageFile
    -221                for data, file in zip(img_datas, img_files):
    -222                    face_file, _ = self.detect_faces(data, file.img_path, with_caption, store_image)
    -223                    faces.extend(face_file)
    -224
    -225        return len(img_files), len(faces)
    +123        filename: str = filename or str(now_ms())
    +124        final_path: str = build_img_path(PHOTO_DIR, str(filename), "-PHOTO.jpg")
    +125        if final_path and cv2.imwrite(final_path, photo):
    +126            log.debug("WebCam photo taken: %s", final_path)
    +127            photo_file = ImageFile(
    +128                hash_text(basename(final_path)),
    +129                final_path,
    +130                store.PHOTO_CATEGORY,
    +131                parse_caption(image_captioner(final_path)) if with_caption else msg.no_caption(),
    +132            )
    +133            if store_image:
    +134                store.store_image(photo_file)
    +135            events.reply.emit(reply=AIReply.debug(msg.photo_captured(photo_file.img_path)))
    +136            return photo_file, photo
    +137
    +138        return None
    +139
    +140    def detect_faces(
    +141        self, photo: ImageData, filename: AnyPath = None, with_caption: bool = True, store_image: bool = True
    +142    ) -> tuple[list[ImageFile], list[ImageData]]:
    +143        """Detect all faces in the provided photo.
    +144        :param photo: The image data in which to detect faces.
    +145        :param filename: The file name for the detected face image.(optional).
    +146        :param with_caption: Whether to generate captions for the detected faces (default is True).
    +147        :param store_image: Whether to save the processed images to the image store (default is True).
    +148        :return: A tuple containing a list of image files and a list of image data for the detected faces.
    +149        """
    +150
    +151        face_files: list[ImageFile] = []
    +152        face_datas: list[ImageData] = []
    +153        gray_img = cv2.cvtColor(photo, cv2.COLOR_BGR2GRAY)
    +154        faces = self._face_classifier.detectMultiScale(
    +155            gray_img, scaleFactor=configs.scale_factor, minNeighbors=configs.min_neighbors, minSize=configs.min_max_size
    +156        )
    +157        log.debug("Detected faces: %d", len(faces))
    +158
    +159        if len(faces) == 0:
    +160            return face_files, face_datas
    +161
    +162        filename: str = filename or str(now_ms())
    +163        for x, y, w, h in faces:
    +164            cropped_face: ImageData = photo[y : y + h, x : x + w]
    +165            final_path: str = build_img_path(FACE_DIR, str(filename), f"-FACE-{len(face_files)}.jpg")
    +166            if final_path and cv2.imwrite(final_path, cropped_face):
    +167                result: ImageResult = ImageResult.of(image_captioner(final_path))
    +168                face_file = ImageFile(
    +169                    hash_text(basename(final_path)),
    +170                    final_path,
    +171                    store.FACE_CATEGORY,
    +172                    get_or_default(result.people_description, 0, "<N/A>") if with_caption else msg.no_caption(),
    +173                )
    +174                face_files.append(face_file)
    +175                face_datas.append(cropped_face)
    +176                log.debug("Face file successfully saved: '%s' !", final_path)
    +177            else:
    +178                raise CameraAccessFailure(f"Failed to save face file: '{final_path}'!")
    +179
    +180        if store_image:
    +181            store.store_image(*face_files)
    +182
    +183        return face_files, face_datas
    +184
    +185    def identify(self, countdown: int = 0, max_distance: float = configs.max_id_distance) -> Optional[ImageMetadata]:
    +186        """Identify the person in front of the webcam.
    +187        :param countdown: The number of seconds for the countdown before capturing an identification the photo
    +188                          (default is 0).
    +189        :param max_distance: The maximum allowable distance for face recognition accuracy (default is
    +190                          configs.max_id_distance).
    +191        :return: Metadata about the identified person, or None if identification fails.
    +192        """
    +193        _, photo = self.capture("ASKAI-ID", countdown, False, False)
    +194        _ = self.detect_faces(photo, "ASKAI-ID", False, False)
    +195        result = list(filter(lambda p: p.distance <= max_distance, store.find_by_similarity(photo)))
    +196        id_data: ImageMetadata = next(iter(result), None)
    +197        log.info("WebCam identification request: %s", id_data or "<No-One>")
    +198
    +199        return id_data
    +200
    +201    def import_images(
    +202        self, pathname: AnyPath, detect_faces: bool = False, with_caption: bool = True, store_image: bool = True
    +203    ) -> tuple[int, ...]:
    +204        """Import image files into the image collection.
    +205        :param pathname: The path or glob pattern of the images to be imported.
    +206        :param detect_faces: Whether to detect faces in the imported images (default is False).
    +207        :param with_caption: Whether to generate captions for the imported images (default is True).
    +208        :param store_image: Whether to save the processed images to the image store (default is True).
    +209        :return: A tuple containing the number of images successfully imported, and the number of detected faces.
    +210        """
    +211
    +212        img_datas: list[ImageData] = []
    +213        img_files: list[ImageFile] = []
    +214        faces: list[ImageFile] = []
    +215
    +216        def _import_file(src_path: str) -> str:
    +217            dest_path: str = os.path.join(IMG_IMPORTS_DIR, basename(src_path))
    +218            shutil.copyfile(src_path, dest_path)
    +219            return dest_path
    +220
    +221        def _read_file(img_path: str) -> ImageFile:
    +222            return ImageFile(
    +223                hash_text(basename(img_path)),
    +224                img_path,
    +225                store.IMPORTS_CATEGORY,
    +226                parse_caption(image_captioner(img_path)),
    +227            )
    +228
    +229        def _do_import(*img_path: str) -> None:
    +230            log.debug("Importing images: %s", img_path)
    +231            import_paths: list[str] = list(map(_import_file, img_path))
    +232            list(map(img_files.append, map(_read_file, import_paths)))
    +233            list(map(img_datas.append, map(cv2.imread, import_paths)))
    +234
    +235        if os.path.isfile(pathname):
    +236            _do_import(pathname)
    +237        elif os.path.isdir(pathname):
    +238            _do_import(*list(filter(is_image_file, glob.glob(os.path.join(pathname, "*.*")))))
    +239        else:
    +240            _do_import(*glob.glob(pathname))
    +241
    +242        if img_files:
    +243            if store_image:
    +244                store.store_image(*img_files)
    +245            if detect_faces:
    +246                data: ImageData
    +247                file: ImageFile
    +248                for data, file in zip(img_datas, img_files):
    +249                    face_file, _ = self.detect_faces(data, file.img_path, with_caption, store_image)
    +250                    faces.extend(face_file)
    +251
    +252        return len(img_files), len(faces)
     
    -

    Provide an interface to capture WebCam photos.

    +

    Provide an interface to interact with the webcam. This class offers methods for controlling and accessing the +webcam's functionality, ensuring that only one instance interacts with the hardware at a time.

    @@ -618,17 +670,17 @@

    -
    71    @retry(tries=3, backoff=1, delay=1)
    -72    def initialize(self) -> None:
    -73        """Initialize the camera device."""
    -74        if not self._cam or not self._cam.isOpened():
    -75            self._cam = cv2.VideoCapture(0)
    -76            ret, img = self._cam.read()
    -77            log.info("Starting the WebCam device")
    -78            if not (ret and img is not None):
    -79                raise WebCamInitializationFailure("Failed to initialize the WebCam device !")
    -80            else:
    -81                atexit.register(self.shutdown)
    +            
    81    @retry(tries=3, backoff=1, delay=1)
    +82    def initialize(self) -> None:
    +83        """Initialize the camera device."""
    +84        if not self._cam or not self._cam.isOpened():
    +85            self._cam = cv2.VideoCapture(0)
    +86            ret, img = self._cam.read()
    +87            log.info("Starting the WebCam device")
    +88            if not (ret and img is not None):
    +89                raise WebCamInitializationFailure("Failed to initialize the WebCam device !")
    +90            else:
    +91                atexit.register(self.shutdown)
     
    @@ -649,12 +701,12 @@

    -
    83    @retry(tries=3, backoff=1, delay=1)
    -84    def shutdown(self) -> None:
    -85        """Shutdown the camera device."""
    -86        if self._cam and self._cam.isOpened():
    -87            log.info("Shutting down the WebCam device")
    -88            self._cam.release()
    +            
    93    @retry(tries=3, backoff=1, delay=1)
    +94    def shutdown(self) -> None:
    +95        """Shutdown the camera device."""
    +96        if self._cam and self._cam.isOpened():
    +97            log.info("Shutting down the WebCam device")
    +98            self._cam.release()
     
    @@ -674,44 +726,64 @@

    -
     90    def capture(
    - 91        self,
    - 92        filename: AnyPath,
    - 93        countdown: int = 3,
    - 94        with_caption: bool = True,
    - 95        store_image: bool = True
    - 96    ) -> Optional[tuple[ImageFile, ImageData]]:
    - 97        """Capture a WebCam frame (take a photo)."""
    - 98
    - 99        self.initialize()
    -100
    -101        if not self._cam.isOpened():
    -102            events.reply_error.emit(message=msg.camera_not_open())
    -103            return None
    -104
    -105        self._countdown(countdown)
    -106
    -107        ret, photo = self._cam.read()
    -108        if not ret:
    -109            raise CameraAccessFailure("Failed to take a photo from WebCam!")
    +            
    100    def capture(
    +101        self, filename: AnyPath, countdown: int = 3, with_caption: bool = True, store_image: bool = True
    +102    ) -> Optional[tuple[ImageFile, ImageData]]:
    +103        """Capture a webcam frame (take a photo).
    +104        :param filename: The file name for the capturing image.
    +105        :param countdown: The number of seconds for the countdown before capturing the photo (default is 3).
    +106        :param with_caption: Whether to generate a caption for the captured image (default is True).
    +107        :param store_image: Whether to save the captured image to the image store (default is True).
    +108        :return: A tuple containing the image file and image data, or None if the capture fails.
    +109        """
     110
    -111        filename: str = filename or str(now_ms())
    -112        final_path: str = build_img_path(PHOTO_DIR, str(filename), '-PHOTO.jpg')
    -113        if final_path and cv2.imwrite(final_path, photo):
    -114            photo_file = ImageFile(
    -115                hash_text(basename(final_path)), final_path, store.PHOTO_CATEGORY,
    -116                image_captioner(final_path) if with_caption else msg.no_caption()
    -117            )
    -118            if store_image:
    -119                store.store_image(photo_file)
    -120            events.reply.emit(message=msg.photo_captured(photo_file.img_path), verbosity="debug")
    -121            return photo_file, photo
    +111        self.initialize()
    +112
    +113        if not self._cam.isOpened():
    +114            events.reply.emit(reply=AIReply.error(msg.camera_not_open()))
    +115            return None
    +116
    +117        self._countdown(countdown)
    +118
    +119        ret, photo = self._cam.read()
    +120        if not ret:
    +121            raise CameraAccessFailure("Failed to take a photo from WebCam!")
     122
    -123        return None
    +123        filename: str = filename or str(now_ms())
    +124        final_path: str = build_img_path(PHOTO_DIR, str(filename), "-PHOTO.jpg")
    +125        if final_path and cv2.imwrite(final_path, photo):
    +126            log.debug("WebCam photo taken: %s", final_path)
    +127            photo_file = ImageFile(
    +128                hash_text(basename(final_path)),
    +129                final_path,
    +130                store.PHOTO_CATEGORY,
    +131                parse_caption(image_captioner(final_path)) if with_caption else msg.no_caption(),
    +132            )
    +133            if store_image:
    +134                store.store_image(photo_file)
    +135            events.reply.emit(reply=AIReply.debug(msg.photo_captured(photo_file.img_path)))
    +136            return photo_file, photo
    +137
    +138        return None
     
    -

    Capture a WebCam frame (take a photo).

    +

    Capture a webcam frame (take a photo).

    + +
    Parameters
    + +
      +
    • filename: The file name for the capturing image.
    • +
    • countdown: The number of seconds for the countdown before capturing the photo (default is 3).
    • +
    • with_caption: Whether to generate a caption for the captured image (default is True).
    • +
    • store_image: Whether to save the captured image to the image store (default is True).
    • +
    + +
    Returns
    + +
    +

    A tuple containing the image file and image data, or None if the capture fails.

    +
    @@ -727,52 +799,69 @@

    -
    125    def detect_faces(
    -126        self,
    -127        photo: ImageData,
    -128        filename: AnyPath = None,
    -129        with_caption: bool = True,
    -130        store_image: bool = True
    -131    ) -> tuple[list[ImageFile], list[ImageData]]:
    -132        """Detect all faces in the photo."""
    -133
    -134        face_files: list[ImageFile] = []
    -135        face_datas: list[ImageData] = []
    -136        gray_img = cv2.cvtColor(photo, cv2.COLOR_BGR2GRAY)
    -137        faces = self._face_classifier.detectMultiScale(
    -138            gray_img,
    -139            scaleFactor=configs.scale_factor,
    -140            minNeighbors=configs.min_neighbors,
    -141            minSize=configs.min_max_size,
    -142            maxSize=configs.min_max_size
    -143        )
    -144        log.debug("Detected faces: %d", len(faces))
    -145
    -146        if len(faces) == 0:
    -147            return face_files, face_datas
    -148
    -149        for x, y, w, h in faces:
    -150            cropped_face: ImageData = photo[y: y + h, x: x + w]
    -151            final_path: str = build_img_path(FACE_DIR, str(filename), f'-FACE-{len(face_files)}.jpg')
    -152            if final_path and cv2.imwrite(final_path, cropped_face):
    -153                face_file = ImageFile(
    -154                    hash_text(basename(final_path)), final_path, store.FACE_CATEGORY,
    -155                    image_captioner(final_path) if with_caption else msg.no_caption()
    -156                )
    -157                face_files.append(face_file)
    -158                face_datas.append(cropped_face)
    -159                log.debug("Face file successfully saved: '%s' !", final_path)
    -160            else:
    -161                raise CameraAccessFailure(f"Failed to save face file: '{final_path}'!")
    -162
    -163        if store_image:
    -164            store.store_image(*face_files)
    -165
    -166        return face_files, face_datas
    +            
    140    def detect_faces(
    +141        self, photo: ImageData, filename: AnyPath = None, with_caption: bool = True, store_image: bool = True
    +142    ) -> tuple[list[ImageFile], list[ImageData]]:
    +143        """Detect all faces in the provided photo.
    +144        :param photo: The image data in which to detect faces.
    +145        :param filename: The file name for the detected face image.(optional).
    +146        :param with_caption: Whether to generate captions for the detected faces (default is True).
    +147        :param store_image: Whether to save the processed images to the image store (default is True).
    +148        :return: A tuple containing a list of image files and a list of image data for the detected faces.
    +149        """
    +150
    +151        face_files: list[ImageFile] = []
    +152        face_datas: list[ImageData] = []
    +153        gray_img = cv2.cvtColor(photo, cv2.COLOR_BGR2GRAY)
    +154        faces = self._face_classifier.detectMultiScale(
    +155            gray_img, scaleFactor=configs.scale_factor, minNeighbors=configs.min_neighbors, minSize=configs.min_max_size
    +156        )
    +157        log.debug("Detected faces: %d", len(faces))
    +158
    +159        if len(faces) == 0:
    +160            return face_files, face_datas
    +161
    +162        filename: str = filename or str(now_ms())
    +163        for x, y, w, h in faces:
    +164            cropped_face: ImageData = photo[y : y + h, x : x + w]
    +165            final_path: str = build_img_path(FACE_DIR, str(filename), f"-FACE-{len(face_files)}.jpg")
    +166            if final_path and cv2.imwrite(final_path, cropped_face):
    +167                result: ImageResult = ImageResult.of(image_captioner(final_path))
    +168                face_file = ImageFile(
    +169                    hash_text(basename(final_path)),
    +170                    final_path,
    +171                    store.FACE_CATEGORY,
    +172                    get_or_default(result.people_description, 0, "<N/A>") if with_caption else msg.no_caption(),
    +173                )
    +174                face_files.append(face_file)
    +175                face_datas.append(cropped_face)
    +176                log.debug("Face file successfully saved: '%s' !", final_path)
    +177            else:
    +178                raise CameraAccessFailure(f"Failed to save face file: '{final_path}'!")
    +179
    +180        if store_image:
    +181            store.store_image(*face_files)
    +182
    +183        return face_files, face_datas
     
    -

    Detect all faces in the photo.

    +

    Detect all faces in the provided photo.

    + +
    Parameters
    + +
      +
    • photo: The image data in which to detect faces.
    • +
    • filename: The file name for the detected face image.(optional).
    • +
    • with_caption: Whether to generate captions for the detected faces (default is True).
    • +
    • store_image: Whether to save the processed images to the image store (default is True).
    • +
    + +
    Returns
    + +
    +

    A tuple containing a list of image files and a list of image data for the detected faces.

    +
    @@ -782,24 +871,46 @@

    def - identify( self, max_distance: float = 0.7) -> Optional[askai.core.component.image_store.ImageMetadata]: + identify( self, countdown: int = 0, max_distance: float = 0.7) -> Optional[askai.core.component.image_store.ImageMetadata]:
    -
    168    def identify(self, max_distance: float = configs.max_id_distance) -> Optional[ImageMetadata]:
    -169        """Identify the person in front of the WebCam."""
    -170
    -171        _, photo = self.capture("ASKAI-ID", 0, False, False)
    -172        _ = self.detect_faces(photo, "ASKAI-ID", False, False)
    -173        result = list(filter(lambda p: p.distance <= max_distance, store.search_face(photo)))
    -174
    -175        return next(iter(result), None)
    +            
    185    def identify(self, countdown: int = 0, max_distance: float = configs.max_id_distance) -> Optional[ImageMetadata]:
    +186        """Identify the person in front of the webcam.
    +187        :param countdown: The number of seconds for the countdown before capturing an identification the photo
    +188                          (default is 0).
    +189        :param max_distance: The maximum allowable distance for face recognition accuracy (default is
    +190                          configs.max_id_distance).
    +191        :return: Metadata about the identified person, or None if identification fails.
    +192        """
    +193        _, photo = self.capture("ASKAI-ID", countdown, False, False)
    +194        _ = self.detect_faces(photo, "ASKAI-ID", False, False)
    +195        result = list(filter(lambda p: p.distance <= max_distance, store.find_by_similarity(photo)))
    +196        id_data: ImageMetadata = next(iter(result), None)
    +197        log.info("WebCam identification request: %s", id_data or "<No-One>")
    +198
    +199        return id_data
     
    -

    Identify the person in front of the WebCam.

    +

    Identify the person in front of the webcam.

    + +
    Parameters
    + +
      +
    • countdown: The number of seconds for the countdown before capturing an identification the photo +(default is 0).
    • +
    • max_distance: The maximum allowable distance for face recognition accuracy (default is +configs.max_id_distance).
    • +
    + +
    Returns
    + +
    +

    Metadata about the identified person, or None if identification fails.

    +
    @@ -815,59 +926,77 @@

    -
    177    def import_images(
    -178        self,
    -179        pathname: AnyPath,
    -180        detect_faces: bool = False,
    -181        with_caption: bool = True,
    -182        store_image: bool = True
    -183    ) -> tuple[int, ...]:
    -184        """Import image files into the image collection."""
    -185
    -186        img_datas: list[ImageData] = []
    -187        img_files: list[ImageFile] = []
    -188        faces: list[ImageFile] = []
    -189
    -190        def _import_file(src_path: str) -> str:
    -191            dest_path: str = os.path.join(IMG_IMPORTS_DIR, basename(src_path))
    -192            shutil.copyfile(src_path, dest_path)
    -193            return dest_path
    -194
    -195        def _read_file(img_path: str) -> ImageFile:
    -196            return ImageFile(
    -197                hash_text(basename(img_path)),
    -198                img_path, store.IMPORTS_CATEGORY,
    -199                image_captioner(img_path) if with_caption else msg.no_caption()
    -200            )
    -201
    -202        def _do_import(*img_path: str) -> None:
    -203            log.debug("Importing images: %s", img_path)
    -204            import_paths: list[str] = list(map(_import_file, img_path))
    -205            list(map(img_files.append, map(_read_file, import_paths)))
    -206            list(map(img_datas.append, map(cv2.imread, import_paths)))
    -207
    -208        if os.path.isfile(pathname):
    -209            _do_import(pathname)
    -210        elif os.path.isdir(pathname):
    -211            _do_import(*list(filter(is_image_file, glob.glob(os.path.join(pathname, "*.*")))))
    -212        else:
    -213            _do_import(*glob.glob(pathname))
    -214
    -215        if img_files:
    -216            if store_image:
    -217                store.store_image(*img_files)
    -218            if detect_faces:
    -219                data: ImageData
    -220                file: ImageFile
    -221                for data, file in zip(img_datas, img_files):
    -222                    face_file, _ = self.detect_faces(data, file.img_path, with_caption, store_image)
    -223                    faces.extend(face_file)
    -224
    -225        return len(img_files), len(faces)
    +            
    201    def import_images(
    +202        self, pathname: AnyPath, detect_faces: bool = False, with_caption: bool = True, store_image: bool = True
    +203    ) -> tuple[int, ...]:
    +204        """Import image files into the image collection.
    +205        :param pathname: The path or glob pattern of the images to be imported.
    +206        :param detect_faces: Whether to detect faces in the imported images (default is False).
    +207        :param with_caption: Whether to generate captions for the imported images (default is True).
    +208        :param store_image: Whether to save the processed images to the image store (default is True).
    +209        :return: A tuple containing the number of images successfully imported, and the number of detected faces.
    +210        """
    +211
    +212        img_datas: list[ImageData] = []
    +213        img_files: list[ImageFile] = []
    +214        faces: list[ImageFile] = []
    +215
    +216        def _import_file(src_path: str) -> str:
    +217            dest_path: str = os.path.join(IMG_IMPORTS_DIR, basename(src_path))
    +218            shutil.copyfile(src_path, dest_path)
    +219            return dest_path
    +220
    +221        def _read_file(img_path: str) -> ImageFile:
    +222            return ImageFile(
    +223                hash_text(basename(img_path)),
    +224                img_path,
    +225                store.IMPORTS_CATEGORY,
    +226                parse_caption(image_captioner(img_path)),
    +227            )
    +228
    +229        def _do_import(*img_path: str) -> None:
    +230            log.debug("Importing images: %s", img_path)
    +231            import_paths: list[str] = list(map(_import_file, img_path))
    +232            list(map(img_files.append, map(_read_file, import_paths)))
    +233            list(map(img_datas.append, map(cv2.imread, import_paths)))
    +234
    +235        if os.path.isfile(pathname):
    +236            _do_import(pathname)
    +237        elif os.path.isdir(pathname):
    +238            _do_import(*list(filter(is_image_file, glob.glob(os.path.join(pathname, "*.*")))))
    +239        else:
    +240            _do_import(*glob.glob(pathname))
    +241
    +242        if img_files:
    +243            if store_image:
    +244                store.store_image(*img_files)
    +245            if detect_faces:
    +246                data: ImageData
    +247                file: ImageFile
    +248                for data, file in zip(img_datas, img_files):
    +249                    face_file, _ = self.detect_faces(data, file.img_path, with_caption, store_image)
    +250                    faces.extend(face_file)
    +251
    +252        return len(img_files), len(faces)
     

    Import image files into the image collection.

    + +
    Parameters
    + +
      +
    • pathname: The path or glob pattern of the images to be imported.
    • +
    • detect_faces: Whether to detect faces in the imported images (default is False).
    • +
    • with_caption: Whether to generate captions for the imported images (default is True).
    • +
    • store_image: Whether to save the processed images to the image store (default is True).
    • +
    + +
    Returns
    + +
    +

    A tuple containing the number of images successfully imported, and the number of detected faces.

    +
    diff --git a/docs/api/askai/main/askai/core/component/geo_location.html b/docs/api/askai/main/askai/core/component/geo_location.html index 9d06b325..9603f770 100644 --- a/docs/api/askai/main/askai/core/component/geo_location.html +++ b/docs/api/askai/main/askai/core/component/geo_location.html @@ -3,7 +3,7 @@ - + main.askai.core.component.geo_location API documentation @@ -148,7 +148,7 @@

    28 29 30class GeoLocation(metaclass=Singleton): - 31 """TODO""" + 31 """A class for managing and retrieving geographic location data.""" 32 33 INSTANCE: "GeoLocation" 34 @@ -162,82 +162,84 @@

    42 "isp": "", "org": "", "as": "", "query": "" 43 } 44 """ - 45 ) + 45 ).strip() 46 47 # Date format used in prompts, e.g: Fri 22 Mar 19:47 2024. 48 DATE_FMT: str = "%a %d %b %-H:%M %Y" 49 50 @classmethod 51 def get_location(cls, ip: str = None) -> Namespace: - 52 """TODO""" - 53 try: - 54 url = f"{cls.GEO_LOC_URL}{'/' + ip if ip else ''}" - 55 log.debug("Fetching the Geo Position from: %s", url) - 56 geo_req = fetch.get(url) - 57 except (JSONDecodeError, ConnectionError) as err: - 58 log.error("Failed to retrieve geo location => %s", str(err)) - 59 geo_req = Namespace(body=cls.EMPTY_JSON_RESP) - 60 geo_json = json.loads(geo_req.body) - 61 geo_location: Namespace = Namespace(**geo_json) - 62 return geo_location - 63 - 64 def __init__(self, ip: str = None): - 65 self._geo_location = self.get_location(ip) - 66 self._idiom: str = configs.language.idiom - 67 - 68 def __str__(self): - 69 geo_loc = self._geo_location - 70 geo_loc.setattr("zoned_datetime", self.datetime) - 71 return str(self._geo_location) - 72 - 73 @property - 74 def latitude(self) -> float: - 75 return self._geo_location.lat - 76 - 77 @property - 78 def longitude(self) -> float: - 79 return self._geo_location.lon - 80 - 81 @property - 82 def country(self) -> str: - 83 return self._geo_location.country - 84 - 85 @property - 86 def country_code(self) -> str: - 87 return self._geo_location.countryCode - 88 - 89 @property - 90 def region(self) -> str: - 91 return self._geo_location.region - 92 - 93 @property - 94 def region_name(self) -> str: - 95 return self._geo_location.regionName - 96 - 97 @property - 98 def city(self) -> float: - 99 return self._geo_location.city -100 -101 @property -102 def zip(self) -> str: -103 return self._geo_location.zip -104 -105 @property -106 def timezone(self) -> str: -107 return self._geo_location.timezone -108 -109 @property -110 def location(self) -> str: -111 return f"{self.city}, {self.region_name} {self.country}" -112 -113 @property -114 def datetime(self) -> str: -115 utc_datetime = datetime.utcnow().replace(tzinfo=pytz.utc) -116 zoned_datetime = utc_datetime.astimezone(pytz.timezone(self.timezone)) -117 return zoned_datetime.strftime(self.DATE_FMT) -118 -119 -120assert (geo_location := GeoLocation().INSTANCE) is not None + 52 """Retrieve the geographic location based on the provided IP address. + 53 :param ip: The IP address to locate. If None, the current device's IP address will be used. + 54 :return: A Namespace object containing the geolocation data, such as latitude, longitude, city, and country. + 55 """ + 56 try: + 57 url = f"{cls.GEO_LOC_URL}{'/' + ip if ip else ''}" + 58 log.debug("Fetching the Geo Position from: %s", url) + 59 geo_req = fetch.get(url) + 60 except (JSONDecodeError, ConnectionError) as err: + 61 log.error("Failed to retrieve geo location => %s", str(err)) + 62 geo_req = Namespace(body=cls.EMPTY_JSON_RESP) + 63 geo_loc: Namespace = Namespace(**(json.loads(geo_req.body))) + 64 return geo_loc + 65 + 66 def __init__(self, ip: str = None): + 67 self._geo_location = self.get_location(ip) + 68 self._idiom: str = configs.language.idiom + 69 + 70 def __str__(self): + 71 geo_loc = self._geo_location + 72 geo_loc.setattr("zoned_datetime", self.datetime) + 73 return str(self._geo_location) + 74 + 75 @property + 76 def latitude(self) -> float: + 77 return self._geo_location.lat + 78 + 79 @property + 80 def longitude(self) -> float: + 81 return self._geo_location.lon + 82 + 83 @property + 84 def country(self) -> str: + 85 return self._geo_location.country + 86 + 87 @property + 88 def country_code(self) -> str: + 89 return self._geo_location.countryCode + 90 + 91 @property + 92 def region(self) -> str: + 93 return self._geo_location.region + 94 + 95 @property + 96 def region_name(self) -> str: + 97 return self._geo_location.regionName + 98 + 99 @property +100 def city(self) -> float: +101 return self._geo_location.city +102 +103 @property +104 def zip(self) -> str: +105 return self._geo_location.zip +106 +107 @property +108 def timezone(self) -> str: +109 return self._geo_location.timezone +110 +111 @property +112 def location(self) -> str: +113 return f"{self.city}, {self.region_name} {self.country}" +114 +115 @property +116 def datetime(self) -> str: +117 utc_datetime = datetime.utcnow().replace(tzinfo=pytz.utc) +118 zoned_datetime = utc_datetime.astimezone(pytz.timezone(self.timezone)) +119 return zoned_datetime.strftime(self.DATE_FMT) +120 +121 +122assert (geo_location := GeoLocation().INSTANCE) is not None

    @@ -254,7 +256,7 @@

     31class GeoLocation(metaclass=Singleton):
    - 32    """TODO"""
    + 32    """A class for managing and retrieving geographic location data."""
      33
      34    INSTANCE: "GeoLocation"
      35
    @@ -268,83 +270,85 @@ 

    43 "isp": "", "org": "", "as": "", "query": "" 44 } 45 """ - 46 ) + 46 ).strip() 47 48 # Date format used in prompts, e.g: Fri 22 Mar 19:47 2024. 49 DATE_FMT: str = "%a %d %b %-H:%M %Y" 50 51 @classmethod 52 def get_location(cls, ip: str = None) -> Namespace: - 53 """TODO""" - 54 try: - 55 url = f"{cls.GEO_LOC_URL}{'/' + ip if ip else ''}" - 56 log.debug("Fetching the Geo Position from: %s", url) - 57 geo_req = fetch.get(url) - 58 except (JSONDecodeError, ConnectionError) as err: - 59 log.error("Failed to retrieve geo location => %s", str(err)) - 60 geo_req = Namespace(body=cls.EMPTY_JSON_RESP) - 61 geo_json = json.loads(geo_req.body) - 62 geo_location: Namespace = Namespace(**geo_json) - 63 return geo_location - 64 - 65 def __init__(self, ip: str = None): - 66 self._geo_location = self.get_location(ip) - 67 self._idiom: str = configs.language.idiom - 68 - 69 def __str__(self): - 70 geo_loc = self._geo_location - 71 geo_loc.setattr("zoned_datetime", self.datetime) - 72 return str(self._geo_location) - 73 - 74 @property - 75 def latitude(self) -> float: - 76 return self._geo_location.lat - 77 - 78 @property - 79 def longitude(self) -> float: - 80 return self._geo_location.lon - 81 - 82 @property - 83 def country(self) -> str: - 84 return self._geo_location.country - 85 - 86 @property - 87 def country_code(self) -> str: - 88 return self._geo_location.countryCode - 89 - 90 @property - 91 def region(self) -> str: - 92 return self._geo_location.region - 93 - 94 @property - 95 def region_name(self) -> str: - 96 return self._geo_location.regionName - 97 - 98 @property - 99 def city(self) -> float: -100 return self._geo_location.city -101 -102 @property -103 def zip(self) -> str: -104 return self._geo_location.zip -105 -106 @property -107 def timezone(self) -> str: -108 return self._geo_location.timezone -109 -110 @property -111 def location(self) -> str: -112 return f"{self.city}, {self.region_name} {self.country}" -113 -114 @property -115 def datetime(self) -> str: -116 utc_datetime = datetime.utcnow().replace(tzinfo=pytz.utc) -117 zoned_datetime = utc_datetime.astimezone(pytz.timezone(self.timezone)) -118 return zoned_datetime.strftime(self.DATE_FMT) + 53 """Retrieve the geographic location based on the provided IP address. + 54 :param ip: The IP address to locate. If None, the current device's IP address will be used. + 55 :return: A Namespace object containing the geolocation data, such as latitude, longitude, city, and country. + 56 """ + 57 try: + 58 url = f"{cls.GEO_LOC_URL}{'/' + ip if ip else ''}" + 59 log.debug("Fetching the Geo Position from: %s", url) + 60 geo_req = fetch.get(url) + 61 except (JSONDecodeError, ConnectionError) as err: + 62 log.error("Failed to retrieve geo location => %s", str(err)) + 63 geo_req = Namespace(body=cls.EMPTY_JSON_RESP) + 64 geo_loc: Namespace = Namespace(**(json.loads(geo_req.body))) + 65 return geo_loc + 66 + 67 def __init__(self, ip: str = None): + 68 self._geo_location = self.get_location(ip) + 69 self._idiom: str = configs.language.idiom + 70 + 71 def __str__(self): + 72 geo_loc = self._geo_location + 73 geo_loc.setattr("zoned_datetime", self.datetime) + 74 return str(self._geo_location) + 75 + 76 @property + 77 def latitude(self) -> float: + 78 return self._geo_location.lat + 79 + 80 @property + 81 def longitude(self) -> float: + 82 return self._geo_location.lon + 83 + 84 @property + 85 def country(self) -> str: + 86 return self._geo_location.country + 87 + 88 @property + 89 def country_code(self) -> str: + 90 return self._geo_location.countryCode + 91 + 92 @property + 93 def region(self) -> str: + 94 return self._geo_location.region + 95 + 96 @property + 97 def region_name(self) -> str: + 98 return self._geo_location.regionName + 99 +100 @property +101 def city(self) -> float: +102 return self._geo_location.city +103 +104 @property +105 def zip(self) -> str: +106 return self._geo_location.zip +107 +108 @property +109 def timezone(self) -> str: +110 return self._geo_location.timezone +111 +112 @property +113 def location(self) -> str: +114 return f"{self.city}, {self.region_name} {self.country}" +115 +116 @property +117 def datetime(self) -> str: +118 utc_datetime = datetime.utcnow().replace(tzinfo=pytz.utc) +119 zoned_datetime = utc_datetime.astimezone(pytz.timezone(self.timezone)) +120 return zoned_datetime.strftime(self.DATE_FMT)

    -

    TODO

    +

    A class for managing and retrieving geographic location data.

    @@ -406,7 +410,7 @@

    EMPTY_JSON_RESP: str = - '\n{\n "status": "failure", "country": "", "countryCode": "", "region": "", "regionName": "",\n "city": "", "zip": "", "lat": 0.0, "lon": 0.0, "timezone": "",\n "isp": "", "org": "", "as": "", "query": ""\n}\n' + '{\n "status": "failure", "country": "", "countryCode": "", "region": "", "regionName": "",\n "city": "", "zip": "", "lat": 0.0, "lon": 0.0, "timezone": "",\n "isp": "", "org": "", "as": "", "query": ""\n}'
    @@ -441,21 +445,35 @@

    51    @classmethod
     52    def get_location(cls, ip: str = None) -> Namespace:
    -53        """TODO"""
    -54        try:
    -55            url = f"{cls.GEO_LOC_URL}{'/' + ip if ip else ''}"
    -56            log.debug("Fetching the Geo Position from: %s", url)
    -57            geo_req = fetch.get(url)
    -58        except (JSONDecodeError, ConnectionError) as err:
    -59            log.error("Failed to retrieve geo location => %s", str(err))
    -60            geo_req = Namespace(body=cls.EMPTY_JSON_RESP)
    -61        geo_json = json.loads(geo_req.body)
    -62        geo_location: Namespace = Namespace(**geo_json)
    -63        return geo_location
    +53        """Retrieve the geographic location based on the provided IP address.
    +54        :param ip: The IP address to locate. If None, the current device's IP address will be used.
    +55        :return: A Namespace object containing the geolocation data, such as latitude, longitude, city, and country.
    +56        """
    +57        try:
    +58            url = f"{cls.GEO_LOC_URL}{'/' + ip if ip else ''}"
    +59            log.debug("Fetching the Geo Position from: %s", url)
    +60            geo_req = fetch.get(url)
    +61        except (JSONDecodeError, ConnectionError) as err:
    +62            log.error("Failed to retrieve geo location => %s", str(err))
    +63            geo_req = Namespace(body=cls.EMPTY_JSON_RESP)
    +64        geo_loc: Namespace = Namespace(**(json.loads(geo_req.body)))
    +65        return geo_loc
     
    -

    TODO

    +

    Retrieve the geographic location based on the provided IP address.

    + +
    Parameters
    + +
      +
    • ip: The IP address to locate. If None, the current device's IP address will be used.
    • +
    + +
    Returns
    + +
    +

    A Namespace object containing the geolocation data, such as latitude, longitude, city, and country.

    +
    @@ -469,9 +487,9 @@

    -
    74    @property
    -75    def latitude(self) -> float:
    -76        return self._geo_location.lat
    +            
    76    @property
    +77    def latitude(self) -> float:
    +78        return self._geo_location.lat
     
    @@ -487,9 +505,9 @@

    -
    78    @property
    -79    def longitude(self) -> float:
    -80        return self._geo_location.lon
    +            
    80    @property
    +81    def longitude(self) -> float:
    +82        return self._geo_location.lon
     
    @@ -505,9 +523,9 @@

    -
    82    @property
    -83    def country(self) -> str:
    -84        return self._geo_location.country
    +            
    84    @property
    +85    def country(self) -> str:
    +86        return self._geo_location.country
     
    @@ -523,9 +541,9 @@

    -
    86    @property
    -87    def country_code(self) -> str:
    -88        return self._geo_location.countryCode
    +            
    88    @property
    +89    def country_code(self) -> str:
    +90        return self._geo_location.countryCode
     
    @@ -541,9 +559,9 @@

    -
    90    @property
    -91    def region(self) -> str:
    -92        return self._geo_location.region
    +            
    92    @property
    +93    def region(self) -> str:
    +94        return self._geo_location.region
     
    @@ -559,9 +577,9 @@

    -
    94    @property
    -95    def region_name(self) -> str:
    -96        return self._geo_location.regionName
    +            
    96    @property
    +97    def region_name(self) -> str:
    +98        return self._geo_location.regionName
     
    @@ -577,9 +595,9 @@

    -
     98    @property
    - 99    def city(self) -> float:
    -100        return self._geo_location.city
    +            
    100    @property
    +101    def city(self) -> float:
    +102        return self._geo_location.city
     
    @@ -595,9 +613,9 @@

    -
    102    @property
    -103    def zip(self) -> str:
    -104        return self._geo_location.zip
    +            
    104    @property
    +105    def zip(self) -> str:
    +106        return self._geo_location.zip
     
    @@ -613,9 +631,9 @@

    -
    106    @property
    -107    def timezone(self) -> str:
    -108        return self._geo_location.timezone
    +            
    108    @property
    +109    def timezone(self) -> str:
    +110        return self._geo_location.timezone
     
    @@ -631,9 +649,9 @@

    -
    110    @property
    -111    def location(self) -> str:
    -112        return f"{self.city}, {self.region_name} {self.country}"
    +            
    112    @property
    +113    def location(self) -> str:
    +114        return f"{self.city}, {self.region_name} {self.country}"
     
    @@ -649,11 +667,11 @@

    -
    114    @property
    -115    def datetime(self) -> str:
    -116        utc_datetime = datetime.utcnow().replace(tzinfo=pytz.utc)
    -117        zoned_datetime = utc_datetime.astimezone(pytz.timezone(self.timezone))
    -118        return zoned_datetime.strftime(self.DATE_FMT)
    +            
    116    @property
    +117    def datetime(self) -> str:
    +118        utc_datetime = datetime.utcnow().replace(tzinfo=pytz.utc)
    +119        zoned_datetime = utc_datetime.astimezone(pytz.timezone(self.timezone))
    +120        return zoned_datetime.strftime(self.DATE_FMT)
     
    diff --git a/docs/api/askai/main/askai/core/component/image_store.html b/docs/api/askai/main/askai/core/component/image_store.html index b01c9b9e..0dbc9fc3 100644 --- a/docs/api/askai/main/askai/core/component/image_store.html +++ b/docs/api/askai/main/askai/core/component/image_store.html @@ -3,7 +3,7 @@ - + main.askai.core.component.image_store API documentation @@ -64,7 +64,7 @@

    API Documentation

    ImageMetadata
  • - name + caption
  • data @@ -127,10 +127,10 @@

    API Documentation

    query_face
  • - search_image + find_by_description
  • - search_face + find_by_similarity
  • @@ -180,7 +180,7 @@

    12 13 Copyright (c) 2024, HomeSetup 14""" - 15from askai.core.features.router.tools.vision import image_captioner + 15from askai.core.features.tools.vision import offline_captioner 16from chromadb.api.types import IncludeEnum 17from chromadb.utils.data_loaders import ImageLoader 18from chromadb.utils.embedding_functions.open_clip_embedding_function import OpenCLIPEmbeddingFunction @@ -204,169 +204,207 @@

    36 37ImageData: TypeAlias = numpy.ndarray[Any, numpy.dtype] 38 - 39ImageFile = namedtuple('ImageFile', ['img_id', 'img_path', 'img_category', 'img_caption']) + 39ImageFile = namedtuple("ImageFile", ["img_id", "img_path", "img_category", "img_caption"]) 40 - 41ImageMetadata = namedtuple('ImageMetadata', ['name', 'data', 'uri', 'distance']) + 41ImageMetadata = namedtuple("ImageMetadata", ["caption", "data", "uri", "distance"]) 42 43 44class ImageStore(metaclass=Singleton): - 45 """Provide an interface to capture WebCam photos.""" - 46 - 47 INSTANCE: "ImageStore" + 45 """Provide an interface to store, retrieve, locate, and vectorize images. This class manages the storage and + 46 retrieval of images, as well as their localization and vectorization for various applications. + 47 """ 48 - 49 COLLECTION_NAME: str = "image_store" + 49 INSTANCE: "ImageStore" 50 - 51 PHOTO_CATEGORY: str = 'photos' + 51 # fmt: off 52 - 53 FACE_CATEGORY: str = 'faces' + 53 COLLECTION_NAME: str = "image_store" 54 - 55 IMPORTS_CATEGORY: str = 'imports' + 55 PHOTO_CATEGORY: str = "photos" 56 - 57 @staticmethod - 58 def sync_folders(with_caption: bool = False, *dirs: AnyPath) -> list[ImageFile]: - 59 """Load image files from the specified directories.""" + 57 FACE_CATEGORY: str = "faces" + 58 + 59 IMPORTS_CATEGORY: str = "imports" 60 - 61 def category() -> str: - 62 """TODO""" - 63 cat_str: str - 64 if dir_path == str(cache.PHOTO_DIR): - 65 cat_str = store.PHOTO_CATEGORY - 66 elif dir_path == str(cache.FACE_DIR): - 67 cat_str = store.FACE_CATEGORY - 68 else: - 69 cat_str = store.IMPORTS_CATEGORY - 70 return cat_str - 71 - 72 img_files: list[ImageFile] = [] - 73 load_dirs: list[str] = [str(p) for p in dirs] - 74 for (dir_path, dir_names, file_names) in os.walk(cache.PICTURE_DIR): - 75 if dir_path in load_dirs: - 76 cat: str = category() - 77 files: list[str] = list(filter(is_image_file, map(lambda fn: os.path.join(dir_path, fn), file_names))) - 78 img_files.extend( - 79 ImageFile( - 80 hash_text(basename(f)), f, cat, image_captioner(f) if with_caption else "No caption" - 81 ) for f in files - 82 ) - 83 - 84 return img_files - 85 - 86 def __init__(self): - 87 self._db_client = chromadb.PersistentClient(path=str(self.persist_dir)) - 88 self._img_collection = self._db_client.get_or_create_collection( - 89 self.COLLECTION_NAME, - 90 embedding_function=OpenCLIPEmbeddingFunction(), - 91 data_loader=ImageLoader() - 92 ) - 93 - 94 @property - 95 def persist_dir(self) -> Path: - 96 return Path.joinpath(cache.PICTURE_DIR, self.COLLECTION_NAME) - 97 - 98 @property - 99 def metadatas(self) -> Optional[list[Metadata]]: -100 return self._img_collection.get()['metadatas'] -101 -102 def enlist(self) -> Optional[list[str]]: -103 return [str(mt).replace("'", '"') for mt in self.metadatas] -104 -105 def store_image(self, *face_files: ImageFile) -> int: -106 """Add the faces into the image store collection.""" -107 -108 if face_files: -109 img_ids = [ff.img_id for ff in face_files] -110 img_uris = [ff.img_path for ff in face_files] -111 img_metas = [ -112 { -113 'img_id': ff.img_id, -114 'img_path': ff.img_path, -115 'img_category': ff.img_category, -116 'img_caption': ff.img_caption, -117 } -118 for ff in face_files -119 ] -120 self._img_collection.add(ids=img_ids, uris=img_uris, metadatas=img_metas) -121 log.info("Face collection increased to: '%d' !", self._img_collection.count()) -122 -123 return self._img_collection.count() -124 -125 def clear_store(self) -> None: -126 """Clear the image store collection.""" -127 -128 log.info("Clearing image store collection: '%s'", self.COLLECTION_NAME) -129 self._db_client.delete_collection(self.COLLECTION_NAME) -130 self._img_collection = self._db_client.get_or_create_collection( -131 self.COLLECTION_NAME, -132 embedding_function=OpenCLIPEmbeddingFunction(), -133 data_loader=ImageLoader() -134 ) -135 -136 def sync_store(self, with_caption: bool = False) -> int: -137 """Synchronize image store collection with the picture folder.""" -138 -139 log.info("Synchronizing image store folders: '%s', '%s' and '%s'", -140 cache.PHOTO_DIR, cache.FACE_DIR, cache.IMG_IMPORTS_DIR) -141 self.clear_store() -142 img_files: list[ImageFile] = self.sync_folders( -143 with_caption, cache.PHOTO_DIR, cache.FACE_DIR, cache.IMG_IMPORTS_DIR) -144 self.store_image(*img_files) -145 -146 return len(img_files) -147 -148 def query_image(self, query: str, max_results: int = 1) -> list[ImageMetadata]: -149 """Query for a photo matching the query.""" -150 return self.search_image(query, [self.PHOTO_CATEGORY, self.IMPORTS_CATEGORY], max_results) -151 -152 def query_face(self, query: str, max_results: int = 1) -> list[ImageMetadata]: -153 """Query for a face matching the query.""" -154 return self.search_image(query, [self.FACE_CATEGORY], max_results) -155 -156 def search_image(self, query: str, categories: list[str], max_results: int = 1) -> list[ImageMetadata]: -157 """Search for images using natural language.""" -158 return self._query(max_results, categories=categories, query_texts=[query]) + 61 # fmt: on + 62 + 63 @staticmethod + 64 def sync_folders(with_caption: bool = False, *dirs: AnyPath) -> list[ImageFile]: + 65 """Load image files from the specified directories.""" + 66 + 67 def category() -> str: + 68 """Determine and return the category based on the directory path. + 69 :return: A string representing the category. + 70 """ + 71 cat_str: str + 72 if dir_path == str(cache.PHOTO_DIR): + 73 cat_str = store.PHOTO_CATEGORY + 74 elif dir_path == str(cache.FACE_DIR): + 75 cat_str = store.FACE_CATEGORY + 76 else: + 77 cat_str = store.IMPORTS_CATEGORY + 78 return cat_str + 79 + 80 img_files: list[ImageFile] = [] + 81 load_dirs: list[str] = [str(p) for p in dirs] + 82 for dir_path, dir_names, file_names in os.walk(cache.PICTURE_DIR): + 83 if dir_path in load_dirs: + 84 cat: str = category() + 85 files: list[str] = list(filter(is_image_file, map(lambda fn: os.path.join(dir_path, fn), file_names))) + 86 img_files.extend( + 87 ImageFile(hash_text(basename(f)), f, cat, offline_captioner(f) if with_caption else "No caption") + 88 for f in files + 89 ) + 90 + 91 return img_files + 92 + 93 def __init__(self): + 94 self._db_client = chromadb.PersistentClient(path=str(self.persist_dir)) + 95 self._img_collection = self._db_client.get_or_create_collection( + 96 self.COLLECTION_NAME, embedding_function=OpenCLIPEmbeddingFunction(), data_loader=ImageLoader() + 97 ) + 98 + 99 @property +100 def persist_dir(self) -> Path: +101 return Path.joinpath(cache.PICTURE_DIR, self.COLLECTION_NAME) +102 +103 @property +104 def metadatas(self) -> Optional[list[Metadata]]: +105 return self._img_collection.get()["metadatas"] +106 +107 def enlist(self) -> Optional[list[str]]: +108 return [str(mt).replace("'", '"') for mt in self.metadatas] +109 +110 def store_image(self, *image_files: ImageFile) -> int: +111 """Add the provided images to the image store collection. +112 :param image_files: One or more ImageFile objects representing the images to be stored. +113 :return: The number of images successfully added to the store. +114 """ +115 img_ids: list[str] = list() +116 if image_files: +117 img_ids.extend([ff.img_id for ff in image_files]) +118 img_uris = [ff.img_path for ff in image_files] +119 img_metas = [ +120 { +121 "img_id": ff.img_id, +122 "img_path": ff.img_path, +123 "img_category": ff.img_category, +124 "img_caption": ff.img_caption, +125 } +126 for ff in image_files +127 ] +128 self._img_collection.add(ids=img_ids, uris=img_uris, metadatas=img_metas) +129 log.info("Image collection increased to: '%d' !", self._img_collection.count()) +130 +131 return len(img_ids) +132 +133 def clear_store(self) -> None: +134 """Clear the image store collection.""" +135 log.info("Clearing image store collection: '%s'", self.COLLECTION_NAME) +136 self._db_client.delete_collection(self.COLLECTION_NAME) +137 self._img_collection = self._db_client.get_or_create_collection( +138 self.COLLECTION_NAME, embedding_function=OpenCLIPEmbeddingFunction(), data_loader=ImageLoader() +139 ) +140 +141 def sync_store(self, re_caption: bool = False) -> int: +142 """Synchronize the image store collection with the cached pictures folder. +143 :param re_caption: Whether to regenerate captions for the images during synchronization (default is False). +144 :return: The number of images synchronized with the store. +145 """ +146 log.info( +147 "Synchronizing image store folders: '%s', '%s' and '%s'", +148 cache.PHOTO_DIR, +149 cache.FACE_DIR, +150 cache.IMG_IMPORTS_DIR, +151 ) +152 self.clear_store() +153 img_files: list[ImageFile] = self.sync_folders( +154 re_caption, cache.PHOTO_DIR, cache.FACE_DIR, cache.IMG_IMPORTS_DIR +155 ) +156 self.store_image(*img_files) +157 +158 return len(img_files) 159 -160 def search_face(self, photo: ImageData, max_results: int = 1) -> list[ImageMetadata]: -161 """Search for faces matching the provided photo using similarity methods.""" -162 return self._query(max_results, categories=[self.FACE_CATEGORY], query_images=[photo]) -163 -164 def _query(self, max_results: int = 5, **kwargs) -> list[ImageMetadata]: -165 """Query the database for images.""" -166 result: list[ImageMetadata] = [] -167 categories: list[str] = kwargs['categories'] or [] -168 filters: dict[str, Any] = self._categories(categories) -169 del kwargs['categories'] -170 query_results = self._img_collection.query( -171 **kwargs, -172 n_results=5, -173 include=[ -174 IncludeEnum.documents, -175 IncludeEnum.distances, -176 IncludeEnum.metadatas, -177 IncludeEnum.data, -178 IncludeEnum.uris -179 ], -180 where=filters -181 ) -182 for i in range(len(query_results['ids'][0])): -183 if (img_data := get_or_default(query_results['data'][0], i, None)) is None: -184 continue -185 img_name = query_results['metadatas'][0][i]['img_caption'] -186 img_path = query_results['metadatas'][0][i]['img_path'] -187 img_dist = query_results['distances'][0][i] -188 result.append(ImageMetadata(img_name, img_data, img_path, img_dist)) -189 # Sort by distance -190 result.sort(key=lambda img: img[3]) -191 -192 return result[0:max_results] -193 -194 def _categories(self, categories: list[str]) -> dict[str, Any]: -195 """Build the category filter to query images.""" -196 return {"$or": [{'img_category': cat} for cat in categories]} \ -197 if len(categories) > 1 \ -198 else {'img_category': get_or_default(categories, 0, "")} -199 -200 -201assert (store := ImageStore().INSTANCE) is not None +160 def query_image(self, description: str, k: int = 3) -> list[ImageMetadata]: +161 """Query the image store for photos matching the provided description. +162 :param description: A text description to match against the stored images. +163 :param k: The maximum number of matching results to return (default is 3). +164 :return: A list of ImageMetadata objects for the photos that match the description. +165 """ +166 return self.find_by_description(description, [self.PHOTO_CATEGORY, self.IMPORTS_CATEGORY], k) +167 +168 def query_face(self, description: str, k: int = 3) -> list[ImageMetadata]: +169 """Query the image store for faces matching the provided description. +170 :param description: A text description to match against the stored faces. +171 :param k: The maximum number of matching faces to return (default is 1). +172 :return: A list of ImageMetadata objects for the faces that match the description. +173 """ +174 return self.find_by_description(description, [self.FACE_CATEGORY], k) +175 +176 def find_by_description(self, description: str, categories: list[str], k: int = 3) -> list[ImageMetadata]: +177 """Find images using natural language. +178 :param description: A natural language description to match against stored images. +179 :param categories: A list of categories to limit the search within. +180 :param k: The maximum number of matching images to return (default is 3). +181 :return: A list of ImageMetadata objects for the images that match the description and categories. +182 """ +183 return self._query(k, categories=categories, query_texts=[description]) +184 +185 def find_by_similarity(self, photo: ImageData, k: int = 3) -> list[ImageMetadata]: +186 """Find images that match the provided photo using similarity methods. +187 :param photo: The ImageData object representing the photo to match against stored faces. +188 :param k: The maximum number of matching faces to return (default is 3). +189 :return: A list of ImageMetadata objects for the faces that match the provided photo. +190 """ +191 return self._query(k, categories=[self.FACE_CATEGORY], query_images=[photo]) +192 +193 def _query(self, k: int = 5, **kwargs) -> list[ImageMetadata]: +194 """Query the image store collection for entries that match the provided arguments, sorted by distance. +195 :param k: The maximum number of results to return (default is 5). +196 :param kwargs: Additional arguments to filter and refine the query, such as categories or other search parameters. +197 :return: A list of ImageMetadata objects that match the query, sorted by distance. +198 """ +199 result: list[ImageMetadata] = [] +200 categories: list[str] = kwargs["categories"] or [] +201 filters: dict[str, Any] = self._category_filter(categories) +202 del kwargs["categories"] +203 query_results = self._img_collection.query( +204 **kwargs, +205 n_results=5, +206 include=[ +207 IncludeEnum.documents, +208 IncludeEnum.distances, +209 IncludeEnum.metadatas, +210 IncludeEnum.data, +211 IncludeEnum.uris, +212 ], +213 where=filters, +214 ) +215 for i in range(len(query_results["ids"][0])): +216 if (img_data := get_or_default(query_results["data"][0], i, None)) is None: +217 continue +218 img_name = query_results["metadatas"][0][i]["img_caption"] +219 img_path = query_results["metadatas"][0][i]["img_path"] +220 img_dist = query_results["distances"][0][i] +221 result.append(ImageMetadata(img_name, img_data, img_path, img_dist)) +222 # Sort by distance +223 result.sort(key=lambda img: img[3]) +224 +225 return result[0:k] +226 +227 def _category_filter(self, categories: list[str]) -> dict[str, Any]: +228 """Build a ChromaDB category filter for querying images. +229 :param categories: A list of category names to include in the filter. +230 :return: A dictionary representing the category filter to be used in image queries. +231 """ +232 return ( +233 {"$or": [{"img_category": cat} for cat in categories]} +234 if len(categories) > 1 +235 else {"img_category": get_or_default(categories, 0, "")} +236 ) +237 +238 +239assert (store := ImageStore().INSTANCE) is not None

    @@ -496,31 +534,31 @@

    Inherited Members
    -

    ImageMetadata(name, data, uri, distance)

    +

    ImageMetadata(caption, data, uri, distance)

    - ImageMetadata(name, data, uri, distance) + ImageMetadata(caption, data, uri, distance)
    -

    Create new instance of ImageMetadata(name, data, uri, distance)

    +

    Create new instance of ImageMetadata(caption, data, uri, distance)

    -
    +
    - name + caption
    - +

    Alias for field number 0

    @@ -589,164 +627,203 @@
    Inherited Members
     45class ImageStore(metaclass=Singleton):
    - 46    """Provide an interface to capture WebCam photos."""
    - 47
    - 48    INSTANCE: "ImageStore"
    + 46    """Provide an interface to store, retrieve, locate, and vectorize images. This class manages the storage and
    + 47    retrieval of images, as well as their localization and vectorization for various applications.
    + 48    """
      49
    - 50    COLLECTION_NAME: str = "image_store"
    + 50    INSTANCE: "ImageStore"
      51
    - 52    PHOTO_CATEGORY: str = 'photos'
    + 52    # fmt: off
      53
    - 54    FACE_CATEGORY: str = 'faces'
    + 54    COLLECTION_NAME: str    = "image_store"
      55
    - 56    IMPORTS_CATEGORY: str = 'imports'
    + 56    PHOTO_CATEGORY: str     = "photos"
      57
    - 58    @staticmethod
    - 59    def sync_folders(with_caption: bool = False, *dirs: AnyPath) -> list[ImageFile]:
    - 60        """Load image files from the specified directories."""
    + 58    FACE_CATEGORY: str      = "faces"
    + 59
    + 60    IMPORTS_CATEGORY: str   = "imports"
      61
    - 62        def category() -> str:
    - 63            """TODO"""
    - 64            cat_str: str
    - 65            if dir_path == str(cache.PHOTO_DIR):
    - 66                cat_str = store.PHOTO_CATEGORY
    - 67            elif dir_path == str(cache.FACE_DIR):
    - 68                cat_str = store.FACE_CATEGORY
    - 69            else:
    - 70                cat_str = store.IMPORTS_CATEGORY
    - 71            return cat_str
    - 72
    - 73        img_files: list[ImageFile] = []
    - 74        load_dirs: list[str] = [str(p) for p in dirs]
    - 75        for (dir_path, dir_names, file_names) in os.walk(cache.PICTURE_DIR):
    - 76            if dir_path in load_dirs:
    - 77                cat: str = category()
    - 78                files: list[str] = list(filter(is_image_file, map(lambda fn: os.path.join(dir_path, fn), file_names)))
    - 79                img_files.extend(
    - 80                    ImageFile(
    - 81                        hash_text(basename(f)), f, cat, image_captioner(f) if with_caption else "No caption"
    - 82                    ) for f in files
    - 83                )
    - 84
    - 85        return img_files
    - 86
    - 87    def __init__(self):
    - 88        self._db_client = chromadb.PersistentClient(path=str(self.persist_dir))
    - 89        self._img_collection = self._db_client.get_or_create_collection(
    - 90            self.COLLECTION_NAME,
    - 91            embedding_function=OpenCLIPEmbeddingFunction(),
    - 92            data_loader=ImageLoader()
    - 93        )
    - 94
    - 95    @property
    - 96    def persist_dir(self) -> Path:
    - 97        return Path.joinpath(cache.PICTURE_DIR, self.COLLECTION_NAME)
    - 98
    - 99    @property
    -100    def metadatas(self) -> Optional[list[Metadata]]:
    -101        return self._img_collection.get()['metadatas']
    -102
    -103    def enlist(self) -> Optional[list[str]]:
    -104        return [str(mt).replace("'", '"') for mt in self.metadatas]
    -105
    -106    def store_image(self, *face_files: ImageFile) -> int:
    -107        """Add the faces into the image store collection."""
    -108
    -109        if face_files:
    -110            img_ids = [ff.img_id for ff in face_files]
    -111            img_uris = [ff.img_path for ff in face_files]
    -112            img_metas = [
    -113                {
    -114                    'img_id': ff.img_id,
    -115                    'img_path': ff.img_path,
    -116                    'img_category': ff.img_category,
    -117                    'img_caption': ff.img_caption,
    -118                }
    -119                for ff in face_files
    -120            ]
    -121            self._img_collection.add(ids=img_ids, uris=img_uris, metadatas=img_metas)
    -122            log.info("Face collection increased to: '%d' !", self._img_collection.count())
    -123
    -124        return self._img_collection.count()
    -125
    -126    def clear_store(self) -> None:
    -127        """Clear the image store collection."""
    -128
    -129        log.info("Clearing image store collection: '%s'", self.COLLECTION_NAME)
    -130        self._db_client.delete_collection(self.COLLECTION_NAME)
    -131        self._img_collection = self._db_client.get_or_create_collection(
    -132            self.COLLECTION_NAME,
    -133            embedding_function=OpenCLIPEmbeddingFunction(),
    -134            data_loader=ImageLoader()
    -135        )
    -136
    -137    def sync_store(self, with_caption: bool = False) -> int:
    -138        """Synchronize image store collection with the picture folder."""
    -139
    -140        log.info("Synchronizing image store folders: '%s', '%s' and '%s'",
    -141                 cache.PHOTO_DIR, cache.FACE_DIR, cache.IMG_IMPORTS_DIR)
    -142        self.clear_store()
    -143        img_files: list[ImageFile] = self.sync_folders(
    -144            with_caption, cache.PHOTO_DIR, cache.FACE_DIR, cache.IMG_IMPORTS_DIR)
    -145        self.store_image(*img_files)
    -146
    -147        return len(img_files)
    -148
    -149    def query_image(self, query: str, max_results: int = 1) -> list[ImageMetadata]:
    -150        """Query for a photo matching the query."""
    -151        return self.search_image(query, [self.PHOTO_CATEGORY, self.IMPORTS_CATEGORY], max_results)
    -152
    -153    def query_face(self, query: str, max_results: int = 1) -> list[ImageMetadata]:
    -154        """Query for a face matching the query."""
    -155        return self.search_image(query, [self.FACE_CATEGORY], max_results)
    -156
    -157    def search_image(self, query: str, categories: list[str], max_results: int = 1) -> list[ImageMetadata]:
    -158        """Search for images using natural language."""
    -159        return self._query(max_results, categories=categories, query_texts=[query])
    + 62    # fmt: on
    + 63
    + 64    @staticmethod
    + 65    def sync_folders(with_caption: bool = False, *dirs: AnyPath) -> list[ImageFile]:
    + 66        """Load image files from the specified directories."""
    + 67
    + 68        def category() -> str:
    + 69            """Determine and return the category based on the directory path.
    + 70            :return: A string representing the category.
    + 71            """
    + 72            cat_str: str
    + 73            if dir_path == str(cache.PHOTO_DIR):
    + 74                cat_str = store.PHOTO_CATEGORY
    + 75            elif dir_path == str(cache.FACE_DIR):
    + 76                cat_str = store.FACE_CATEGORY
    + 77            else:
    + 78                cat_str = store.IMPORTS_CATEGORY
    + 79            return cat_str
    + 80
    + 81        img_files: list[ImageFile] = []
    + 82        load_dirs: list[str] = [str(p) for p in dirs]
    + 83        for dir_path, dir_names, file_names in os.walk(cache.PICTURE_DIR):
    + 84            if dir_path in load_dirs:
    + 85                cat: str = category()
    + 86                files: list[str] = list(filter(is_image_file, map(lambda fn: os.path.join(dir_path, fn), file_names)))
    + 87                img_files.extend(
    + 88                    ImageFile(hash_text(basename(f)), f, cat, offline_captioner(f) if with_caption else "No caption")
    + 89                    for f in files
    + 90                )
    + 91
    + 92        return img_files
    + 93
    + 94    def __init__(self):
    + 95        self._db_client = chromadb.PersistentClient(path=str(self.persist_dir))
    + 96        self._img_collection = self._db_client.get_or_create_collection(
    + 97            self.COLLECTION_NAME, embedding_function=OpenCLIPEmbeddingFunction(), data_loader=ImageLoader()
    + 98        )
    + 99
    +100    @property
    +101    def persist_dir(self) -> Path:
    +102        return Path.joinpath(cache.PICTURE_DIR, self.COLLECTION_NAME)
    +103
    +104    @property
    +105    def metadatas(self) -> Optional[list[Metadata]]:
    +106        return self._img_collection.get()["metadatas"]
    +107
    +108    def enlist(self) -> Optional[list[str]]:
    +109        return [str(mt).replace("'", '"') for mt in self.metadatas]
    +110
    +111    def store_image(self, *image_files: ImageFile) -> int:
    +112        """Add the provided images to the image store collection.
    +113        :param image_files: One or more ImageFile objects representing the images to be stored.
    +114        :return: The number of images successfully added to the store.
    +115        """
    +116        img_ids: list[str] = list()
    +117        if image_files:
    +118            img_ids.extend([ff.img_id for ff in image_files])
    +119            img_uris = [ff.img_path for ff in image_files]
    +120            img_metas = [
    +121                {
    +122                    "img_id": ff.img_id,
    +123                    "img_path": ff.img_path,
    +124                    "img_category": ff.img_category,
    +125                    "img_caption": ff.img_caption,
    +126                }
    +127                for ff in image_files
    +128            ]
    +129            self._img_collection.add(ids=img_ids, uris=img_uris, metadatas=img_metas)
    +130            log.info("Image collection increased to: '%d' !", self._img_collection.count())
    +131
    +132        return len(img_ids)
    +133
    +134    def clear_store(self) -> None:
    +135        """Clear the image store collection."""
    +136        log.info("Clearing image store collection: '%s'", self.COLLECTION_NAME)
    +137        self._db_client.delete_collection(self.COLLECTION_NAME)
    +138        self._img_collection = self._db_client.get_or_create_collection(
    +139            self.COLLECTION_NAME, embedding_function=OpenCLIPEmbeddingFunction(), data_loader=ImageLoader()
    +140        )
    +141
    +142    def sync_store(self, re_caption: bool = False) -> int:
    +143        """Synchronize the image store collection with the cached pictures folder.
    +144        :param re_caption: Whether to regenerate captions for the images during synchronization (default is False).
    +145        :return: The number of images synchronized with the store.
    +146        """
    +147        log.info(
    +148            "Synchronizing image store folders: '%s', '%s' and '%s'",
    +149            cache.PHOTO_DIR,
    +150            cache.FACE_DIR,
    +151            cache.IMG_IMPORTS_DIR,
    +152        )
    +153        self.clear_store()
    +154        img_files: list[ImageFile] = self.sync_folders(
    +155            re_caption, cache.PHOTO_DIR, cache.FACE_DIR, cache.IMG_IMPORTS_DIR
    +156        )
    +157        self.store_image(*img_files)
    +158
    +159        return len(img_files)
     160
    -161    def search_face(self, photo: ImageData, max_results: int = 1) -> list[ImageMetadata]:
    -162        """Search for faces matching the provided photo using similarity methods."""
    -163        return self._query(max_results, categories=[self.FACE_CATEGORY], query_images=[photo])
    -164
    -165    def _query(self, max_results: int = 5, **kwargs) -> list[ImageMetadata]:
    -166        """Query the database for images."""
    -167        result: list[ImageMetadata] = []
    -168        categories: list[str] = kwargs['categories'] or []
    -169        filters: dict[str, Any] = self._categories(categories)
    -170        del kwargs['categories']
    -171        query_results = self._img_collection.query(
    -172            **kwargs,
    -173            n_results=5,
    -174            include=[
    -175                IncludeEnum.documents,
    -176                IncludeEnum.distances,
    -177                IncludeEnum.metadatas,
    -178                IncludeEnum.data,
    -179                IncludeEnum.uris
    -180            ],
    -181            where=filters
    -182        )
    -183        for i in range(len(query_results['ids'][0])):
    -184            if (img_data := get_or_default(query_results['data'][0], i, None)) is None:
    -185                continue
    -186            img_name = query_results['metadatas'][0][i]['img_caption']
    -187            img_path = query_results['metadatas'][0][i]['img_path']
    -188            img_dist = query_results['distances'][0][i]
    -189            result.append(ImageMetadata(img_name, img_data, img_path, img_dist))
    -190        # Sort by distance
    -191        result.sort(key=lambda img: img[3])
    -192
    -193        return result[0:max_results]
    -194
    -195    def _categories(self, categories: list[str]) -> dict[str, Any]:
    -196        """Build the category filter to query images."""
    -197        return {"$or": [{'img_category': cat} for cat in categories]} \
    -198            if len(categories) > 1 \
    -199            else {'img_category': get_or_default(categories, 0, "")}
    +161    def query_image(self, description: str, k: int = 3) -> list[ImageMetadata]:
    +162        """Query the image store for photos matching the provided description.
    +163        :param description: A text description to match against the stored images.
    +164        :param k: The maximum number of matching results to return (default is 3).
    +165        :return: A list of ImageMetadata objects for the photos that match the description.
    +166        """
    +167        return self.find_by_description(description, [self.PHOTO_CATEGORY, self.IMPORTS_CATEGORY], k)
    +168
    +169    def query_face(self, description: str, k: int = 3) -> list[ImageMetadata]:
    +170        """Query the image store for faces matching the provided description.
    +171        :param description: A text description to match against the stored faces.
    +172        :param k: The maximum number of matching faces to return (default is 1).
    +173        :return: A list of ImageMetadata objects for the faces that match the description.
    +174        """
    +175        return self.find_by_description(description, [self.FACE_CATEGORY], k)
    +176
    +177    def find_by_description(self, description: str, categories: list[str], k: int = 3) -> list[ImageMetadata]:
    +178        """Find images using natural language.
    +179        :param description: A natural language description to match against stored images.
    +180        :param categories: A list of categories to limit the search within.
    +181        :param k: The maximum number of matching images to return (default is 3).
    +182        :return: A list of ImageMetadata objects for the images that match the description and categories.
    +183        """
    +184        return self._query(k, categories=categories, query_texts=[description])
    +185
    +186    def find_by_similarity(self, photo: ImageData, k: int = 3) -> list[ImageMetadata]:
    +187        """Find images that match the provided photo using similarity methods.
    +188        :param photo: The ImageData object representing the photo to match against stored faces.
    +189        :param k: The maximum number of matching faces to return (default is 3).
    +190        :return: A list of ImageMetadata objects for the faces that match the provided photo.
    +191        """
    +192        return self._query(k, categories=[self.FACE_CATEGORY], query_images=[photo])
    +193
    +194    def _query(self, k: int = 5, **kwargs) -> list[ImageMetadata]:
    +195        """Query the image store collection for entries that match the provided arguments, sorted by distance.
    +196        :param k: The maximum number of results to return (default is 5).
    +197        :param kwargs: Additional arguments to filter and refine the query, such as categories or other search parameters.
    +198        :return: A list of ImageMetadata objects that match the query, sorted by distance.
    +199        """
    +200        result: list[ImageMetadata] = []
    +201        categories: list[str] = kwargs["categories"] or []
    +202        filters: dict[str, Any] = self._category_filter(categories)
    +203        del kwargs["categories"]
    +204        query_results = self._img_collection.query(
    +205            **kwargs,
    +206            n_results=5,
    +207            include=[
    +208                IncludeEnum.documents,
    +209                IncludeEnum.distances,
    +210                IncludeEnum.metadatas,
    +211                IncludeEnum.data,
    +212                IncludeEnum.uris,
    +213            ],
    +214            where=filters,
    +215        )
    +216        for i in range(len(query_results["ids"][0])):
    +217            if (img_data := get_or_default(query_results["data"][0], i, None)) is None:
    +218                continue
    +219            img_name = query_results["metadatas"][0][i]["img_caption"]
    +220            img_path = query_results["metadatas"][0][i]["img_path"]
    +221            img_dist = query_results["distances"][0][i]
    +222            result.append(ImageMetadata(img_name, img_data, img_path, img_dist))
    +223        # Sort by distance
    +224        result.sort(key=lambda img: img[3])
    +225
    +226        return result[0:k]
    +227
    +228    def _category_filter(self, categories: list[str]) -> dict[str, Any]:
    +229        """Build a ChromaDB category filter for querying images.
    +230        :param categories: A list of category names to include in the filter.
    +231        :return: A dictionary representing the category filter to be used in image queries.
    +232        """
    +233        return (
    +234            {"$or": [{"img_category": cat} for cat in categories]}
    +235            if len(categories) > 1
    +236            else {"img_category": get_or_default(categories, 0, "")}
    +237        )
     
    -

    Provide an interface to capture WebCam photos.

    +

    Provide an interface to store, retrieve, locate, and vectorize images. This class manages the storage and +retrieval of images, as well as their localization and vectorization for various applications.

    @@ -852,34 +929,35 @@
    Inherited Members
    -
    58    @staticmethod
    -59    def sync_folders(with_caption: bool = False, *dirs: AnyPath) -> list[ImageFile]:
    -60        """Load image files from the specified directories."""
    -61
    -62        def category() -> str:
    -63            """TODO"""
    -64            cat_str: str
    -65            if dir_path == str(cache.PHOTO_DIR):
    -66                cat_str = store.PHOTO_CATEGORY
    -67            elif dir_path == str(cache.FACE_DIR):
    -68                cat_str = store.FACE_CATEGORY
    -69            else:
    -70                cat_str = store.IMPORTS_CATEGORY
    -71            return cat_str
    -72
    -73        img_files: list[ImageFile] = []
    -74        load_dirs: list[str] = [str(p) for p in dirs]
    -75        for (dir_path, dir_names, file_names) in os.walk(cache.PICTURE_DIR):
    -76            if dir_path in load_dirs:
    -77                cat: str = category()
    -78                files: list[str] = list(filter(is_image_file, map(lambda fn: os.path.join(dir_path, fn), file_names)))
    -79                img_files.extend(
    -80                    ImageFile(
    -81                        hash_text(basename(f)), f, cat, image_captioner(f) if with_caption else "No caption"
    -82                    ) for f in files
    -83                )
    -84
    -85        return img_files
    +            
    64    @staticmethod
    +65    def sync_folders(with_caption: bool = False, *dirs: AnyPath) -> list[ImageFile]:
    +66        """Load image files from the specified directories."""
    +67
    +68        def category() -> str:
    +69            """Determine and return the category based on the directory path.
    +70            :return: A string representing the category.
    +71            """
    +72            cat_str: str
    +73            if dir_path == str(cache.PHOTO_DIR):
    +74                cat_str = store.PHOTO_CATEGORY
    +75            elif dir_path == str(cache.FACE_DIR):
    +76                cat_str = store.FACE_CATEGORY
    +77            else:
    +78                cat_str = store.IMPORTS_CATEGORY
    +79            return cat_str
    +80
    +81        img_files: list[ImageFile] = []
    +82        load_dirs: list[str] = [str(p) for p in dirs]
    +83        for dir_path, dir_names, file_names in os.walk(cache.PICTURE_DIR):
    +84            if dir_path in load_dirs:
    +85                cat: str = category()
    +86                files: list[str] = list(filter(is_image_file, map(lambda fn: os.path.join(dir_path, fn), file_names)))
    +87                img_files.extend(
    +88                    ImageFile(hash_text(basename(f)), f, cat, offline_captioner(f) if with_caption else "No caption")
    +89                    for f in files
    +90                )
    +91
    +92        return img_files
     
    @@ -897,9 +975,9 @@
    Inherited Members
    -
    95    @property
    -96    def persist_dir(self) -> Path:
    -97        return Path.joinpath(cache.PICTURE_DIR, self.COLLECTION_NAME)
    +            
    100    @property
    +101    def persist_dir(self) -> Path:
    +102        return Path.joinpath(cache.PICTURE_DIR, self.COLLECTION_NAME)
     
    @@ -915,9 +993,9 @@
    Inherited Members
    -
     99    @property
    -100    def metadatas(self) -> Optional[list[Metadata]]:
    -101        return self._img_collection.get()['metadatas']
    +            
    104    @property
    +105    def metadatas(self) -> Optional[list[Metadata]]:
    +106        return self._img_collection.get()["metadatas"]
     
    @@ -935,8 +1013,8 @@
    Inherited Members
    -
    103    def enlist(self) -> Optional[list[str]]:
    -104        return [str(mt).replace("'", '"') for mt in self.metadatas]
    +            
    108    def enlist(self) -> Optional[list[str]]:
    +109        return [str(mt).replace("'", '"') for mt in self.metadatas]
     
    @@ -948,35 +1026,50 @@
    Inherited Members
    def - store_image( self, *face_files: ImageFile) -> int: + store_image( self, *image_files: ImageFile) -> int:
    -
    106    def store_image(self, *face_files: ImageFile) -> int:
    -107        """Add the faces into the image store collection."""
    -108
    -109        if face_files:
    -110            img_ids = [ff.img_id for ff in face_files]
    -111            img_uris = [ff.img_path for ff in face_files]
    -112            img_metas = [
    -113                {
    -114                    'img_id': ff.img_id,
    -115                    'img_path': ff.img_path,
    -116                    'img_category': ff.img_category,
    -117                    'img_caption': ff.img_caption,
    -118                }
    -119                for ff in face_files
    -120            ]
    -121            self._img_collection.add(ids=img_ids, uris=img_uris, metadatas=img_metas)
    -122            log.info("Face collection increased to: '%d' !", self._img_collection.count())
    -123
    -124        return self._img_collection.count()
    +            
    111    def store_image(self, *image_files: ImageFile) -> int:
    +112        """Add the provided images to the image store collection.
    +113        :param image_files: One or more ImageFile objects representing the images to be stored.
    +114        :return: The number of images successfully added to the store.
    +115        """
    +116        img_ids: list[str] = list()
    +117        if image_files:
    +118            img_ids.extend([ff.img_id for ff in image_files])
    +119            img_uris = [ff.img_path for ff in image_files]
    +120            img_metas = [
    +121                {
    +122                    "img_id": ff.img_id,
    +123                    "img_path": ff.img_path,
    +124                    "img_category": ff.img_category,
    +125                    "img_caption": ff.img_caption,
    +126                }
    +127                for ff in image_files
    +128            ]
    +129            self._img_collection.add(ids=img_ids, uris=img_uris, metadatas=img_metas)
    +130            log.info("Image collection increased to: '%d' !", self._img_collection.count())
    +131
    +132        return len(img_ids)
     
    -

    Add the faces into the image store collection.

    +

    Add the provided images to the image store collection.

    + +
    Parameters
    + +
      +
    • image_files: One or more ImageFile objects representing the images to be stored.
    • +
    + +
    Returns
    + +
    +

    The number of images successfully added to the store.

    +
    @@ -992,16 +1085,13 @@
    Inherited Members
    -
    126    def clear_store(self) -> None:
    -127        """Clear the image store collection."""
    -128
    -129        log.info("Clearing image store collection: '%s'", self.COLLECTION_NAME)
    -130        self._db_client.delete_collection(self.COLLECTION_NAME)
    -131        self._img_collection = self._db_client.get_or_create_collection(
    -132            self.COLLECTION_NAME,
    -133            embedding_function=OpenCLIPEmbeddingFunction(),
    -134            data_loader=ImageLoader()
    -135        )
    +            
    134    def clear_store(self) -> None:
    +135        """Clear the image store collection."""
    +136        log.info("Clearing image store collection: '%s'", self.COLLECTION_NAME)
    +137        self._db_client.delete_collection(self.COLLECTION_NAME)
    +138        self._img_collection = self._db_client.get_or_create_collection(
    +139            self.COLLECTION_NAME, embedding_function=OpenCLIPEmbeddingFunction(), data_loader=ImageLoader()
    +140        )
     
    @@ -1015,27 +1105,46 @@
    Inherited Members
    def - sync_store(self, with_caption: bool = False) -> int: + sync_store(self, re_caption: bool = False) -> int:
    -
    137    def sync_store(self, with_caption: bool = False) -> int:
    -138        """Synchronize image store collection with the picture folder."""
    -139
    -140        log.info("Synchronizing image store folders: '%s', '%s' and '%s'",
    -141                 cache.PHOTO_DIR, cache.FACE_DIR, cache.IMG_IMPORTS_DIR)
    -142        self.clear_store()
    -143        img_files: list[ImageFile] = self.sync_folders(
    -144            with_caption, cache.PHOTO_DIR, cache.FACE_DIR, cache.IMG_IMPORTS_DIR)
    -145        self.store_image(*img_files)
    -146
    -147        return len(img_files)
    +            
    142    def sync_store(self, re_caption: bool = False) -> int:
    +143        """Synchronize the image store collection with the cached pictures folder.
    +144        :param re_caption: Whether to regenerate captions for the images during synchronization (default is False).
    +145        :return: The number of images synchronized with the store.
    +146        """
    +147        log.info(
    +148            "Synchronizing image store folders: '%s', '%s' and '%s'",
    +149            cache.PHOTO_DIR,
    +150            cache.FACE_DIR,
    +151            cache.IMG_IMPORTS_DIR,
    +152        )
    +153        self.clear_store()
    +154        img_files: list[ImageFile] = self.sync_folders(
    +155            re_caption, cache.PHOTO_DIR, cache.FACE_DIR, cache.IMG_IMPORTS_DIR
    +156        )
    +157        self.store_image(*img_files)
    +158
    +159        return len(img_files)
     
    -

    Synchronize image store collection with the picture folder.

    +

    Synchronize the image store collection with the cached pictures folder.

    + +
    Parameters
    + +
      +
    • re_caption: Whether to regenerate captions for the images during synchronization (default is False).
    • +
    + +
    Returns
    + +
    +

    The number of images synchronized with the store.

    +
    @@ -1045,19 +1154,36 @@
    Inherited Members
    def - query_image( self, query: str, max_results: int = 1) -> list[ImageMetadata]: + query_image( self, description: str, k: int = 3) -> list[ImageMetadata]:
    -
    149    def query_image(self, query: str, max_results: int = 1) -> list[ImageMetadata]:
    -150        """Query for a photo matching the query."""
    -151        return self.search_image(query, [self.PHOTO_CATEGORY, self.IMPORTS_CATEGORY], max_results)
    +            
    161    def query_image(self, description: str, k: int = 3) -> list[ImageMetadata]:
    +162        """Query the image store for photos matching the provided description.
    +163        :param description: A text description to match against the stored images.
    +164        :param k: The maximum number of matching results to return (default is 3).
    +165        :return: A list of ImageMetadata objects for the photos that match the description.
    +166        """
    +167        return self.find_by_description(description, [self.PHOTO_CATEGORY, self.IMPORTS_CATEGORY], k)
     
    -

    Query for a photo matching the query.

    +

    Query the image store for photos matching the provided description.

    + +
    Parameters
    + +
      +
    • description: A text description to match against the stored images.
    • +
    • k: The maximum number of matching results to return (default is 3).
    • +
    + +
    Returns
    + +
    +

    A list of ImageMetadata objects for the photos that match the description.

    +
    @@ -1067,63 +1193,116 @@
    Inherited Members
    def - query_face( self, query: str, max_results: int = 1) -> list[ImageMetadata]: + query_face( self, description: str, k: int = 3) -> list[ImageMetadata]:
    -
    153    def query_face(self, query: str, max_results: int = 1) -> list[ImageMetadata]:
    -154        """Query for a face matching the query."""
    -155        return self.search_image(query, [self.FACE_CATEGORY], max_results)
    +            
    169    def query_face(self, description: str, k: int = 3) -> list[ImageMetadata]:
    +170        """Query the image store for faces matching the provided description.
    +171        :param description: A text description to match against the stored faces.
    +172        :param k: The maximum number of matching faces to return (default is 1).
    +173        :return: A list of ImageMetadata objects for the faces that match the description.
    +174        """
    +175        return self.find_by_description(description, [self.FACE_CATEGORY], k)
     
    -

    Query for a face matching the query.

    +

    Query the image store for faces matching the provided description.

    + +
    Parameters
    + +
      +
    • description: A text description to match against the stored faces.
    • +
    • k: The maximum number of matching faces to return (default is 1).
    • +
    + +
    Returns
    + +
    +

    A list of ImageMetadata objects for the faces that match the description.

    +
    -
    - +
    +
    def - search_image( self, query: str, categories: list[str], max_results: int = 1) -> list[ImageMetadata]: + find_by_description( self, description: str, categories: list[str], k: int = 3) -> list[ImageMetadata]: - +
    - -
    157    def search_image(self, query: str, categories: list[str], max_results: int = 1) -> list[ImageMetadata]:
    -158        """Search for images using natural language."""
    -159        return self._query(max_results, categories=categories, query_texts=[query])
    +    
    +            
    177    def find_by_description(self, description: str, categories: list[str], k: int = 3) -> list[ImageMetadata]:
    +178        """Find images using natural language.
    +179        :param description: A natural language description to match against stored images.
    +180        :param categories: A list of categories to limit the search within.
    +181        :param k: The maximum number of matching images to return (default is 3).
    +182        :return: A list of ImageMetadata objects for the images that match the description and categories.
    +183        """
    +184        return self._query(k, categories=categories, query_texts=[description])
     
    -

    Search for images using natural language.

    +

    Find images using natural language.

    + +
    Parameters
    + +
      +
    • description: A natural language description to match against stored images.
    • +
    • categories: A list of categories to limit the search within.
    • +
    • k: The maximum number of matching images to return (default is 3).
    • +
    + +
    Returns
    + +
    +

    A list of ImageMetadata objects for the images that match the description and categories.

    +
    -
    - +
    +
    def - search_face( self, photo: numpy.ndarray[typing.Any, numpy.dtype], max_results: int = 1) -> list[ImageMetadata]: + find_by_similarity( self, photo: numpy.ndarray[typing.Any, numpy.dtype], k: int = 3) -> list[ImageMetadata]: - +
    - -
    161    def search_face(self, photo: ImageData, max_results: int = 1) -> list[ImageMetadata]:
    -162        """Search for faces matching the provided photo using similarity methods."""
    -163        return self._query(max_results, categories=[self.FACE_CATEGORY], query_images=[photo])
    +    
    +            
    186    def find_by_similarity(self, photo: ImageData, k: int = 3) -> list[ImageMetadata]:
    +187        """Find images that match the provided photo using similarity methods.
    +188        :param photo: The ImageData object representing the photo to match against stored faces.
    +189        :param k: The maximum number of matching faces to return (default is 3).
    +190        :return: A list of ImageMetadata objects for the faces that match the provided photo.
    +191        """
    +192        return self._query(k, categories=[self.FACE_CATEGORY], query_images=[photo])
     
    -

    Search for faces matching the provided photo using similarity methods.

    +

    Find images that match the provided photo using similarity methods.

    + +
    Parameters
    + +
      +
    • photo: The ImageData object representing the photo to match against stored faces.
    • +
    • k: The maximum number of matching faces to return (default is 3).
    • +
    + +
    Returns
    + +
    +

    A list of ImageMetadata objects for the faces that match the provided photo.

    +
    diff --git a/docs/api/askai/main/askai/core/component/internet_service.html b/docs/api/askai/main/askai/core/component/internet_service.html index e0e6ef5a..21d9fe94 100644 --- a/docs/api/askai/main/askai/core/component/internet_service.html +++ b/docs/api/askai/main/askai/core/component/internet_service.html @@ -3,7 +3,7 @@ - + main.askai.core.component.internet_service API documentation @@ -43,13 +43,10 @@

    API Documentation

    ASKAI_INTERNET_DATA_KEY
  • - wrap_response + refine_template
  • - refine_template -
  • -
  • - search_google + google_search
  • scrap_sites @@ -105,175 +102,191 @@

    12 13 Copyright (c) 2024, HomeSetup 14""" - 15from askai.core.askai_configs import configs - 16from askai.core.askai_events import events - 17from askai.core.askai_messages import msg - 18from askai.core.askai_prompt import prompt - 19from askai.core.component.geo_location import geo_location - 20from askai.core.component.summarizer import summarizer - 21from askai.core.engine.openai.temperature import Temperature - 22from askai.core.model.search_result import SearchResult - 23from askai.core.support.langchain_support import lc_llm - 24from askai.core.support.shared_instances import shared - 25from googleapiclient.errors import HttpError - 26from hspylib.core.metaclass.singleton import Singleton - 27from hspylib.core.zoned_datetime import now - 28from langchain.chains.combine_documents import create_stuff_documents_chain - 29from langchain_community.document_loaders.web_base import WebBaseLoader - 30from langchain_community.vectorstores.chroma import Chroma - 31from langchain_core.documents import Document - 32from langchain_core.output_parsers import StrOutputParser - 33from langchain_core.prompts import ChatPromptTemplate, PromptTemplate - 34from langchain_core.runnables import RunnablePassthrough - 35from langchain_core.runnables.utils import Output - 36from langchain_core.tools import Tool - 37from langchain_google_community import GoogleSearchAPIWrapper - 38from langchain_text_splitters import RecursiveCharacterTextSplitter - 39from typing import List - 40 - 41import bs4 - 42import logging as log - 43import re - 44 - 45 - 46class InternetService(metaclass=Singleton): - 47 """Provide a internet search service to complete queries that require realtime data.""" + 15from askai.__classpath__ import API_KEYS + 16from askai.core.askai_configs import configs + 17from askai.core.askai_events import events + 18from askai.core.askai_messages import msg + 19from askai.core.askai_prompt import prompt + 20from askai.core.component.geo_location import geo_location + 21from askai.core.component.summarizer import summarizer + 22from askai.core.engine.openai.temperature import Temperature + 23from askai.core.model.ai_reply import AIReply + 24from askai.core.model.search_result import SearchResult + 25from askai.core.support.langchain_support import lc_llm + 26from askai.core.support.shared_instances import shared + 27from googleapiclient.errors import HttpError + 28from hspylib.core.metaclass.singleton import Singleton + 29from hspylib.core.zoned_datetime import now + 30from langchain.chains.combine_documents import create_stuff_documents_chain + 31from langchain_community.document_loaders.web_base import WebBaseLoader + 32from langchain_community.vectorstores.chroma import Chroma + 33from langchain_core.documents import Document + 34from langchain_core.output_parsers import StrOutputParser + 35from langchain_core.prompts import ChatPromptTemplate, PromptTemplate + 36from langchain_core.runnables import RunnablePassthrough + 37from langchain_core.runnables.utils import Output + 38from langchain_core.tools import Tool + 39from langchain_google_community import GoogleSearchAPIWrapper + 40from langchain_text_splitters import RecursiveCharacterTextSplitter + 41from textwrap import dedent + 42from typing import List, Literal + 43 + 44import bs4 + 45import logging as log + 46import re + 47 48 - 49 INSTANCE: "InternetService" - 50 - 51 ASKAI_INTERNET_DATA_KEY = "askai-internet-data" - 52 - 53 @staticmethod - 54 def _build_google_query(search: SearchResult) -> str: - 55 """Build a Google Search query from search parameters. - 56 :param search: The AI search parameters. - 57 """ - 58 query = "" - 59 # Gather the sites to be used in te search. - 60 if search.sites: - 61 query += f" {' OR '.join(['site:' + url for url in search.sites])}" - 62 # Weather is a filter that does not require any other search parameter. - 63 if search.filters and any(f.find("weather:") >= 0 for f in search.filters): - 64 return query + ' ' + re.sub(r"^weather:(.*)", r'weather:"\1"', " AND ".join(search.filters)) - 65 # We want to find pages containing the exact name of the person. - 66 if search.filters and any(f.find("people:") >= 0 for f in search.filters): - 67 return query + ' ' + f" intext:\"{'+'.join([f.split(':')[1] for f in search.filters])}\" " - 68 # Make the search query using the provided keywords. - 69 if search.keywords: - 70 query = f"{' '.join(search.keywords)} {query} " - 71 - 72 return query - 73 - 74 @staticmethod - 75 def wrap_response(terms: str, output: str, sites: list[str]) -> str: - 76 return ( - 77 "Your search returned the following:\n" - 78 f"\n{output}\n" - 79 f"\n---" - 80 f"\nSources: {', '.join(sites)}\n" - 81 f"\n* Accessed: {geo_location.location} {now('%d %B, %Y')}*" - 82 f"\n>  Terms: {terms}\n" - 83 ) - 84 - 85 def __init__(self): - 86 self._google = GoogleSearchAPIWrapper() - 87 self._tool = Tool( - 88 name="google_search", description="Search Google for recent results.", - 89 func=self._google.run - 90 ) - 91 self._text_splitter = RecursiveCharacterTextSplitter( - 92 chunk_size=configs.chunk_size, chunk_overlap=configs.chunk_overlap) - 93 - 94 def refine_template(self) -> str: - 95 return prompt.read_prompt("refine-search") - 96 - 97 def search_google(self, search: SearchResult) -> str: - 98 """Search the web using google search API. - 99 Google search operators: https://ahrefs.com/blog/google-advanced-search-operators/ -100 :param search: The AI search parameters. -101 """ -102 events.reply.emit(message=msg.searching()) -103 search.sites = search.sites or ["google.com", "bing.com", "duckduckgo.com", "ask.com"] -104 terms = self._build_google_query(search).strip() -105 try: -106 log.info("Searching Google for '%s'", terms) -107 events.reply.emit(message=msg.final_query(terms), verbosity="debug") -108 ctx = str(self._tool.run(terms)) -109 llm_prompt = ChatPromptTemplate.from_messages([("system", "{query}\n\n{context}")]) -110 context: List[Document] = [Document(ctx)] -111 chain = create_stuff_documents_chain( -112 lc_llm.create_chat_model(temperature=Temperature.CREATIVE_WRITING.temp), llm_prompt -113 ) -114 output = chain.invoke({"query": search.question, "context": context}) -115 except HttpError as err: -116 output = msg.fail_to_search(str(err)) -117 -118 return self.refine_search(search.question, output, terms, search.sites) -119 -120 def scrap_sites(self, search: SearchResult) -> str: -121 """Scrap a web page and summarize it's contents. -122 :param search: The AI search parameters. -123 """ -124 events.reply.emit(message=msg.scrapping()) -125 if len(search.sites) > 0: -126 log.info("Scrapping sites: '%s'", str(", ".join(search.sites))) -127 loader = WebBaseLoader( -128 web_paths=search.sites, -129 bs_kwargs=dict(parse_only=bs4.SoupStrainer(["article", "span", "div", "h1", "h2", "h3"])), -130 ) -131 if (page_content := loader.load()) and len(page_content) > 0: -132 splits: List[Document] = summarizer.text_splitter.split_documents(page_content) -133 v_store = Chroma.from_documents(splits, lc_llm.create_embeddings()) -134 retriever = v_store.as_retriever() -135 scrap_prompt = PromptTemplate( -136 input_variables=["context", "question"], template=prompt.read_prompt("qstring") -137 ) -138 -139 def format_docs(docs): -140 return "\n\n".join(doc.page_content for doc in docs) -141 -142 rag_chain = ( -143 {"context": retriever | format_docs, "question": RunnablePassthrough()} -144 | scrap_prompt -145 | lc_llm.create_model() -146 | StrOutputParser() -147 ) -148 -149 output: Output = rag_chain.invoke(search.question) -150 # cleanup -151 v_store.delete_collection() -152 log.info("Scrapping sites returned: '%s'", str(output)) -153 -154 return self.refine_search(search.question, str(output), "", search.sites) -155 return msg.no_output("search") -156 -157 def refine_search(self, question: str, result: str, terms: str, sites: list[str]) -> str: -158 """Refines the text retrieved by the search engine. -159 :param question: The user question, used to refine the context. -160 :param result: The search result to refine. -161 :param terms: The terms used on the Google search. -162 :param sites: The list of source sites used on the search. -163 """ -164 refine_prompt = PromptTemplate.from_template(self.refine_template()).format( -165 idiom=shared.idiom, -166 sources=sites, -167 location=geo_location.location, -168 datetime=geo_location.datetime, -169 result=result, -170 question=question, -171 ) -172 log.info("STT::[QUESTION] '%s'", result) -173 llm = lc_llm.create_chat_model(temperature=Temperature.CREATIVE_WRITING.temp) -174 -175 if (response := llm.invoke(refine_prompt)) and (output := response.content): -176 output = output -177 else: -178 output = "The search did not bring any result" -179 -180 return self.wrap_response(terms, output, sites) -181 -182 -183assert (internet := InternetService().INSTANCE) is not None + 49class InternetService(metaclass=Singleton): + 50 """Provide an internet search service to complete queries that require real-time data. This service allows for the + 51 retrieval of up-to-date information from the web, enabling queries that depend on current data. + 52 """ + 53 + 54 INSTANCE: "InternetService" + 55 + 56 ASKAI_INTERNET_DATA_KEY = "askai-internet-data" + 57 + 58 @staticmethod + 59 def _build_google_query(search: SearchResult) -> str: + 60 """Build a Google Search query from the provided search parameters. + 61 :param search: The AI search parameters encapsulated in a SearchResult object. + 62 :return: A string representing the constructed Google Search query. + 63 """ + 64 # The order of conditions is important here, as the execution may stop early if a condition is met. + 65 final_query = "" + 66 # Gather the sites to be used in the search. + 67 if search.sites: + 68 final_query += f" {' OR '.join(['site:' + url for url in search.sites])}" + 69 # Weather is a filter that does not require any other search parameters. + 70 if search.filters and any(f.find("weather:") >= 0 for f in search.filters): + 71 return final_query + " " + re.sub(r"^weather:(.*)", r'weather:"\1"', " AND ".join(search.filters)) + 72 # We want to find pages containing the exact name of the person. + 73 if search.filters and any(f.find("people:") >= 0 for f in search.filters): + 74 return final_query + " " + f" intext:\"{'+'.join([f.split(':')[1] for f in search.filters])}\" " + 75 # Make the search query using the provided keywords. + 76 if search.keywords: + 77 final_query = f"{' '.join(search.keywords)} {final_query} " + 78 + 79 return final_query + 80 + 81 @staticmethod + 82 def _wrap_response(terms: str, output: str, sites: list[str], method: Literal["Google", "Other"] = "Google") -> str: + 83 """Format and wrap the search response based on the search terms, output, and method used. + 84 :param terms: The search terms used in the query. + 85 :param output: The raw output or results from the search. + 86 :param sites: A list of websites included in or relevant to the search results. + 87 :param method: The search method used, either 'Google' or 'Other'. + 88 :return: A formatted string that encapsulates the search response. + 89 """ + 90 method_icon = {"google": "", "other": ""} + 91 return dedent( + 92 f""" + 93 Your {method.title()} search returned the following: + 94 {output} + 95 \n---\n + 96 Sources: {', '.join(sites)} + 97 *{method_icon[method]} Accessed: {geo_location.location} {now('%d %B, %Y')}* + 98 >  Terms: {terms}""" + 99 ).strip() +100 +101 def __init__(self): +102 API_KEYS.ensure("GOOGLE_API_KEY", "google_search") +103 self._google = GoogleSearchAPIWrapper(google_api_key=API_KEYS.GOOGLE_API_KEY) +104 self._tool = Tool(name="google_search", description="Search Google for recent results.", func=self._google.run) +105 self._text_splitter = RecursiveCharacterTextSplitter( +106 chunk_size=configs.chunk_size, chunk_overlap=configs.chunk_overlap +107 ) +108 +109 @property +110 def refine_template(self) -> str: +111 return prompt.read_prompt("refine-search") +112 +113 def google_search(self, search: SearchResult) -> str: +114 """Search the web using the Google Search API. This method utilizes advanced Google search operators to refine +115 and execute the search. +116 Reference: https://ahrefs.com/blog/google-advanced-search-operators/ +117 :param search: The AI search parameters encapsulated in a SearchResult object. +118 :return: A refined string containing the search results. +119 """ +120 events.reply.emit(reply=AIReply.info(msg.searching())) +121 search.sites = search.sites or ["google.com", "bing.com", "duckduckgo.com", "ask.com"] +122 terms = self._build_google_query(search).strip() +123 try: +124 log.info("Searching Google for '%s'", terms) +125 events.reply.emit(reply=AIReply.debug(msg.final_query(terms))) +126 ctx = str(self._tool.run(terms)) +127 llm_prompt = ChatPromptTemplate.from_messages([("system", "{query}\n\n{context}")]) +128 context: List[Document] = [Document(ctx)] +129 chain = create_stuff_documents_chain( +130 lc_llm.create_chat_model(temperature=Temperature.CREATIVE_WRITING.temp), llm_prompt +131 ) +132 output = chain.invoke({"query": search.question, "context": context}) +133 except HttpError as err: +134 return msg.fail_to_search(str(err)) +135 +136 return self.refine_search(search.question, output, terms, search.sites) +137 +138 def scrap_sites(self, search: SearchResult) -> str: +139 """Scrape a web page and summarize its contents. +140 :param search: The AI search parameters encapsulated in a SearchResult object. +141 :return: A string containing the summarized contents of the scraped web page. +142 """ +143 events.reply.emit(reply=AIReply.info(msg.scrapping())) +144 if len(search.sites) > 0: +145 log.info("Scrapping sites: '%s'", str(", ".join(search.sites))) +146 loader = WebBaseLoader( +147 web_paths=search.sites, +148 bs_kwargs=dict(parse_only=bs4.SoupStrainer(["article", "span", "div", "h1", "h2", "h3"])), +149 ) +150 if (page_content := loader.load()) and len(page_content) > 0: +151 splits: List[Document] = summarizer.text_splitter.split_documents(page_content) +152 v_store = Chroma.from_documents(splits, lc_llm.create_embeddings()) +153 retriever = v_store.as_retriever() +154 scrap_prompt = PromptTemplate( +155 input_variables=["context", "question"], template=prompt.read_prompt("qstring") +156 ) +157 +158 def _format_docs(docs) -> str: +159 return "\n\n".join(doc.page_content for doc in docs) +160 +161 rag_chain = ( +162 {"context": retriever | _format_docs, "question": RunnablePassthrough()} +163 | scrap_prompt +164 | lc_llm.create_model() +165 | StrOutputParser() +166 ) +167 +168 output: Output = rag_chain.invoke(search.question) +169 v_store.delete_collection() # cleanup +170 log.info("Scrapping sites returned: '%s'", str(output)) +171 return self.refine_search(search.question, str(output), "", search.sites) +172 return msg.no_output("search") +173 +174 def refine_search(self, question: str, result: str, terms: str, sites: list[str]) -> str: +175 """Refine the text retrieved by the search engine. +176 :param question: The user's question, used to refine and contextualize the search results. +177 :param result: The raw search result text that needs refinement. +178 :param terms: The search terms used in the Google search. +179 :param sites: The list of source sites that were included in the search. +180 :return: A refined version of the search result text, tailored to better answer the user's question. +181 """ +182 refine_prompt = PromptTemplate.from_template(self.refine_template).format( +183 idiom=shared.idiom, +184 sources=sites, +185 location=geo_location.location, +186 datetime=geo_location.datetime, +187 result=result, +188 question=question, +189 ) +190 log.info("STT::[QUESTION] '%s'", result) +191 llm = lc_llm.create_chat_model(temperature=Temperature.CREATIVE_WRITING.temp) +192 +193 if (response := llm.invoke(refine_prompt)) and (output := response.content): +194 return self._wrap_response(terms, output, sites) +195 +196 return msg.no_good_result() +197 +198 +199assert (internet := InternetService().INSTANCE) is not None

  • @@ -289,145 +302,159 @@

    -
     47class InternetService(metaclass=Singleton):
    - 48    """Provide a internet search service to complete queries that require realtime data."""
    - 49
    - 50    INSTANCE: "InternetService"
    - 51
    - 52    ASKAI_INTERNET_DATA_KEY = "askai-internet-data"
    - 53
    - 54    @staticmethod
    - 55    def _build_google_query(search: SearchResult) -> str:
    - 56        """Build a Google Search query from search parameters.
    - 57        :param search: The AI search parameters.
    - 58        """
    - 59        query = ""
    - 60        # Gather the sites to be used in te search.
    - 61        if search.sites:
    - 62            query += f" {' OR '.join(['site:' + url for url in search.sites])}"
    - 63        # Weather is a filter that does not require any other search parameter.
    - 64        if search.filters and any(f.find("weather:") >= 0 for f in search.filters):
    - 65            return query + ' ' + re.sub(r"^weather:(.*)", r'weather:"\1"', " AND ".join(search.filters))
    - 66        # We want to find pages containing the exact name of the person.
    - 67        if search.filters and any(f.find("people:") >= 0 for f in search.filters):
    - 68            return query + ' ' + f" intext:\"{'+'.join([f.split(':')[1] for f in search.filters])}\" "
    - 69        # Make the search query using the provided keywords.
    - 70        if search.keywords:
    - 71            query = f"{' '.join(search.keywords)} {query} "
    - 72
    - 73        return query
    - 74
    - 75    @staticmethod
    - 76    def wrap_response(terms: str, output: str, sites: list[str]) -> str:
    - 77        return (
    - 78            "Your search returned the following:\n"
    - 79            f"\n{output}\n"
    - 80            f"\n---"
    - 81            f"\nSources: {', '.join(sites)}\n"
    - 82            f"\n* Accessed: {geo_location.location} {now('%d %B, %Y')}*"
    - 83            f"\n>  Terms: {terms}\n"
    - 84        )
    - 85
    - 86    def __init__(self):
    - 87        self._google = GoogleSearchAPIWrapper()
    - 88        self._tool = Tool(
    - 89            name="google_search", description="Search Google for recent results.",
    - 90            func=self._google.run
    - 91        )
    - 92        self._text_splitter = RecursiveCharacterTextSplitter(
    - 93            chunk_size=configs.chunk_size, chunk_overlap=configs.chunk_overlap)
    - 94
    - 95    def refine_template(self) -> str:
    - 96        return prompt.read_prompt("refine-search")
    - 97
    - 98    def search_google(self, search: SearchResult) -> str:
    - 99        """Search the web using google search API.
    -100        Google search operators: https://ahrefs.com/blog/google-advanced-search-operators/
    -101        :param search: The AI search parameters.
    -102        """
    -103        events.reply.emit(message=msg.searching())
    -104        search.sites = search.sites or ["google.com", "bing.com", "duckduckgo.com", "ask.com"]
    -105        terms = self._build_google_query(search).strip()
    -106        try:
    -107            log.info("Searching Google for '%s'", terms)
    -108            events.reply.emit(message=msg.final_query(terms), verbosity="debug")
    -109            ctx = str(self._tool.run(terms))
    -110            llm_prompt = ChatPromptTemplate.from_messages([("system", "{query}\n\n{context}")])
    -111            context: List[Document] = [Document(ctx)]
    -112            chain = create_stuff_documents_chain(
    -113                lc_llm.create_chat_model(temperature=Temperature.CREATIVE_WRITING.temp), llm_prompt
    -114            )
    -115            output = chain.invoke({"query": search.question, "context": context})
    -116        except HttpError as err:
    -117            output = msg.fail_to_search(str(err))
    -118
    -119        return self.refine_search(search.question, output, terms, search.sites)
    -120
    -121    def scrap_sites(self, search: SearchResult) -> str:
    -122        """Scrap a web page and summarize it's contents.
    -123        :param search: The AI search parameters.
    -124        """
    -125        events.reply.emit(message=msg.scrapping())
    -126        if len(search.sites) > 0:
    -127            log.info("Scrapping sites: '%s'", str(", ".join(search.sites)))
    -128            loader = WebBaseLoader(
    -129                web_paths=search.sites,
    -130                bs_kwargs=dict(parse_only=bs4.SoupStrainer(["article", "span", "div", "h1", "h2", "h3"])),
    -131            )
    -132            if (page_content := loader.load()) and len(page_content) > 0:
    -133                splits: List[Document] = summarizer.text_splitter.split_documents(page_content)
    -134                v_store = Chroma.from_documents(splits, lc_llm.create_embeddings())
    -135                retriever = v_store.as_retriever()
    -136                scrap_prompt = PromptTemplate(
    -137                    input_variables=["context", "question"], template=prompt.read_prompt("qstring")
    -138                )
    -139
    -140                def format_docs(docs):
    -141                    return "\n\n".join(doc.page_content for doc in docs)
    -142
    -143                rag_chain = (
    -144                    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    -145                    | scrap_prompt
    -146                    | lc_llm.create_model()
    -147                    | StrOutputParser()
    -148                )
    -149
    -150                output: Output = rag_chain.invoke(search.question)
    -151                # cleanup
    -152                v_store.delete_collection()
    -153                log.info("Scrapping sites returned: '%s'", str(output))
    -154
    -155                return self.refine_search(search.question, str(output), "", search.sites)
    -156        return msg.no_output("search")
    -157
    -158    def refine_search(self, question: str, result: str, terms: str, sites: list[str]) -> str:
    -159        """Refines the text retrieved by the search engine.
    -160        :param question: The user question, used to refine the context.
    -161        :param result: The search result to refine.
    -162        :param terms: The terms used on the Google search.
    -163        :param sites: The list of source sites used on the search.
    -164        """
    -165        refine_prompt = PromptTemplate.from_template(self.refine_template()).format(
    -166            idiom=shared.idiom,
    -167            sources=sites,
    -168            location=geo_location.location,
    -169            datetime=geo_location.datetime,
    -170            result=result,
    -171            question=question,
    -172        )
    -173        log.info("STT::[QUESTION] '%s'", result)
    -174        llm = lc_llm.create_chat_model(temperature=Temperature.CREATIVE_WRITING.temp)
    -175
    -176        if (response := llm.invoke(refine_prompt)) and (output := response.content):
    -177            output = output
    -178        else:
    -179            output = "The search did not bring any result"
    -180
    -181        return self.wrap_response(terms, output, sites)
    +            
     50class InternetService(metaclass=Singleton):
    + 51    """Provide an internet search service to complete queries that require real-time data. This service allows for the
    + 52    retrieval of up-to-date information from the web, enabling queries that depend on current data.
    + 53    """
    + 54
    + 55    INSTANCE: "InternetService"
    + 56
    + 57    ASKAI_INTERNET_DATA_KEY = "askai-internet-data"
    + 58
    + 59    @staticmethod
    + 60    def _build_google_query(search: SearchResult) -> str:
    + 61        """Build a Google Search query from the provided search parameters.
    + 62        :param search: The AI search parameters encapsulated in a SearchResult object.
    + 63        :return: A string representing the constructed Google Search query.
    + 64        """
    + 65        # The order of conditions is important here, as the execution may stop early if a condition is met.
    + 66        final_query = ""
    + 67        # Gather the sites to be used in the search.
    + 68        if search.sites:
    + 69            final_query += f" {' OR '.join(['site:' + url for url in search.sites])}"
    + 70        # Weather is a filter that does not require any other search parameters.
    + 71        if search.filters and any(f.find("weather:") >= 0 for f in search.filters):
    + 72            return final_query + " " + re.sub(r"^weather:(.*)", r'weather:"\1"', " AND ".join(search.filters))
    + 73        # We want to find pages containing the exact name of the person.
    + 74        if search.filters and any(f.find("people:") >= 0 for f in search.filters):
    + 75            return final_query + " " + f" intext:\"{'+'.join([f.split(':')[1] for f in search.filters])}\" "
    + 76        # Make the search query using the provided keywords.
    + 77        if search.keywords:
    + 78            final_query = f"{' '.join(search.keywords)} {final_query} "
    + 79
    + 80        return final_query
    + 81
    + 82    @staticmethod
    + 83    def _wrap_response(terms: str, output: str, sites: list[str], method: Literal["Google", "Other"] = "Google") -> str:
    + 84        """Format and wrap the search response based on the search terms, output, and method used.
    + 85        :param terms: The search terms used in the query.
    + 86        :param output: The raw output or results from the search.
    + 87        :param sites: A list of websites included in or relevant to the search results.
    + 88        :param method: The search method used, either 'Google' or 'Other'.
    + 89        :return: A formatted string that encapsulates the search response.
    + 90        """
    + 91        method_icon = {"google": "", "other": ""}
    + 92        return dedent(
    + 93            f"""
    + 94            Your {method.title()} search returned the following:
    + 95            {output}
    + 96            \n---\n
    + 97            Sources: {', '.join(sites)}
    + 98            *{method_icon[method]} Accessed: {geo_location.location} {now('%d %B, %Y')}*
    + 99            >  Terms: {terms}"""
    +100        ).strip()
    +101
    +102    def __init__(self):
    +103        API_KEYS.ensure("GOOGLE_API_KEY", "google_search")
    +104        self._google = GoogleSearchAPIWrapper(google_api_key=API_KEYS.GOOGLE_API_KEY)
    +105        self._tool = Tool(name="google_search", description="Search Google for recent results.", func=self._google.run)
    +106        self._text_splitter = RecursiveCharacterTextSplitter(
    +107            chunk_size=configs.chunk_size, chunk_overlap=configs.chunk_overlap
    +108        )
    +109
    +110    @property
    +111    def refine_template(self) -> str:
    +112        return prompt.read_prompt("refine-search")
    +113
    +114    def google_search(self, search: SearchResult) -> str:
    +115        """Search the web using the Google Search API. This method utilizes advanced Google search operators to refine
    +116        and execute the search.
    +117        Reference: https://ahrefs.com/blog/google-advanced-search-operators/
    +118        :param search: The AI search parameters encapsulated in a SearchResult object.
    +119        :return: A refined string containing the search results.
    +120        """
    +121        events.reply.emit(reply=AIReply.info(msg.searching()))
    +122        search.sites = search.sites or ["google.com", "bing.com", "duckduckgo.com", "ask.com"]
    +123        terms = self._build_google_query(search).strip()
    +124        try:
    +125            log.info("Searching Google for '%s'", terms)
    +126            events.reply.emit(reply=AIReply.debug(msg.final_query(terms)))
    +127            ctx = str(self._tool.run(terms))
    +128            llm_prompt = ChatPromptTemplate.from_messages([("system", "{query}\n\n{context}")])
    +129            context: List[Document] = [Document(ctx)]
    +130            chain = create_stuff_documents_chain(
    +131                lc_llm.create_chat_model(temperature=Temperature.CREATIVE_WRITING.temp), llm_prompt
    +132            )
    +133            output = chain.invoke({"query": search.question, "context": context})
    +134        except HttpError as err:
    +135            return msg.fail_to_search(str(err))
    +136
    +137        return self.refine_search(search.question, output, terms, search.sites)
    +138
    +139    def scrap_sites(self, search: SearchResult) -> str:
    +140        """Scrape a web page and summarize its contents.
    +141        :param search: The AI search parameters encapsulated in a SearchResult object.
    +142        :return: A string containing the summarized contents of the scraped web page.
    +143        """
    +144        events.reply.emit(reply=AIReply.info(msg.scrapping()))
    +145        if len(search.sites) > 0:
    +146            log.info("Scrapping sites: '%s'", str(", ".join(search.sites)))
    +147            loader = WebBaseLoader(
    +148                web_paths=search.sites,
    +149                bs_kwargs=dict(parse_only=bs4.SoupStrainer(["article", "span", "div", "h1", "h2", "h3"])),
    +150            )
    +151            if (page_content := loader.load()) and len(page_content) > 0:
    +152                splits: List[Document] = summarizer.text_splitter.split_documents(page_content)
    +153                v_store = Chroma.from_documents(splits, lc_llm.create_embeddings())
    +154                retriever = v_store.as_retriever()
    +155                scrap_prompt = PromptTemplate(
    +156                    input_variables=["context", "question"], template=prompt.read_prompt("qstring")
    +157                )
    +158
    +159                def _format_docs(docs) -> str:
    +160                    return "\n\n".join(doc.page_content for doc in docs)
    +161
    +162                rag_chain = (
    +163                    {"context": retriever | _format_docs, "question": RunnablePassthrough()}
    +164                    | scrap_prompt
    +165                    | lc_llm.create_model()
    +166                    | StrOutputParser()
    +167                )
    +168
    +169                output: Output = rag_chain.invoke(search.question)
    +170                v_store.delete_collection()  # cleanup
    +171                log.info("Scrapping sites returned: '%s'", str(output))
    +172                return self.refine_search(search.question, str(output), "", search.sites)
    +173        return msg.no_output("search")
    +174
    +175    def refine_search(self, question: str, result: str, terms: str, sites: list[str]) -> str:
    +176        """Refine the text retrieved by the search engine.
    +177        :param question: The user's question, used to refine and contextualize the search results.
    +178        :param result: The raw search result text that needs refinement.
    +179        :param terms: The search terms used in the Google search.
    +180        :param sites: The list of source sites that were included in the search.
    +181        :return: A refined version of the search result text, tailored to better answer the user's question.
    +182        """
    +183        refine_prompt = PromptTemplate.from_template(self.refine_template).format(
    +184            idiom=shared.idiom,
    +185            sources=sites,
    +186            location=geo_location.location,
    +187            datetime=geo_location.datetime,
    +188            result=result,
    +189            question=question,
    +190        )
    +191        log.info("STT::[QUESTION] '%s'", result)
    +192        llm = lc_llm.create_chat_model(temperature=Temperature.CREATIVE_WRITING.temp)
    +193
    +194        if (response := llm.invoke(refine_prompt)) and (output := response.content):
    +195            return self._wrap_response(terms, output, sites)
    +196
    +197        return msg.no_good_result()
     
    -

    Provide a internet search service to complete queries that require realtime data.

    +

    Provide an internet search service to complete queries that require real-time data. This service allows for the +retrieval of up-to-date information from the web, enabling queries that depend on current data.

    @@ -484,98 +511,78 @@

    -

    -
    - -
    -
    @staticmethod
    - - def - wrap_response(terms: str, output: str, sites: list[str]) -> str: - - - -
    - -
    75    @staticmethod
    -76    def wrap_response(terms: str, output: str, sites: list[str]) -> str:
    -77        return (
    -78            "Your search returned the following:\n"
    -79            f"\n{output}\n"
    -80            f"\n---"
    -81            f"\nSources: {', '.join(sites)}\n"
    -82            f"\n* Accessed: {geo_location.location} {now('%d %B, %Y')}*"
    -83            f"\n>  Terms: {terms}\n"
    -84        )
    -
    - - - -
    -
    - - def - refine_template(self) -> str: +
    + refine_template: str
    -
    95    def refine_template(self) -> str:
    -96        return prompt.read_prompt("refine-search")
    +            
    110    @property
    +111    def refine_template(self) -> str:
    +112        return prompt.read_prompt("refine-search")
     
    -
    - +

    -
    - +
    +
    -
    @dataclass
    - + class - AIReply: + AIVision(typing.Protocol): - +
    - -
    20@dataclass
    -21class AIReply:
    -22    """Data class that represent AI replies."""
    -23
    -24    message: str = None
    -25    is_success: bool = None
    -26
    -27    def __str__(self):
    -28        return f"Success = {self.is_success}  \nMessage = {self.message}"
    +    
    +            
    20class AIVision(Protocol):
    +21    """Provide an interface for AI vision."""
    +22
    +23    def caption(self, filename: AnyPath, load_dir: AnyPath | None = None) -> str:
    +24        """Generate a caption for the provided image.
    +25        :param filename: File name of the image for which the caption is to be generated.
    +26        :param load_dir: Optional directory path for loading related resources.
    +27        :return: A dictionary containing the generated caption.
    +28        """
    +29        ...
     
    -

    Data class that represent AI replies.

    +

    Provide an interface for AI vision.

    -
    -
    +
    + +
    - AIReply(message: str = None, is_success: bool = None) + AIVision(*args, **kwargs) + + -
    - - - + +
    1953def _no_init_or_replace_init(self, *args, **kwargs):
    +1954    cls = type(self)
    +1955
    +1956    if cls._is_protocol:
    +1957        raise TypeError('Protocols cannot be instantiated')
    +1958
    +1959    # Already using a custom `__init__`. No need to calculate correct
    +1960    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
    +1961    if cls.__init__ is not _no_init_or_replace_init:
    +1962        return
    +1963
    +1964    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
    +1965    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
    +1966    # searches for a proper new `__init__` in the MRO. The new `__init__`
    +1967    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
    +1968    # instantiation of the protocol subclass will thus use the new
    +1969    # `__init__` and no longer call `_no_init_or_replace_init`.
    +1970    for base in cls.__mro__:
    +1971        init = base.__dict__.get('__init__', _no_init_or_replace_init)
    +1972        if init is not _no_init_or_replace_init:
    +1973            cls.__init__ = init
    +1974            break
    +1975    else:
    +1976        # should not happen
    +1977        cls.__init__ = object.__init__
    +1978
    +1979    cls.__init__(self, *args, **kwargs)
    +
    -
    -
    -
    - message: str = -None - -
    - -
    -
    -
    - is_success: bool = -None +
    + +
    + + def + caption( self, filename: Union[pathlib.Path, str, NoneType], load_dir: Union[pathlib.Path, str, NoneType] = None) -> str: + + -
    - - - + +
    23    def caption(self, filename: AnyPath, load_dir: AnyPath | None = None) -> str:
    +24        """Generate a caption for the provided image.
    +25        :param filename: File name of the image for which the caption is to be generated.
    +26        :param load_dir: Optional directory path for loading related resources.
    +27        :return: A dictionary containing the generated caption.
    +28        """
    +29        ...
    +
    + + +

    Generate a caption for the provided image.

    + +
    Parameters
    + +
      +
    • filename: File name of the image for which the caption is to be generated.
    • +
    • load_dir: Optional directory path for loading related resources.
    • +
    + +
    Returns
    + +
    +

    A dictionary containing the generated caption.

    +
    +
    +
    diff --git a/docs/api/askai/main/askai/core/engine/engine_factory.html b/docs/api/askai/main/askai/core/engine/engine_factory.html index b9aa2618..40b2b46d 100644 --- a/docs/api/askai/main/askai/core/engine/engine_factory.html +++ b/docs/api/askai/main/askai/core/engine/engine_factory.html @@ -3,7 +3,7 @@ - + main.askai.core.engine.engine_factory API documentation @@ -101,21 +101,21 @@

    20from askai.exception.exceptions import NoSuchEngineError 21from hspylib.core.metaclass.singleton import Singleton 22from hspylib.core.preconditions import check_not_none -23from typing import List +23 24 -25 -26class EngineFactory(metaclass=Singleton): -27 """TODO""" -28 -29 INSTANCE: "EngineFactory" -30 -31 _ACTIVE_AI_ENGINE: AIEngine | None = None -32 -33 @classmethod -34 def create_engine(cls, engine_name: str | List[str], engine_model: str | List[str]) -> AIEngine: -35 """Find the suitable AI engine according to the provided engine name. -36 :param engine_name: the AI engine name. -37 :param engine_model: the AI engine model. +25class EngineFactory(metaclass=Singleton): +26 """Factory class to create AI engines.""" +27 +28 INSTANCE: "EngineFactory" +29 +30 _ACTIVE_AI_ENGINE: AIEngine | None = None +31 +32 @classmethod +33 def create_engine(cls, engine_name: str | list[str], engine_model: str | list[str]) -> AIEngine: +34 """Create the suitable AI engine according to the provided engine name. +35 :param engine_name: The AI engine name(s). +36 :param engine_model: The AI engine model(s). +37 :return: An instance of AIEngine. 38 """ 39 engine_name = engine_name[0] if isinstance(engine_name, list) else engine_name 40 model_name = engine_model[0] if isinstance(engine_model, list) else engine_model @@ -124,7 +124,7 @@

    43 model: AIModel = OpenAIModel.of_name(model_name) if model_name else None 44 cls._ACTIVE_AI_ENGINE = OpenAIEngine(model or OpenAIModel.GPT_4_O_MINI) 45 case "gemini": -46 raise NoSuchEngineError("Google 'paml' is not yet implemented!") +46 raise NoSuchEngineError("Google 'gemini' is not yet implemented!") 47 case _: 48 raise NoSuchEngineError(f"Engine name: {engine_name} model: {engine_model}") 49 @@ -132,10 +132,14 @@

    51 52 @classmethod 53 def active_ai(cls) -> AIEngine: -54 return check_not_none(cls._ACTIVE_AI_ENGINE, "No AI engine has been created yet!") -55 -56 -57assert EngineFactory().INSTANCE is not None +54 """Get the currently active AI engine. +55 :return: The active AI engine. +56 :raises TypeError: If no AI engine has been created yet. +57 """ +58 return check_not_none(cls._ACTIVE_AI_ENGINE, "No AI engine has been created yet!") +59 +60 +61assert EngineFactory().INSTANCE is not None @@ -151,18 +155,19 @@

    -
    27class EngineFactory(metaclass=Singleton):
    -28    """TODO"""
    -29
    -30    INSTANCE: "EngineFactory"
    -31
    -32    _ACTIVE_AI_ENGINE: AIEngine | None = None
    -33
    -34    @classmethod
    -35    def create_engine(cls, engine_name: str | List[str], engine_model: str | List[str]) -> AIEngine:
    -36        """Find the suitable AI engine according to the provided engine name.
    -37        :param engine_name: the AI engine name.
    -38        :param engine_model: the AI engine model.
    +            
    26class EngineFactory(metaclass=Singleton):
    +27    """Factory class to create AI engines."""
    +28
    +29    INSTANCE: "EngineFactory"
    +30
    +31    _ACTIVE_AI_ENGINE: AIEngine | None = None
    +32
    +33    @classmethod
    +34    def create_engine(cls, engine_name: str | list[str], engine_model: str | list[str]) -> AIEngine:
    +35        """Create the suitable AI engine according to the provided engine name.
    +36        :param engine_name: The AI engine name(s).
    +37        :param engine_model: The AI engine model(s).
    +38        :return: An instance of AIEngine.
     39        """
     40        engine_name = engine_name[0] if isinstance(engine_name, list) else engine_name
     41        model_name = engine_model[0] if isinstance(engine_model, list) else engine_model
    @@ -171,7 +176,7 @@ 

    44 model: AIModel = OpenAIModel.of_name(model_name) if model_name else None 45 cls._ACTIVE_AI_ENGINE = OpenAIEngine(model or OpenAIModel.GPT_4_O_MINI) 46 case "gemini": -47 raise NoSuchEngineError("Google 'paml' is not yet implemented!") +47 raise NoSuchEngineError("Google 'gemini' is not yet implemented!") 48 case _: 49 raise NoSuchEngineError(f"Engine name: {engine_name} model: {engine_model}") 50 @@ -179,11 +184,15 @@

    52 53 @classmethod 54 def active_ai(cls) -> AIEngine: -55 return check_not_none(cls._ACTIVE_AI_ENGINE, "No AI engine has been created yet!") +55 """Get the currently active AI engine. +56 :return: The active AI engine. +57 :raises TypeError: If no AI engine has been created yet. +58 """ +59 return check_not_none(cls._ACTIVE_AI_ENGINE, "No AI engine has been created yet!")

    -

    TODO

    +

    Factory class to create AI engines.

    @@ -235,17 +244,18 @@

    @classmethod
    def - create_engine( cls, engine_name: Union[str, List[str]], engine_model: Union[str, List[str]]) -> askai.core.engine.ai_engine.AIEngine: + create_engine( cls, engine_name: str | list[str], engine_model: str | list[str]) -> askai.core.engine.ai_engine.AIEngine:

    -
    34    @classmethod
    -35    def create_engine(cls, engine_name: str | List[str], engine_model: str | List[str]) -> AIEngine:
    -36        """Find the suitable AI engine according to the provided engine name.
    -37        :param engine_name: the AI engine name.
    -38        :param engine_model: the AI engine model.
    +            
    33    @classmethod
    +34    def create_engine(cls, engine_name: str | list[str], engine_model: str | list[str]) -> AIEngine:
    +35        """Create the suitable AI engine according to the provided engine name.
    +36        :param engine_name: The AI engine name(s).
    +37        :param engine_model: The AI engine model(s).
    +38        :return: An instance of AIEngine.
     39        """
     40        engine_name = engine_name[0] if isinstance(engine_name, list) else engine_name
     41        model_name = engine_model[0] if isinstance(engine_model, list) else engine_model
    @@ -254,7 +264,7 @@ 

    44 model: AIModel = OpenAIModel.of_name(model_name) if model_name else None 45 cls._ACTIVE_AI_ENGINE = OpenAIEngine(model or OpenAIModel.GPT_4_O_MINI) 46 case "gemini": -47 raise NoSuchEngineError("Google 'paml' is not yet implemented!") +47 raise NoSuchEngineError("Google 'gemini' is not yet implemented!") 48 case _: 49 raise NoSuchEngineError(f"Engine name: {engine_name} model: {engine_model}") 50 @@ -262,14 +272,20 @@

    -

    Find the suitable AI engine according to the provided engine name.

    +

    Create the suitable AI engine according to the provided engine name.

    Parameters
      -
    • engine_name: the AI engine name.
    • -
    • engine_model: the AI engine model.
    • +
    • engine_name: The AI engine name(s).
    • +
    • engine_model: The AI engine model(s).
    + +
    Returns
    + +
    +

    An instance of AIEngine.

    +
    @@ -288,11 +304,29 @@
    Parameters
    53    @classmethod
     54    def active_ai(cls) -> AIEngine:
    -55        return check_not_none(cls._ACTIVE_AI_ENGINE, "No AI engine has been created yet!")
    +55        """Get the currently active AI engine.
    +56        :return: The active AI engine.
    +57        :raises TypeError: If no AI engine has been created yet.
    +58        """
    +59        return check_not_none(cls._ACTIVE_AI_ENGINE, "No AI engine has been created yet!")
     
    - +

    Get the currently active AI engine.

    + +
    Returns
    + +
    +

    The active AI engine.

    +
    + +
    Raises
    + +
      +
    • TypeError: If no AI engine has been created yet.
    • +
    +
    +

    diff --git a/docs/api/askai/main/askai/core/engine/openai.html b/docs/api/askai/main/askai/core/engine/openai.html index 36b374d5..fab49032 100644 --- a/docs/api/askai/main/askai/core/engine/openai.html +++ b/docs/api/askai/main/askai/core/engine/openai.html @@ -3,7 +3,7 @@ - + main.askai.core.engine.openai API documentation @@ -32,6 +32,7 @@

    Submodules

  • openai_configs
  • openai_engine
  • openai_model
  • +
  • openai_vision
  • temperature
  • @@ -58,7 +59,7 @@

     1# _*_ coding: utf-8 _*_
      2#
    - 3# hspylib-askai v1.0.11
    + 3# hspylib-askai v1.0.13
      4#
      5# Package: main.askai.core.engine.openai
      6"""Package initialization."""
    @@ -67,9 +68,10 @@ 

    9 'openai_configs', 10 'openai_engine', 11 'openai_model', -12 'temperature' -13] -14__version__ = '1.0.11' +12 'openai_vision', +13 'temperature' +14] +15__version__ = '1.0.13'

    diff --git a/docs/api/askai/main/askai/core/engine/openai/openai_configs.html b/docs/api/askai/main/askai/core/engine/openai/openai_configs.html index 72747f76..f0f8322e 100644 --- a/docs/api/askai/main/askai/core/engine/openai/openai_configs.html +++ b/docs/api/askai/main/askai/core/engine/openai/openai_configs.html @@ -3,7 +3,7 @@ - + main.askai.core.engine.openai.openai_configs API documentation @@ -39,9 +39,6 @@

    API Documentation

  • INSTANCE
  • -
  • - RESOURCE_DIR -
  • stt_model
  • @@ -102,62 +99,58 @@

    12 13 Copyright (c) 2024, HomeSetup 14""" -15from askai.__classpath__ import classpath -16from askai.core.askai_configs import AskAiConfigs -17from askai.core.askai_settings import settings -18from hspylib.core.metaclass.singleton import Singleton -19from typing import Literal +15from askai.core.askai_configs import AskAiConfigs +16from askai.core.askai_settings import settings +17from hspylib.core.metaclass.singleton import Singleton +18from typing import Literal +19 20 -21 -22class OpenAiConfigs(AskAiConfigs, metaclass=Singleton): -23 """Provides access to OpenAI configurations.""" -24 -25 INSTANCE: "OpenAiConfigs" -26 -27 # The resources folder -28 RESOURCE_DIR = str(classpath.resource_path()) -29 -30 def __init__(self): -31 super().__init__() -32 self._stt_model = settings.get("openai.speech.to.text.model") -33 self._tts_model = settings.get("openai.text.to.speech.model") -34 self._tts_voice = settings.get("openai.text.to.speech.voice") -35 self._tts_format = settings.get("openai.text.to.speech.audio.format") +21class OpenAiConfigs(AskAiConfigs, metaclass=Singleton): +22 """Provides access to OpenAI configurations.""" +23 +24 INSTANCE: "OpenAiConfigs" +25 +26 def __init__(self): +27 super().__init__() +28 self._stt_model = settings.get("askai.openai.speech.to.text.model") +29 self._tts_model = settings.get("askai.openai.text.to.speech.model") +30 self._tts_voice = settings.get("askai.openai.text.to.speech.voice") +31 self._tts_format = settings.get("askai.openai.text.to.speech.audio.format") +32 +33 @property +34 def stt_model(self) -> Literal["whisper-1"]: +35 return self._stt_model 36 -37 @property -38 def stt_model(self) -> Literal["whisper-1"]: -39 return self._stt_model +37 @stt_model.setter +38 def stt_model(self, value: Literal["whisper-1"]) -> None: +39 self._stt_model = value 40 -41 @stt_model.setter -42 def stt_model(self, value: Literal["whisper-1"]) -> None: -43 self._stt_model = value +41 @property +42 def tts_model(self) -> Literal["tts-1", "tts-1-hd"]: +43 return self._tts_model 44 -45 @property -46 def tts_model(self) -> Literal["tts-1", "tts-1-hd"]: -47 return self._tts_model +45 @tts_model.setter +46 def tts_model(self, value: Literal["tts-1", "tts-1-hd"]) -> None: +47 self._tts_model = value 48 -49 @tts_model.setter -50 def tts_model(self, value: Literal["tts-1", "tts-1-hd"]) -> None: -51 self._tts_model = value +49 @property +50 def tts_voice(self) -> Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]: +51 return self._tts_voice 52 -53 @property -54 def tts_voice(self) -> Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]: -55 return self._tts_voice +53 @tts_voice.setter +54 def tts_voice(self, value: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]) -> None: +55 self._tts_voice = value 56 -57 @tts_voice.setter -58 def tts_voice(self, value: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]) -> None: -59 self._tts_voice = value +57 @property +58 def tts_format(self) -> Literal["mp3", "opus", "aac", "flac"]: +59 return self._tts_format 60 -61 @property -62 def tts_format(self) -> Literal["mp3", "opus", "aac", "flac"]: -63 return self._tts_format +61 @tts_format.setter +62 def tts_format(self, value: Literal["mp3", "opus", "aac", "flac"]) -> None: +63 self._tts_format = value 64 -65 @tts_format.setter -66 def tts_format(self, value: Literal["mp3", "opus", "aac", "flac"]) -> None: -67 self._tts_format = value -68 -69 -70assert OpenAiConfigs().INSTANCE is not None +65 +66assert OpenAiConfigs().INSTANCE is not None @@ -173,52 +166,49 @@

    -
    23class OpenAiConfigs(AskAiConfigs, metaclass=Singleton):
    -24    """Provides access to OpenAI configurations."""
    -25
    -26    INSTANCE: "OpenAiConfigs"
    -27
    -28    # The resources folder
    -29    RESOURCE_DIR = str(classpath.resource_path())
    -30
    -31    def __init__(self):
    -32        super().__init__()
    -33        self._stt_model = settings.get("openai.speech.to.text.model")
    -34        self._tts_model = settings.get("openai.text.to.speech.model")
    -35        self._tts_voice = settings.get("openai.text.to.speech.voice")
    -36        self._tts_format = settings.get("openai.text.to.speech.audio.format")
    +            
    22class OpenAiConfigs(AskAiConfigs, metaclass=Singleton):
    +23    """Provides access to OpenAI configurations."""
    +24
    +25    INSTANCE: "OpenAiConfigs"
    +26
    +27    def __init__(self):
    +28        super().__init__()
    +29        self._stt_model = settings.get("askai.openai.speech.to.text.model")
    +30        self._tts_model = settings.get("askai.openai.text.to.speech.model")
    +31        self._tts_voice = settings.get("askai.openai.text.to.speech.voice")
    +32        self._tts_format = settings.get("askai.openai.text.to.speech.audio.format")
    +33
    +34    @property
    +35    def stt_model(self) -> Literal["whisper-1"]:
    +36        return self._stt_model
     37
    -38    @property
    -39    def stt_model(self) -> Literal["whisper-1"]:
    -40        return self._stt_model
    +38    @stt_model.setter
    +39    def stt_model(self, value: Literal["whisper-1"]) -> None:
    +40        self._stt_model = value
     41
    -42    @stt_model.setter
    -43    def stt_model(self, value: Literal["whisper-1"]) -> None:
    -44        self._stt_model = value
    +42    @property
    +43    def tts_model(self) -> Literal["tts-1", "tts-1-hd"]:
    +44        return self._tts_model
     45
    -46    @property
    -47    def tts_model(self) -> Literal["tts-1", "tts-1-hd"]:
    -48        return self._tts_model
    +46    @tts_model.setter
    +47    def tts_model(self, value: Literal["tts-1", "tts-1-hd"]) -> None:
    +48        self._tts_model = value
     49
    -50    @tts_model.setter
    -51    def tts_model(self, value: Literal["tts-1", "tts-1-hd"]) -> None:
    -52        self._tts_model = value
    +50    @property
    +51    def tts_voice(self) -> Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]:
    +52        return self._tts_voice
     53
    -54    @property
    -55    def tts_voice(self) -> Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]:
    -56        return self._tts_voice
    +54    @tts_voice.setter
    +55    def tts_voice(self, value: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]) -> None:
    +56        self._tts_voice = value
     57
    -58    @tts_voice.setter
    -59    def tts_voice(self, value: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]) -> None:
    -60        self._tts_voice = value
    +58    @property
    +59    def tts_format(self) -> Literal["mp3", "opus", "aac", "flac"]:
    +60        return self._tts_format
     61
    -62    @property
    -63    def tts_format(self) -> Literal["mp3", "opus", "aac", "flac"]:
    -64        return self._tts_format
    -65
    -66    @tts_format.setter
    -67    def tts_format(self, value: Literal["mp3", "opus", "aac", "flac"]) -> None:
    -68        self._tts_format = value
    +62    @tts_format.setter
    +63    def tts_format(self, value: Literal["mp3", "opus", "aac", "flac"]) -> None:
    +64        self._tts_format = value
     
    @@ -268,18 +258,6 @@

    -

    -
    -
    - RESOURCE_DIR = -'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources' - - -
    - - - -
    @@ -290,9 +268,9 @@

    -
    38    @property
    -39    def stt_model(self) -> Literal["whisper-1"]:
    -40        return self._stt_model
    +            
    34    @property
    +35    def stt_model(self) -> Literal["whisper-1"]:
    +36        return self._stt_model
     
    @@ -308,9 +286,9 @@

    -
    46    @property
    -47    def tts_model(self) -> Literal["tts-1", "tts-1-hd"]:
    -48        return self._tts_model
    +            
    42    @property
    +43    def tts_model(self) -> Literal["tts-1", "tts-1-hd"]:
    +44        return self._tts_model
     
    @@ -326,9 +304,9 @@

    -
    54    @property
    -55    def tts_voice(self) -> Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]:
    -56        return self._tts_voice
    +            
    50    @property
    +51    def tts_voice(self) -> Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]:
    +52        return self._tts_voice
     
    @@ -344,9 +322,9 @@

    -
    62    @property
    -63    def tts_format(self) -> Literal["mp3", "opus", "aac", "flac"]:
    -64        return self._tts_format
    +            
    58    @property
    +59    def tts_format(self) -> Literal["mp3", "opus", "aac", "flac"]:
    +60        return self._tts_format
     
    @@ -357,7 +335,8 @@

    Inherited Members
    askai.core.askai_configs.AskAiConfigs
    -
    engine
    +
    RESOURCE_DIR
    +
    engine
    model
    is_interactive
    is_speak
    @@ -365,8 +344,11 @@
    Inherited Members
    is_cache
    tempo
    ttl
    +
    verbosity
    chunk_size
    chunk_overlap
    +
    rag_retrival_amount
    +
    is_rag
    language
    encoding
    max_iteractions
    @@ -385,6 +367,7 @@
    Inherited Members
    recorder_devices
    add_device
    remove_device
    +
    clear_devices
    diff --git a/docs/api/askai/main/askai/core/engine/openai/openai_engine.html b/docs/api/askai/main/askai/core/engine/openai/openai_engine.html index 1a290d3b..1fa203c4 100644 --- a/docs/api/askai/main/askai/core/engine/openai/openai_engine.html +++ b/docs/api/askai/main/askai/core/engine/openai/openai_engine.html @@ -3,7 +3,7 @@ - + main.askai.core.engine.openai.openai_engine API documentation @@ -45,9 +45,18 @@

    API Documentation

  • configs
  • +
  • + nickname +
  • +
  • + models +
  • voices
  • +
  • + vision +
  • lc_model
  • @@ -66,12 +75,6 @@

    API Documentation

  • ai_token_limit
  • -
  • - nickname -
  • -
  • - models -
  • ask
  • @@ -135,153 +138,196 @@

    15from askai.core.component.audio_player import player 16from askai.core.component.cache_service import cache 17from askai.core.component.recorder import Recorder - 18from askai.core.engine.ai_model import AIModel - 19from askai.core.engine.ai_reply import AIReply - 20from askai.core.engine.openai.openai_configs import OpenAiConfigs - 21from askai.core.engine.openai.openai_model import OpenAIModel - 22from askai.core.support.utilities import stream_text - 23from hspylib.core.preconditions import check_not_none - 24from langchain_core.embeddings import Embeddings - 25from langchain_core.language_models import BaseChatModel, BaseLLM - 26from openai import APIError, OpenAI - 27from pathlib import Path - 28from threading import Thread - 29from typing import List, Optional - 30 - 31import langchain_openai - 32import logging as log - 33import os - 34import pause - 35import tiktoken - 36 - 37 - 38class OpenAIEngine: - 39 """Provide a base class for OpenAI features. Implements the prototype AIEngine.""" - 40 - 41 def __init__(self, model: AIModel = OpenAIModel.GPT_4_O_MINI): - 42 super().__init__() - 43 self._model: AIModel = model - 44 self._configs: OpenAiConfigs = OpenAiConfigs.INSTANCE - 45 self._api_key: str = os.environ.get("OPENAI_API_KEY") - 46 self._client = OpenAI(api_key=self._api_key) - 47 - 48 def __str__(self): - 49 return f"{self.ai_name()} '{self.nickname()}' '{self._model}'" + 18from askai.core.component.text_streamer import streamer + 19from askai.core.engine.ai_model import AIModel + 20from askai.core.engine.ai_vision import AIVision + 21from askai.core.engine.openai.openai_configs import OpenAiConfigs + 22from askai.core.engine.openai.openai_model import OpenAIModel + 23from askai.core.engine.openai.openai_vision import OpenAIVision + 24from askai.core.model.ai_reply import AIReply + 25from hspylib.core.preconditions import check_not_none + 26from langchain_core.embeddings import Embeddings + 27from langchain_core.language_models import BaseChatModel, BaseLLM + 28from openai import APIError, OpenAI + 29from pathlib import Path + 30from threading import Thread + 31from typing import List, Optional + 32 + 33import langchain_openai + 34import logging as log + 35import os + 36import pause + 37import tiktoken + 38 + 39 + 40class OpenAIEngine: + 41 """Provide a base class for OpenAI features. This class implements the AIEngine protocol.""" + 42 + 43 def __init__(self, model: AIModel = OpenAIModel.GPT_4_O_MINI): + 44 super().__init__() + 45 self._model: AIModel = model + 46 self._configs: OpenAiConfigs = OpenAiConfigs.INSTANCE + 47 self._api_key: str = os.environ.get("OPENAI_API_KEY") + 48 self._client = OpenAI(api_key=self._api_key) + 49 self._vision = OpenAIVision() 50 - 51 @property - 52 def url(self) -> str: - 53 return "https://api.openai.com/v1/chat/completions" - 54 - 55 @property - 56 def client(self) -> OpenAI: - 57 return self._client - 58 - 59 def configs(self) -> OpenAiConfigs: - 60 return self._configs + 51 def __str__(self): + 52 return f"{self.ai_name()} '{self.nickname()}' '{self._model}'" + 53 + 54 @property + 55 def url(self) -> str: + 56 return "https://api.openai.com/v1/chat/completions" + 57 + 58 @property + 59 def client(self) -> OpenAI: + 60 return self._client 61 - 62 def voices(self) -> list[str]: - 63 return ["alloy", "echo", "fable", "onyx", "nova", "shimmer"] - 64 - 65 def lc_model(self, temperature: float, top_p: float) -> BaseLLM: - 66 """Create a LangChain OpenAI llm model instance.""" - 67 return langchain_openai.OpenAI(openai_api_key=self._api_key, temperature=temperature, top_p=top_p) - 68 - 69 def lc_chat_model(self, temperature: float) -> BaseChatModel: - 70 """Create a LangChain OpenAI llm chat model instance.""" - 71 return langchain_openai.ChatOpenAI(openai_api_key=self._api_key, temperature=temperature) - 72 - 73 def lc_embeddings(self, model: str) -> Embeddings: - 74 """Create a LangChain AI embeddings instance.""" - 75 return langchain_openai.OpenAIEmbeddings(openai_api_key=self._api_key, model=model) - 76 - 77 def ai_name(self) -> str: - 78 """Get the AI model name.""" - 79 return self.__class__.__name__ - 80 - 81 def ai_model_name(self) -> str: - 82 """Get the AI model name.""" - 83 return self._model.model_name() - 84 - 85 def ai_token_limit(self) -> int: - 86 """Get the AI model tokens limit.""" - 87 return self._model.token_limit() - 88 - 89 def nickname(self) -> str: - 90 """Get the AI engine nickname.""" - 91 return "ChatGPT" - 92 - 93 def models(self) -> List[AIModel]: - 94 """Get the list of available models for the engine.""" - 95 return OpenAIModel.models() - 96 - 97 def ask(self, chat_context: List[dict], temperature: float = 0.8, top_p: float = 0.0) -> AIReply: - 98 """Ask AI assistance for the given question and expect a response. - 99 :param chat_context: The chat history or context. -100 :param temperature: The model engine temperature. -101 :param top_p: The model engine top_p. -102 """ -103 try: -104 check_not_none(chat_context) -105 log.debug(f"Generating AI answer") -106 response = self.client.chat.completions.create( -107 model=self.ai_model_name(), messages=chat_context, temperature=temperature, top_p=top_p -108 ) -109 reply = AIReply(response.choices[0].message.content, True) -110 log.debug("Response received from LLM: %s", str(reply)) -111 except APIError as error: -112 body: dict = error.body or {"message": "Message not provided"} -113 reply = AIReply(f"%RED%{error.__class__.__name__} => {body['message']}%NC%", False) -114 -115 return reply -116 -117 def text_to_speech(self, text: str, prefix: str = "", stream: bool = True, playback: bool = True) -> Optional[Path]: -118 """Text-T0-Speech the provided text. -119 :param text: The text to speech. -120 :param prefix: The prefix of the streamed text. -121 :param stream: Whether to stream the text into stdout. -122 :param playback: Whether to playback the generated audio file. -123 """ -124 if text: -125 speech_file_path, file_exists = cache.audio_file_path( -126 text, self._configs.tts_voice, self._configs.tts_format -127 ) -128 if not file_exists: -129 log.debug(f'Audio file "%s" not found in cache. Generating from %s.', self.nickname(), speech_file_path) -130 with self.client.audio.speech.with_streaming_response.create( -131 input=text, -132 model=self._configs.tts_model, -133 voice=self._configs.tts_voice, -134 response_format=self._configs.tts_format, -135 ) as response: -136 response.stream_to_file(speech_file_path) -137 log.debug(f"Audio file created: '%s' at %s", text, speech_file_path) -138 else: -139 log.debug(f"Audio file found in cache: '%s' at %s", text, speech_file_path) -140 if playback: -141 speak_thread = Thread( -142 daemon=True, target=player.play_audio_file, args=(speech_file_path, self._configs.tempo) -143 ) -144 speak_thread.start() -145 if stream: -146 pause.seconds(player.start_delay()) -147 stream_text(text, prefix) -148 speak_thread.join() # Block until the speech has finished. -149 return Path(speech_file_path) -150 return None -151 -152 def speech_to_text(self) -> Optional[str]: -153 """Transcribes audio input from the microphone into the text input language.""" -154 _, text = Recorder.INSTANCE.listen(language=self._configs.language) -155 log.debug(f"Audio transcribed to: {text}") -156 return text.strip() if text else None -157 -158 def calculate_tokens(self, text: str) -> int: -159 """Calculate the number of tokens for the given text. -160 :param text: The text to base the token calculation. -161 """ -162 encoding: tiktoken.Encoding = tiktoken.encoding_for_model(self._model.model_name) -163 tokens: list[int] = encoding.encode(text) -164 return len(tokens) + 62 def configs(self) -> OpenAiConfigs: + 63 """Return the engine-specific configurations.""" + 64 return self._configs + 65 + 66 def nickname(self) -> str: + 67 """Get the AI engine nickname. + 68 :return: The nickname of the AI engine. + 69 """ + 70 return "ChatGPT" + 71 + 72 def models(self) -> List[AIModel]: + 73 """Get the list of available models for the engine. + 74 :return: A list of available AI models. + 75 """ + 76 return OpenAIModel.models() + 77 + 78 def voices(self) -> list[str]: + 79 """Return the available model voices for speech to text. + 80 :return: A list of available voices. + 81 """ + 82 return ["alloy", "echo", "fable", "onyx", "nova", "shimmer"] + 83 + 84 def vision(self) -> AIVision: + 85 """Return the engine's vision component. + 86 :return: The vision component of the engine. + 87 """ + 88 return self._vision + 89 + 90 def lc_model(self, temperature: float, top_p: float) -> BaseLLM: + 91 """Create a LangChain LLM model instance using the current AI engine. + 92 :param temperature: The LLM model temperature. + 93 :param top_p: The model engine top_p. + 94 :return: An instance of BaseLLM. + 95 """ + 96 return langchain_openai.OpenAI( + 97 openai_api_key=self._api_key, model=self._model.model_name(), temperature=temperature, top_p=top_p + 98 ) + 99 +100 def lc_chat_model(self, temperature: float) -> BaseChatModel: +101 """Create a LangChain LLM chat model instance using the current AI engine. +102 :param temperature: The LLM chat model temperature. +103 :return: An instance of BaseChatModel. +104 """ +105 return langchain_openai.ChatOpenAI( +106 openai_api_key=self._api_key, model=self._model.model_name(), temperature=temperature +107 ) +108 +109 def lc_embeddings(self, model: str) -> Embeddings: +110 """Create a LangChain LLM embeddings model instance. +111 :param model: The LLM embeddings model string. +112 :return: An instance of Embeddings. +113 """ +114 return langchain_openai.OpenAIEmbeddings(openai_api_key=self._api_key, model=model) +115 +116 def ai_name(self) -> str: +117 """Get the AI engine name. +118 :return: The name of the AI engine. +119 """ +120 return self.__class__.__name__ +121 +122 def ai_model_name(self) -> str: +123 """Get the AI model name. +124 :return: The name of the AI model. +125 """ +126 return self._model.model_name() +127 +128 def ai_token_limit(self) -> int: +129 """Get the AI model token limit. +130 :return: The token limit of the AI model. +131 """ +132 return self._model.token_limit() +133 +134 def ask(self, chat_context: List[dict], temperature: float = 0.8, top_p: float = 0.0) -> AIReply: +135 """Ask AI assistance for the given question and expect a response. +136 :param chat_context: The chat history or context. +137 :param temperature: The model engine temperature. +138 :param top_p: The model engine top_p. +139 :return: The AI's reply. +140 """ +141 try: +142 check_not_none(chat_context) +143 log.debug(f"Generating AI answer") +144 response = self.client.chat.completions.create( +145 model=self.ai_model_name(), messages=chat_context, temperature=temperature, top_p=top_p +146 ) +147 reply = AIReply(response.choices[0].message.content, True) +148 log.debug("Response received from LLM: %s", str(reply)) +149 except APIError as error: +150 body: dict = error.body or {"message": "Message not provided"} +151 reply = AIReply(f"%RED%{error.__class__.__name__} => {body['message']}%NC%", False) +152 +153 return reply +154 +155 def text_to_speech(self, text: str, prefix: str = "", stream: bool = True, playback: bool = True) -> Optional[Path]: +156 """Convert the provided text to speech. +157 :param text: The text to convert to speech. +158 :param prefix: The prefix of the streamed text. +159 :param stream: Whether to stream the text into stdout. +160 :param playback: Whether to play back the generated audio file. +161 :return: The path to the generated audio file; or None if no file was generated. +162 """ +163 if text: +164 speech_file_path, file_exists = cache.audio_file_path( +165 text, self._configs.tts_voice, self._configs.tts_format +166 ) +167 if not file_exists: +168 log.debug(f'Audio file "%s" not found in cache. Generating from %s.', self.nickname(), speech_file_path) +169 with self.client.audio.speech.with_streaming_response.create( +170 input=text, +171 model=self._configs.tts_model, +172 voice=self._configs.tts_voice, +173 response_format=self._configs.tts_format, +174 ) as response: +175 response.stream_to_file(speech_file_path) +176 log.debug(f"Audio file created: '%s' at %s", text, speech_file_path) +177 else: +178 log.debug(f"Audio file found in cache: '%s' at %s", text, speech_file_path) +179 if playback: +180 speak_thread = Thread( +181 daemon=True, target=player.play_audio_file, args=(speech_file_path, self._configs.tempo) +182 ) +183 speak_thread.start() +184 if stream: +185 pause.seconds(player.start_delay()) +186 streamer.stream_text(text, prefix) +187 speak_thread.join() # Block until the speech has finished. +188 return Path(speech_file_path) +189 return None +190 +191 def speech_to_text(self) -> Optional[str]: +192 """Transcribe audio input from the microphone into text. +193 :return: The transcribed text or None if transcription fails. +194 """ +195 _, text = Recorder.INSTANCE.listen(language=self._configs.language) +196 log.debug(f"Audio transcribed to: {text}") +197 return text.strip() if text else None +198 +199 def calculate_tokens(self, text: str) -> int: +200 """Calculate the number of tokens for the given text. +201 :param text: The text for which to calculate tokens. +202 :return: The number of tokens in the text. +203 """ +204 encoding: tiktoken.Encoding = tiktoken.encoding_for_model(self._model.model_name()) +205 tokens: list[int] = encoding.encode(text) +206 log.debug(f"Tokens calculated. Text: '{text}' Tokens: '{tokens}'") +207 return len(tokens)

    @@ -297,137 +343,178 @@

    -
     39class OpenAIEngine:
    - 40    """Provide a base class for OpenAI features. Implements the prototype AIEngine."""
    - 41
    - 42    def __init__(self, model: AIModel = OpenAIModel.GPT_4_O_MINI):
    - 43        super().__init__()
    - 44        self._model: AIModel = model
    - 45        self._configs: OpenAiConfigs = OpenAiConfigs.INSTANCE
    - 46        self._api_key: str = os.environ.get("OPENAI_API_KEY")
    - 47        self._client = OpenAI(api_key=self._api_key)
    - 48
    - 49    def __str__(self):
    - 50        return f"{self.ai_name()} '{self.nickname()}' '{self._model}'"
    +            
     41class OpenAIEngine:
    + 42    """Provide a base class for OpenAI features. This class implements the AIEngine protocol."""
    + 43
    + 44    def __init__(self, model: AIModel = OpenAIModel.GPT_4_O_MINI):
    + 45        super().__init__()
    + 46        self._model: AIModel = model
    + 47        self._configs: OpenAiConfigs = OpenAiConfigs.INSTANCE
    + 48        self._api_key: str = os.environ.get("OPENAI_API_KEY")
    + 49        self._client = OpenAI(api_key=self._api_key)
    + 50        self._vision = OpenAIVision()
      51
    - 52    @property
    - 53    def url(self) -> str:
    - 54        return "https://api.openai.com/v1/chat/completions"
    - 55
    - 56    @property
    - 57    def client(self) -> OpenAI:
    - 58        return self._client
    - 59
    - 60    def configs(self) -> OpenAiConfigs:
    - 61        return self._configs
    + 52    def __str__(self):
    + 53        return f"{self.ai_name()} '{self.nickname()}' '{self._model}'"
    + 54
    + 55    @property
    + 56    def url(self) -> str:
    + 57        return "https://api.openai.com/v1/chat/completions"
    + 58
    + 59    @property
    + 60    def client(self) -> OpenAI:
    + 61        return self._client
      62
    - 63    def voices(self) -> list[str]:
    - 64        return ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
    - 65
    - 66    def lc_model(self, temperature: float, top_p: float) -> BaseLLM:
    - 67        """Create a LangChain OpenAI llm model instance."""
    - 68        return langchain_openai.OpenAI(openai_api_key=self._api_key, temperature=temperature, top_p=top_p)
    - 69
    - 70    def lc_chat_model(self, temperature: float) -> BaseChatModel:
    - 71        """Create a LangChain OpenAI llm chat model instance."""
    - 72        return langchain_openai.ChatOpenAI(openai_api_key=self._api_key, temperature=temperature)
    - 73
    - 74    def lc_embeddings(self, model: str) -> Embeddings:
    - 75        """Create a LangChain AI embeddings instance."""
    - 76        return langchain_openai.OpenAIEmbeddings(openai_api_key=self._api_key, model=model)
    - 77
    - 78    def ai_name(self) -> str:
    - 79        """Get the AI model name."""
    - 80        return self.__class__.__name__
    - 81
    - 82    def ai_model_name(self) -> str:
    - 83        """Get the AI model name."""
    - 84        return self._model.model_name()
    - 85
    - 86    def ai_token_limit(self) -> int:
    - 87        """Get the AI model tokens limit."""
    - 88        return self._model.token_limit()
    - 89
    - 90    def nickname(self) -> str:
    - 91        """Get the AI engine nickname."""
    - 92        return "ChatGPT"
    - 93
    - 94    def models(self) -> List[AIModel]:
    - 95        """Get the list of available models for the engine."""
    - 96        return OpenAIModel.models()
    - 97
    - 98    def ask(self, chat_context: List[dict], temperature: float = 0.8, top_p: float = 0.0) -> AIReply:
    - 99        """Ask AI assistance for the given question and expect a response.
    -100        :param chat_context: The chat history or context.
    -101        :param temperature: The model engine temperature.
    -102        :param top_p: The model engine top_p.
    -103        """
    -104        try:
    -105            check_not_none(chat_context)
    -106            log.debug(f"Generating AI answer")
    -107            response = self.client.chat.completions.create(
    -108                model=self.ai_model_name(), messages=chat_context, temperature=temperature, top_p=top_p
    -109            )
    -110            reply = AIReply(response.choices[0].message.content, True)
    -111            log.debug("Response received from LLM: %s", str(reply))
    -112        except APIError as error:
    -113            body: dict = error.body or {"message": "Message not provided"}
    -114            reply = AIReply(f"%RED%{error.__class__.__name__} => {body['message']}%NC%", False)
    -115
    -116        return reply
    -117
    -118    def text_to_speech(self, text: str, prefix: str = "", stream: bool = True, playback: bool = True) -> Optional[Path]:
    -119        """Text-T0-Speech the provided text.
    -120        :param text: The text to speech.
    -121        :param prefix: The prefix of the streamed text.
    -122        :param stream: Whether to stream the text into stdout.
    -123        :param playback: Whether to playback the generated audio file.
    -124        """
    -125        if text:
    -126            speech_file_path, file_exists = cache.audio_file_path(
    -127                text, self._configs.tts_voice, self._configs.tts_format
    -128            )
    -129            if not file_exists:
    -130                log.debug(f'Audio file "%s" not found in cache. Generating from %s.', self.nickname(), speech_file_path)
    -131                with self.client.audio.speech.with_streaming_response.create(
    -132                    input=text,
    -133                    model=self._configs.tts_model,
    -134                    voice=self._configs.tts_voice,
    -135                    response_format=self._configs.tts_format,
    -136                ) as response:
    -137                    response.stream_to_file(speech_file_path)
    -138                    log.debug(f"Audio file created: '%s' at %s", text, speech_file_path)
    -139            else:
    -140                log.debug(f"Audio file found in cache: '%s' at %s", text, speech_file_path)
    -141            if playback:
    -142                speak_thread = Thread(
    -143                    daemon=True, target=player.play_audio_file, args=(speech_file_path, self._configs.tempo)
    -144                )
    -145                speak_thread.start()
    -146                if stream:
    -147                    pause.seconds(player.start_delay())
    -148                    stream_text(text, prefix)
    -149                speak_thread.join()  # Block until the speech has finished.
    -150            return Path(speech_file_path)
    -151        return None
    -152
    -153    def speech_to_text(self) -> Optional[str]:
    -154        """Transcribes audio input from the microphone into the text input language."""
    -155        _, text = Recorder.INSTANCE.listen(language=self._configs.language)
    -156        log.debug(f"Audio transcribed to: {text}")
    -157        return text.strip() if text else None
    -158
    -159    def calculate_tokens(self, text: str) -> int:
    -160        """Calculate the number of tokens for the given text.
    -161        :param text: The text to base the token calculation.
    -162        """
    -163        encoding: tiktoken.Encoding = tiktoken.encoding_for_model(self._model.model_name)
    -164        tokens: list[int] = encoding.encode(text)
    -165        return len(tokens)
    + 63    def configs(self) -> OpenAiConfigs:
    + 64        """Return the engine-specific configurations."""
    + 65        return self._configs
    + 66
    + 67    def nickname(self) -> str:
    + 68        """Get the AI engine nickname.
    + 69        :return: The nickname of the AI engine.
    + 70        """
    + 71        return "ChatGPT"
    + 72
    + 73    def models(self) -> List[AIModel]:
    + 74        """Get the list of available models for the engine.
    + 75        :return: A list of available AI models.
    + 76        """
    + 77        return OpenAIModel.models()
    + 78
    + 79    def voices(self) -> list[str]:
    + 80        """Return the available model voices for speech to text.
    + 81        :return: A list of available voices.
    + 82        """
    + 83        return ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
    + 84
    + 85    def vision(self) -> AIVision:
    + 86        """Return the engine's vision component.
    + 87        :return: The vision component of the engine.
    + 88        """
    + 89        return self._vision
    + 90
    + 91    def lc_model(self, temperature: float, top_p: float) -> BaseLLM:
    + 92        """Create a LangChain LLM model instance using the current AI engine.
    + 93        :param temperature: The LLM model temperature.
    + 94        :param top_p: The model engine top_p.
    + 95        :return: An instance of BaseLLM.
    + 96        """
    + 97        return langchain_openai.OpenAI(
    + 98            openai_api_key=self._api_key, model=self._model.model_name(), temperature=temperature, top_p=top_p
    + 99        )
    +100
    +101    def lc_chat_model(self, temperature: float) -> BaseChatModel:
    +102        """Create a LangChain LLM chat model instance using the current AI engine.
    +103        :param temperature: The LLM chat model temperature.
    +104        :return: An instance of BaseChatModel.
    +105        """
    +106        return langchain_openai.ChatOpenAI(
    +107            openai_api_key=self._api_key, model=self._model.model_name(), temperature=temperature
    +108        )
    +109
    +110    def lc_embeddings(self, model: str) -> Embeddings:
    +111        """Create a LangChain LLM embeddings model instance.
    +112        :param model: The LLM embeddings model string.
    +113        :return: An instance of Embeddings.
    +114        """
    +115        return langchain_openai.OpenAIEmbeddings(openai_api_key=self._api_key, model=model)
    +116
    +117    def ai_name(self) -> str:
    +118        """Get the AI engine name.
    +119        :return: The name of the AI engine.
    +120        """
    +121        return self.__class__.__name__
    +122
    +123    def ai_model_name(self) -> str:
    +124        """Get the AI model name.
    +125        :return: The name of the AI model.
    +126        """
    +127        return self._model.model_name()
    +128
    +129    def ai_token_limit(self) -> int:
    +130        """Get the AI model token limit.
    +131        :return: The token limit of the AI model.
    +132        """
    +133        return self._model.token_limit()
    +134
    +135    def ask(self, chat_context: List[dict], temperature: float = 0.8, top_p: float = 0.0) -> AIReply:
    +136        """Ask AI assistance for the given question and expect a response.
    +137        :param chat_context: The chat history or context.
    +138        :param temperature: The model engine temperature.
    +139        :param top_p: The model engine top_p.
    +140        :return: The AI's reply.
    +141        """
    +142        try:
    +143            check_not_none(chat_context)
    +144            log.debug(f"Generating AI answer")
    +145            response = self.client.chat.completions.create(
    +146                model=self.ai_model_name(), messages=chat_context, temperature=temperature, top_p=top_p
    +147            )
    +148            reply = AIReply(response.choices[0].message.content, True)
    +149            log.debug("Response received from LLM: %s", str(reply))
    +150        except APIError as error:
    +151            body: dict = error.body or {"message": "Message not provided"}
    +152            reply = AIReply(f"%RED%{error.__class__.__name__} => {body['message']}%NC%", False)
    +153
    +154        return reply
    +155
    +156    def text_to_speech(self, text: str, prefix: str = "", stream: bool = True, playback: bool = True) -> Optional[Path]:
    +157        """Convert the provided text to speech.
    +158        :param text: The text to convert to speech.
    +159        :param prefix: The prefix of the streamed text.
    +160        :param stream: Whether to stream the text into stdout.
    +161        :param playback: Whether to play back the generated audio file.
    +162        :return: The path to the generated audio file; or None if no file was generated.
    +163        """
    +164        if text:
    +165            speech_file_path, file_exists = cache.audio_file_path(
    +166                text, self._configs.tts_voice, self._configs.tts_format
    +167            )
    +168            if not file_exists:
    +169                log.debug(f'Audio file "%s" not found in cache. Generating from %s.', self.nickname(), speech_file_path)
    +170                with self.client.audio.speech.with_streaming_response.create(
    +171                    input=text,
    +172                    model=self._configs.tts_model,
    +173                    voice=self._configs.tts_voice,
    +174                    response_format=self._configs.tts_format,
    +175                ) as response:
    +176                    response.stream_to_file(speech_file_path)
    +177                    log.debug(f"Audio file created: '%s' at %s", text, speech_file_path)
    +178            else:
    +179                log.debug(f"Audio file found in cache: '%s' at %s", text, speech_file_path)
    +180            if playback:
    +181                speak_thread = Thread(
    +182                    daemon=True, target=player.play_audio_file, args=(speech_file_path, self._configs.tempo)
    +183                )
    +184                speak_thread.start()
    +185                if stream:
    +186                    pause.seconds(player.start_delay())
    +187                    streamer.stream_text(text, prefix)
    +188                speak_thread.join()  # Block until the speech has finished.
    +189            return Path(speech_file_path)
    +190        return None
    +191
    +192    def speech_to_text(self) -> Optional[str]:
    +193        """Transcribe audio input from the microphone into text.
    +194        :return: The transcribed text or None if transcription fails.
    +195        """
    +196        _, text = Recorder.INSTANCE.listen(language=self._configs.language)
    +197        log.debug(f"Audio transcribed to: {text}")
    +198        return text.strip() if text else None
    +199
    +200    def calculate_tokens(self, text: str) -> int:
    +201        """Calculate the number of tokens for the given text.
    +202        :param text: The text for which to calculate tokens.
    +203        :return: The number of tokens in the text.
    +204        """
    +205        encoding: tiktoken.Encoding = tiktoken.encoding_for_model(self._model.model_name())
    +206        tokens: list[int] = encoding.encode(text)
    +207        log.debug(f"Tokens calculated. Text: '{text}'  Tokens: '{tokens}'")
    +208        return len(tokens)
     
    -

    Provide a base class for OpenAI features. Implements the prototype AIEngine.

    +

    Provide a base class for OpenAI features. This class implements the AIEngine protocol.

    @@ -441,12 +528,13 @@

    -
    42    def __init__(self, model: AIModel = OpenAIModel.GPT_4_O_MINI):
    -43        super().__init__()
    -44        self._model: AIModel = model
    -45        self._configs: OpenAiConfigs = OpenAiConfigs.INSTANCE
    -46        self._api_key: str = os.environ.get("OPENAI_API_KEY")
    -47        self._client = OpenAI(api_key=self._api_key)
    +            
    44    def __init__(self, model: AIModel = OpenAIModel.GPT_4_O_MINI):
    +45        super().__init__()
    +46        self._model: AIModel = model
    +47        self._configs: OpenAiConfigs = OpenAiConfigs.INSTANCE
    +48        self._api_key: str = os.environ.get("OPENAI_API_KEY")
    +49        self._client = OpenAI(api_key=self._api_key)
    +50        self._vision = OpenAIVision()
     
    @@ -462,9 +550,9 @@

    -
    52    @property
    -53    def url(self) -> str:
    -54        return "https://api.openai.com/v1/chat/completions"
    +            
    55    @property
    +56    def url(self) -> str:
    +57        return "https://api.openai.com/v1/chat/completions"
     
    @@ -480,9 +568,9 @@

    -
    56    @property
    -57    def client(self) -> OpenAI:
    -58        return self._client
    +            
    59    @property
    +60    def client(self) -> OpenAI:
    +61        return self._client
     
    @@ -500,12 +588,75 @@

    -
    60    def configs(self) -> OpenAiConfigs:
    -61        return self._configs
    +            
    63    def configs(self) -> OpenAiConfigs:
    +64        """Return the engine-specific configurations."""
    +65        return self._configs
     
    - +

    Return the engine-specific configurations.

    +
    + + +
    +
    + +
    + + def + nickname(self) -> str: + + + +
    + +
    67    def nickname(self) -> str:
    +68        """Get the AI engine nickname.
    +69        :return: The nickname of the AI engine.
    +70        """
    +71        return "ChatGPT"
    +
    + + +

    Get the AI engine nickname.

    + +
    Returns
    + +
    +

    The nickname of the AI engine.

    +
    +
    + + +
    +
    + +
    + + def + models(self) -> List[askai.core.engine.ai_model.AIModel]: + + + +
    + +
    73    def models(self) -> List[AIModel]:
    +74        """Get the list of available models for the engine.
    +75        :return: A list of available AI models.
    +76        """
    +77        return OpenAIModel.models()
    +
    + + +

    Get the list of available models for the engine.

    + +
    Returns
    + +
    +

    A list of available AI models.

    +
    +
    +
    @@ -519,12 +670,53 @@

    -
    63    def voices(self) -> list[str]:
    -64        return ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
    +            
    79    def voices(self) -> list[str]:
    +80        """Return the available model voices for speech to text.
    +81        :return: A list of available voices.
    +82        """
    +83        return ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
     
    - +

    Return the available model voices for speech to text.

    + +
    Returns
    + +
    +

    A list of available voices.

    +
    +
    + + +
    +
    + +
    + + def + vision(self) -> askai.core.engine.ai_vision.AIVision: + + + +
    + +
    85    def vision(self) -> AIVision:
    +86        """Return the engine's vision component.
    +87        :return: The vision component of the engine.
    +88        """
    +89        return self._vision
    +
    + + +

    Return the engine's vision component.

    + +
    Returns
    + +
    +

    The vision component of the engine.

    +
    +
    +
    @@ -538,13 +730,32 @@

    -
    66    def lc_model(self, temperature: float, top_p: float) -> BaseLLM:
    -67        """Create a LangChain OpenAI llm model instance."""
    -68        return langchain_openai.OpenAI(openai_api_key=self._api_key, temperature=temperature, top_p=top_p)
    +            
    91    def lc_model(self, temperature: float, top_p: float) -> BaseLLM:
    +92        """Create a LangChain LLM model instance using the current AI engine.
    +93        :param temperature: The LLM model temperature.
    +94        :param top_p: The model engine top_p.
    +95        :return: An instance of BaseLLM.
    +96        """
    +97        return langchain_openai.OpenAI(
    +98            openai_api_key=self._api_key, model=self._model.model_name(), temperature=temperature, top_p=top_p
    +99        )
     
    -

    Create a LangChain OpenAI llm model instance.

    +

    Create a LangChain LLM model instance using the current AI engine.

    + +
    Parameters
    + +
      +
    • temperature: The LLM model temperature.
    • +
    • top_p: The model engine top_p.
    • +
    + +
    Returns
    + +
    +

    An instance of BaseLLM.

    +
    @@ -560,13 +771,30 @@

    -
    70    def lc_chat_model(self, temperature: float) -> BaseChatModel:
    -71        """Create a LangChain OpenAI llm chat model instance."""
    -72        return langchain_openai.ChatOpenAI(openai_api_key=self._api_key, temperature=temperature)
    +            
    101    def lc_chat_model(self, temperature: float) -> BaseChatModel:
    +102        """Create a LangChain LLM chat model instance using the current AI engine.
    +103        :param temperature: The LLM chat model temperature.
    +104        :return: An instance of BaseChatModel.
    +105        """
    +106        return langchain_openai.ChatOpenAI(
    +107            openai_api_key=self._api_key, model=self._model.model_name(), temperature=temperature
    +108        )
     
    -

    Create a LangChain OpenAI llm chat model instance.

    +

    Create a LangChain LLM chat model instance using the current AI engine.

    + +
    Parameters
    + +
      +
    • temperature: The LLM chat model temperature.
    • +
    + +
    Returns
    + +
    +

    An instance of BaseChatModel.

    +
    @@ -582,13 +810,28 @@

    -
    74    def lc_embeddings(self, model: str) -> Embeddings:
    -75        """Create a LangChain AI embeddings instance."""
    -76        return langchain_openai.OpenAIEmbeddings(openai_api_key=self._api_key, model=model)
    +            
    110    def lc_embeddings(self, model: str) -> Embeddings:
    +111        """Create a LangChain LLM embeddings model instance.
    +112        :param model: The LLM embeddings model string.
    +113        :return: An instance of Embeddings.
    +114        """
    +115        return langchain_openai.OpenAIEmbeddings(openai_api_key=self._api_key, model=model)
     
    -

    Create a LangChain AI embeddings instance.

    +

    Create a LangChain LLM embeddings model instance.

    + +
    Parameters
    + +
      +
    • model: The LLM embeddings model string.
    • +
    + +
    Returns
    + +
    +

    An instance of Embeddings.

    +
    @@ -604,13 +847,21 @@

    -
    78    def ai_name(self) -> str:
    -79        """Get the AI model name."""
    -80        return self.__class__.__name__
    +            
    117    def ai_name(self) -> str:
    +118        """Get the AI engine name.
    +119        :return: The name of the AI engine.
    +120        """
    +121        return self.__class__.__name__
     
    -

    Get the AI model name.

    +

    Get the AI engine name.

    + +
    Returns
    + +
    +

    The name of the AI engine.

    +
    @@ -626,13 +877,21 @@

    -
    82    def ai_model_name(self) -> str:
    -83        """Get the AI model name."""
    -84        return self._model.model_name()
    +            
    123    def ai_model_name(self) -> str:
    +124        """Get the AI model name.
    +125        :return: The name of the AI model.
    +126        """
    +127        return self._model.model_name()
     

    Get the AI model name.

    + +
    Returns
    + +
    +

    The name of the AI model.

    +
    @@ -648,57 +907,21 @@

    -
    86    def ai_token_limit(self) -> int:
    -87        """Get the AI model tokens limit."""
    -88        return self._model.token_limit()
    +            
    129    def ai_token_limit(self) -> int:
    +130        """Get the AI model token limit.
    +131        :return: The token limit of the AI model.
    +132        """
    +133        return self._model.token_limit()
     
    -

    Get the AI model tokens limit.

    -
    +

    Get the AI model token limit.

    +
    Returns
    -
    -
    - -
    - - def - nickname(self) -> str: - - - -
    - -
    90    def nickname(self) -> str:
    -91        """Get the AI engine nickname."""
    -92        return "ChatGPT"
    -
    - - -

    Get the AI engine nickname.

    -
    - - -
    -
    - -
    - - def - models(self) -> List[askai.core.engine.ai_model.AIModel]: - - - -
    - -
    94    def models(self) -> List[AIModel]:
    -95        """Get the list of available models for the engine."""
    -96        return OpenAIModel.models()
    -
    - - -

    Get the list of available models for the engine.

    +
    +

    The token limit of the AI model.

    +
    @@ -708,31 +931,32 @@

    def - ask( self, chat_context: List[dict], temperature: float = 0.8, top_p: float = 0.0) -> askai.core.engine.ai_reply.AIReply: + ask( self, chat_context: List[dict], temperature: float = 0.8, top_p: float = 0.0) -> askai.core.model.ai_reply.AIReply:
    -
     98    def ask(self, chat_context: List[dict], temperature: float = 0.8, top_p: float = 0.0) -> AIReply:
    - 99        """Ask AI assistance for the given question and expect a response.
    -100        :param chat_context: The chat history or context.
    -101        :param temperature: The model engine temperature.
    -102        :param top_p: The model engine top_p.
    -103        """
    -104        try:
    -105            check_not_none(chat_context)
    -106            log.debug(f"Generating AI answer")
    -107            response = self.client.chat.completions.create(
    -108                model=self.ai_model_name(), messages=chat_context, temperature=temperature, top_p=top_p
    -109            )
    -110            reply = AIReply(response.choices[0].message.content, True)
    -111            log.debug("Response received from LLM: %s", str(reply))
    -112        except APIError as error:
    -113            body: dict = error.body or {"message": "Message not provided"}
    -114            reply = AIReply(f"%RED%{error.__class__.__name__} => {body['message']}%NC%", False)
    -115
    -116        return reply
    +            
    135    def ask(self, chat_context: List[dict], temperature: float = 0.8, top_p: float = 0.0) -> AIReply:
    +136        """Ask AI assistance for the given question and expect a response.
    +137        :param chat_context: The chat history or context.
    +138        :param temperature: The model engine temperature.
    +139        :param top_p: The model engine top_p.
    +140        :return: The AI's reply.
    +141        """
    +142        try:
    +143            check_not_none(chat_context)
    +144            log.debug(f"Generating AI answer")
    +145            response = self.client.chat.completions.create(
    +146                model=self.ai_model_name(), messages=chat_context, temperature=temperature, top_p=top_p
    +147            )
    +148            reply = AIReply(response.choices[0].message.content, True)
    +149            log.debug("Response received from LLM: %s", str(reply))
    +150        except APIError as error:
    +151            body: dict = error.body or {"message": "Message not provided"}
    +152            reply = AIReply(f"%RED%{error.__class__.__name__} => {body['message']}%NC%", False)
    +153
    +154        return reply
     
    @@ -745,6 +969,12 @@
    Parameters
  • temperature: The model engine temperature.
  • top_p: The model engine top_p.
  • + +
    Returns
    + +
    +

    The AI's reply.

    +
    @@ -760,53 +990,60 @@

    Parameters
    -
    118    def text_to_speech(self, text: str, prefix: str = "", stream: bool = True, playback: bool = True) -> Optional[Path]:
    -119        """Text-T0-Speech the provided text.
    -120        :param text: The text to speech.
    -121        :param prefix: The prefix of the streamed text.
    -122        :param stream: Whether to stream the text into stdout.
    -123        :param playback: Whether to playback the generated audio file.
    -124        """
    -125        if text:
    -126            speech_file_path, file_exists = cache.audio_file_path(
    -127                text, self._configs.tts_voice, self._configs.tts_format
    -128            )
    -129            if not file_exists:
    -130                log.debug(f'Audio file "%s" not found in cache. Generating from %s.', self.nickname(), speech_file_path)
    -131                with self.client.audio.speech.with_streaming_response.create(
    -132                    input=text,
    -133                    model=self._configs.tts_model,
    -134                    voice=self._configs.tts_voice,
    -135                    response_format=self._configs.tts_format,
    -136                ) as response:
    -137                    response.stream_to_file(speech_file_path)
    -138                    log.debug(f"Audio file created: '%s' at %s", text, speech_file_path)
    -139            else:
    -140                log.debug(f"Audio file found in cache: '%s' at %s", text, speech_file_path)
    -141            if playback:
    -142                speak_thread = Thread(
    -143                    daemon=True, target=player.play_audio_file, args=(speech_file_path, self._configs.tempo)
    -144                )
    -145                speak_thread.start()
    -146                if stream:
    -147                    pause.seconds(player.start_delay())
    -148                    stream_text(text, prefix)
    -149                speak_thread.join()  # Block until the speech has finished.
    -150            return Path(speech_file_path)
    -151        return None
    +            
    156    def text_to_speech(self, text: str, prefix: str = "", stream: bool = True, playback: bool = True) -> Optional[Path]:
    +157        """Convert the provided text to speech.
    +158        :param text: The text to convert to speech.
    +159        :param prefix: The prefix of the streamed text.
    +160        :param stream: Whether to stream the text into stdout.
    +161        :param playback: Whether to play back the generated audio file.
    +162        :return: The path to the generated audio file; or None if no file was generated.
    +163        """
    +164        if text:
    +165            speech_file_path, file_exists = cache.audio_file_path(
    +166                text, self._configs.tts_voice, self._configs.tts_format
    +167            )
    +168            if not file_exists:
    +169                log.debug(f'Audio file "%s" not found in cache. Generating from %s.', self.nickname(), speech_file_path)
    +170                with self.client.audio.speech.with_streaming_response.create(
    +171                    input=text,
    +172                    model=self._configs.tts_model,
    +173                    voice=self._configs.tts_voice,
    +174                    response_format=self._configs.tts_format,
    +175                ) as response:
    +176                    response.stream_to_file(speech_file_path)
    +177                    log.debug(f"Audio file created: '%s' at %s", text, speech_file_path)
    +178            else:
    +179                log.debug(f"Audio file found in cache: '%s' at %s", text, speech_file_path)
    +180            if playback:
    +181                speak_thread = Thread(
    +182                    daemon=True, target=player.play_audio_file, args=(speech_file_path, self._configs.tempo)
    +183                )
    +184                speak_thread.start()
    +185                if stream:
    +186                    pause.seconds(player.start_delay())
    +187                    streamer.stream_text(text, prefix)
    +188                speak_thread.join()  # Block until the speech has finished.
    +189            return Path(speech_file_path)
    +190        return None
     
    -

    Text-T0-Speech the provided text.

    +

    Convert the provided text to speech.

    Parameters
      -
    • text: The text to speech.
    • +
    • text: The text to convert to speech.
    • prefix: The prefix of the streamed text.
    • stream: Whether to stream the text into stdout.
    • -
    • playback: Whether to playback the generated audio file.
    • +
    • playback: Whether to play back the generated audio file.
    + +
    Returns
    + +
    +

    The path to the generated audio file; or None if no file was generated.

    +
    @@ -822,15 +1059,23 @@
    Parameters
    -
    153    def speech_to_text(self) -> Optional[str]:
    -154        """Transcribes audio input from the microphone into the text input language."""
    -155        _, text = Recorder.INSTANCE.listen(language=self._configs.language)
    -156        log.debug(f"Audio transcribed to: {text}")
    -157        return text.strip() if text else None
    +            
    192    def speech_to_text(self) -> Optional[str]:
    +193        """Transcribe audio input from the microphone into text.
    +194        :return: The transcribed text or None if transcription fails.
    +195        """
    +196        _, text = Recorder.INSTANCE.listen(language=self._configs.language)
    +197        log.debug(f"Audio transcribed to: {text}")
    +198        return text.strip() if text else None
     
    -

    Transcribes audio input from the microphone into the text input language.

    +

    Transcribe audio input from the microphone into text.

    + +
    Returns
    + +
    +

    The transcribed text or None if transcription fails.

    +
    @@ -846,13 +1091,15 @@
    Parameters
    -
    159    def calculate_tokens(self, text: str) -> int:
    -160        """Calculate the number of tokens for the given text.
    -161        :param text: The text to base the token calculation.
    -162        """
    -163        encoding: tiktoken.Encoding = tiktoken.encoding_for_model(self._model.model_name)
    -164        tokens: list[int] = encoding.encode(text)
    -165        return len(tokens)
    +            
    200    def calculate_tokens(self, text: str) -> int:
    +201        """Calculate the number of tokens for the given text.
    +202        :param text: The text for which to calculate tokens.
    +203        :return: The number of tokens in the text.
    +204        """
    +205        encoding: tiktoken.Encoding = tiktoken.encoding_for_model(self._model.model_name())
    +206        tokens: list[int] = encoding.encode(text)
    +207        log.debug(f"Tokens calculated. Text: '{text}'  Tokens: '{tokens}'")
    +208        return len(tokens)
     
    @@ -861,8 +1108,14 @@
    Parameters
    Parameters
      -
    • text: The text to base the token calculation.
    • +
    • text: The text for which to calculate tokens.
    + +
    Returns
    + +
    +

    The number of tokens in the text.

    +
    diff --git a/docs/api/askai/main/askai/core/engine/openai/openai_model.html b/docs/api/askai/main/askai/core/engine/openai/openai_model.html index 7b4232f7..8791eb70 100644 --- a/docs/api/askai/main/askai/core/engine/openai/openai_model.html +++ b/docs/api/askai/main/askai/core/engine/openai/openai_model.html @@ -3,7 +3,7 @@ - + main.askai.core.engine.openai.openai_model API documentation @@ -37,22 +37,10 @@

    API Documentation

    GPT_3_5_TURBO
  • - GPT_3_5_TURBO_16K -
  • -
  • - GPT_3_5_TURBO_1106 -
  • -
  • - GPT_3_5_TURBO_0301 -
  • -
  • - GPT_3_5_TURBO_0613 -
  • -
  • - GPT_3_5_TURBO_16K_0613 + GPT_4
  • - GPT_4 + GPT_4_TURBO
  • GPT_4_O @@ -61,25 +49,10 @@

    API Documentation

    GPT_4_O_MINI
  • - GPT_4_0314 -
  • -
  • - GPT_4_0613 -
  • -
  • - GPT_4_32K + O1_PREVIEW
  • - GPT_4_32K_0314 -
  • -
  • - GPT_4_32K_0613 -
  • -
  • - GPT_4_1106_PREVIEW -
  • -
  • - GPT_4_VISION_PREVIEW + O1_MINI
  • models @@ -148,37 +121,37 @@

    19 20 21class OpenAIModel(Enumeration): -22 """Enumeration for the supported OpenAi models. Implements the AIModel protocol. -23 Details at: https://www.pluralsight.com/resources/blog/data/ai-gpt-models-differences +22 """Enumeration for the supported OpenAI models. This class implements the AIModel protocol. +23 Reference: https://www.pluralsight.com/resources/blog/data/ai-gpt-models-differences 24 """ 25 26 # ID of the model to use. Currently, only the values below are supported: 27 28 # fmt: off -29 GPT_3_5_TURBO = "gpt-3.5-turbo", 4096 -30 GPT_3_5_TURBO_16K = "gpt-3.5-turbo-16k", 16385 -31 GPT_3_5_TURBO_1106 = "gpt-3.5-turbo-1106", 16385 -32 GPT_3_5_TURBO_0301 = "gpt-3.5-turbo-0301", 4096 -33 GPT_3_5_TURBO_0613 = "gpt-3.5-turbo-0613", 4096 -34 GPT_3_5_TURBO_16K_0613 = "gpt-3.5-turbo-16k-0613", 16385 -35 GPT_4 = "gpt-4", 8192 -36 GPT_4_O = "gpt-4o", 128000 -37 GPT_4_O_MINI = "gpt-4o-mini", 128000 -38 GPT_4_0314 = "gpt-4-0314", 8192 -39 GPT_4_0613 = "gpt-4-0613", 8192 -40 GPT_4_32K = "gpt-4-32k", 32768 -41 GPT_4_32K_0314 = "gpt-4-32k-0314", 32768 -42 GPT_4_32K_0613 = "gpt-4-32k-0613", 32768 -43 GPT_4_1106_PREVIEW = "gpt-4-1106-preview", 128000 -44 GPT_4_VISION_PREVIEW = "gpt-4-vision-preview", 128000 -45 # fmt: on +29 +30 GPT_3_5_TURBO = "gpt-3.5-turbo", 4096 +31 GPT_4 = "gpt-4", 8192 +32 GPT_4_TURBO = "gpt-4-turbo", 128000 +33 GPT_4_O = "gpt-4o", 128000 +34 GPT_4_O_MINI = "gpt-4o-mini", 128000 +35 O1_PREVIEW = "o1-preview", 128000 +36 O1_MINI = "o1-mini", 128000 +37 +38 # fmt: on +39 +40 @staticmethod +41 def models() -> List["AIModel"]: +42 """Get the list of available models for the engine. +43 :return: A list of available AI models. +44 """ +45 return [OpenAIModel.of_value(m) for m in OpenAIModel.values()] 46 47 @staticmethod -48 def models() -> List["AIModel"]: -49 return [OpenAIModel.of_value(m) for m in OpenAIModel.values()] -50 -51 @staticmethod -52 def of_name(model_name: str) -> "AIModel": +48 def of_name(model_name: str) -> "AIModel": +49 """Get the AIModel instance corresponding to the given model name. +50 :param model_name: The name of the AI model. +51 :return: The corresponding AIModel instance. +52 """ 53 found = next((m for m in OpenAIModel.models() if m.model_name() == model_name.casefold()), None) 54 check_not_none(found, '"{}" name does not correspond to a valid "{}" enum', model_name, OpenAIModel.__name__) 55 return found @@ -213,37 +186,37 @@

  • 22class OpenAIModel(Enumeration):
    -23    """Enumeration for the supported OpenAi models. Implements the AIModel protocol.
    -24    Details at: https://www.pluralsight.com/resources/blog/data/ai-gpt-models-differences
    +23    """Enumeration for the supported OpenAI models. This class implements the AIModel protocol.
    +24    Reference: https://www.pluralsight.com/resources/blog/data/ai-gpt-models-differences
     25    """
     26
     27    # ID of the model to use. Currently, only the values below are supported:
     28
     29    # fmt: off
    -30    GPT_3_5_TURBO           = "gpt-3.5-turbo", 4096
    -31    GPT_3_5_TURBO_16K       = "gpt-3.5-turbo-16k", 16385
    -32    GPT_3_5_TURBO_1106      = "gpt-3.5-turbo-1106", 16385
    -33    GPT_3_5_TURBO_0301      = "gpt-3.5-turbo-0301", 4096
    -34    GPT_3_5_TURBO_0613      = "gpt-3.5-turbo-0613", 4096
    -35    GPT_3_5_TURBO_16K_0613  = "gpt-3.5-turbo-16k-0613", 16385
    -36    GPT_4                   = "gpt-4", 8192
    -37    GPT_4_O                 = "gpt-4o", 128000
    -38    GPT_4_O_MINI            = "gpt-4o-mini", 128000
    -39    GPT_4_0314              = "gpt-4-0314", 8192
    -40    GPT_4_0613              = "gpt-4-0613", 8192
    -41    GPT_4_32K               = "gpt-4-32k", 32768
    -42    GPT_4_32K_0314          = "gpt-4-32k-0314", 32768
    -43    GPT_4_32K_0613          = "gpt-4-32k-0613", 32768
    -44    GPT_4_1106_PREVIEW      = "gpt-4-1106-preview", 128000
    -45    GPT_4_VISION_PREVIEW    = "gpt-4-vision-preview", 128000
    -46    # fmt: on
    +30
    +31    GPT_3_5_TURBO           = "gpt-3.5-turbo", 4096
    +32    GPT_4                   = "gpt-4", 8192
    +33    GPT_4_TURBO             = "gpt-4-turbo", 128000
    +34    GPT_4_O                 = "gpt-4o", 128000
    +35    GPT_4_O_MINI            = "gpt-4o-mini", 128000
    +36    O1_PREVIEW              = "o1-preview", 128000
    +37    O1_MINI                 = "o1-mini", 128000
    +38
    +39    # fmt: on
    +40
    +41    @staticmethod
    +42    def models() -> List["AIModel"]:
    +43        """Get the list of available models for the engine.
    +44        :return: A list of available AI models.
    +45        """
    +46        return [OpenAIModel.of_value(m) for m in OpenAIModel.values()]
     47
     48    @staticmethod
    -49    def models() -> List["AIModel"]:
    -50        return [OpenAIModel.of_value(m) for m in OpenAIModel.values()]
    -51
    -52    @staticmethod
    -53    def of_name(model_name: str) -> "AIModel":
    +49    def of_name(model_name: str) -> "AIModel":
    +50        """Get the AIModel instance corresponding to the given model name.
    +51        :param model_name: The name of the AI model.
    +52        :return: The corresponding AIModel instance.
    +53        """
     54        found = next((m for m in OpenAIModel.models() if m.model_name() == model_name.casefold()), None)
     55        check_not_none(found, '"{}" name does not correspond to a valid "{}" enum', model_name, OpenAIModel.__name__)
     56        return found
    @@ -265,8 +238,8 @@ 

    -

    Enumeration for the supported OpenAi models. Implements the AIModel protocol. -Details at: https://www.pluralsight.com/resources/blog/data/ai-gpt-models-differences

    +

    Enumeration for the supported OpenAI models. This class implements the AIModel protocol. +Reference: https://www.pluralsight.com/resources/blog/data/ai-gpt-models-differences

    @@ -282,74 +255,26 @@

    -
    -
    - GPT_3_5_TURBO_16K = -GPT_3_5_TURBO_16K - - -
    - - - - -
    -
    -
    - GPT_3_5_TURBO_1106 = -GPT_3_5_TURBO_1106 - - -
    - - - - -
    -
    -
    - GPT_3_5_TURBO_0301 = -GPT_3_5_TURBO_0301 - - -
    - - - - -
    -
    -
    - GPT_3_5_TURBO_0613 = -GPT_3_5_TURBO_0613 - - -
    - - - - -
    -
    +
    - GPT_3_5_TURBO_16K_0613 = -GPT_3_5_TURBO_16K_0613 + GPT_4 = +GPT_4
    - +
    -
    +
    - GPT_4 = -GPT_4 + GPT_4_TURBO = +GPT_4_TURBO
    - + @@ -378,86 +303,26 @@

    -
    -
    - GPT_4_0314 = -GPT_4_0314 - - -
    - - - - -
    -
    -
    - GPT_4_0613 = -GPT_4_0613 - - -
    - - - - -
    -
    -
    - GPT_4_32K = -GPT_4_32K - - -
    - - - - -
    -
    +
    - GPT_4_32K_0314 = -GPT_4_32K_0314 + O1_PREVIEW = +O1_PREVIEW
    - +
    -
    +
    - GPT_4_32K_0613 = -GPT_4_32K_0613 + O1_MINI = +O1_MINI
    - - - - -
    -
    -
    - GPT_4_1106_PREVIEW = -GPT_4_1106_PREVIEW - - -
    - - - - -
    -
    -
    - GPT_4_VISION_PREVIEW = -GPT_4_VISION_PREVIEW - - -
    - + @@ -474,13 +339,24 @@

    -
    48    @staticmethod
    -49    def models() -> List["AIModel"]:
    -50        return [OpenAIModel.of_value(m) for m in OpenAIModel.values()]
    +            
    41    @staticmethod
    +42    def models() -> List["AIModel"]:
    +43        """Get the list of available models for the engine.
    +44        :return: A list of available AI models.
    +45        """
    +46        return [OpenAIModel.of_value(m) for m in OpenAIModel.values()]
     
    - +

    Get the list of available models for the engine.

    + +
    Returns
    + +
    +

    A list of available AI models.

    +
    +
    +
    @@ -495,15 +371,33 @@

    -
    52    @staticmethod
    -53    def of_name(model_name: str) -> "AIModel":
    +            
    48    @staticmethod
    +49    def of_name(model_name: str) -> "AIModel":
    +50        """Get the AIModel instance corresponding to the given model name.
    +51        :param model_name: The name of the AI model.
    +52        :return: The corresponding AIModel instance.
    +53        """
     54        found = next((m for m in OpenAIModel.models() if m.model_name() == model_name.casefold()), None)
     55        check_not_none(found, '"{}" name does not correspond to a valid "{}" enum', model_name, OpenAIModel.__name__)
     56        return found
     
    - +

    Get the AIModel instance corresponding to the given model name.

    + +
    Parameters
    + +
      +
    • model_name: The name of the AI model.
    • +
    + +
    Returns
    + +
    +

    The corresponding AIModel instance.

    +
    +
    +
    diff --git a/docs/api/askai/main/askai/core/engine/openai/openai_vision.html b/docs/api/askai/main/askai/core/engine/openai/openai_vision.html new file mode 100644 index 00000000..73592a2a --- /dev/null +++ b/docs/api/askai/main/askai/core/engine/openai/openai_vision.html @@ -0,0 +1,616 @@ + + + + + + + main.askai.core.engine.openai.openai_vision API documentation + + + + + + + + + +
    +
    +

    +main.askai.core.engine.openai.openai_vision

    + +

    @project: HsPyLib-AskAI +@package: askai.core.engine.openai + @file: openai_vision.py +@created: Tue, 5 Sep 2024 + @author: Hugo Saporetti Junior + @site: https://github.com/yorevs/askai +@license: MIT - Please refer to https://opensource.org/licenses/MIT

    + +

    Copyright (c) 2024, HomeSetup

    +
    + + + + + +
      1#!/usr/bin/env python3
    +  2# -*- coding: utf-8 -*-
    +  3
    +  4"""
    +  5   @project: HsPyLib-AskAI
    +  6   @package: askai.core.engine.openai
    +  7      @file: openai_vision.py
    +  8   @created: Tue, 5 Sep 2024
    +  9    @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior
    + 10      @site: https://github.com/yorevs/askai
    + 11   @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
    + 12
    + 13   Copyright (c) 2024, HomeSetup
    + 14"""
    + 15from askai.core.model.image_result import ImageResult
    + 16from askai.core.support.utilities import encode_image, find_file
    + 17from hspylib.core.metaclass.classpath import AnyPath
    + 18from hspylib.core.preconditions import check_argument
    + 19from hspylib.core.tools.commons import file_is_not_empty
    + 20from langchain.chains.transform import TransformChain
    + 21from langchain_core.language_models import BaseChatModel
    + 22from langchain_core.messages import BaseMessage, HumanMessage
    + 23from langchain_core.output_parsers import JsonOutputParser
    + 24from langchain_core.runnables import chain
    + 25from langchain_openai import ChatOpenAI
    + 26from retry import retry
    + 27from textwrap import dedent
    + 28from typing import TypeAlias
    + 29
    + 30import os
    + 31
    + 32Base64Image: TypeAlias = dict[str, str]
    + 33
    + 34MessageContent: TypeAlias = str | list[str] | dict
    + 35
    + 36
    + 37class OpenAIVision:
    + 38    """Provide a base class for OpenAI vision features. This class implements the AIVision protocol."""
    + 39
    + 40    _OUT_PARSER = JsonOutputParser(pydantic_object=ImageResult)
    + 41
    + 42    @staticmethod
    + 43    def _encode_image(inputs: dict) -> dict[str, str]:
    + 44        """Load an image from file and encode it as a base64 string.
    + 45        :param inputs: Dictionary containing the file path under a specific key.
    + 46        :return: Dictionary with the base64 encoded image string.
    + 47        """
    + 48        image_path = inputs["image_path"]
    + 49        check_argument(file_is_not_empty(image_path))
    + 50        image_base64: str = encode_image(image_path)
    + 51        return {"image": image_base64}
    + 52
    + 53    @staticmethod
    + 54    @chain
    + 55    def create_image_caption_chain(inputs: dict) -> MessageContent:
    + 56        """Invoke the image caption chain with image and prompt to generate a caption.
    + 57        :param inputs: Dictionary containing the image and prompt information.
    + 58        :return: MessageContent object with the generated caption.
    + 59        """
    + 60        model: BaseChatModel = ChatOpenAI(temperature=0.8, model="gpt-4o-mini", max_tokens=1024)
    + 61        msg: BaseMessage = model.invoke(
    + 62            [
    + 63                HumanMessage(
    + 64                    content=[
    + 65                        {"type": "text", "text": inputs["prompt"]},
    + 66                        {"type": "text", "text": OpenAIVision._OUT_PARSER.get_format_instructions()},
    + 67                        {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{inputs['image']}"}},
    + 68                    ]
    + 69                )
    + 70            ]
    + 71        )
    + 72        return msg.content
    + 73
    + 74    def template(self, question: str | None = None) -> str:
    + 75        return dedent(
    + 76            f"""
    + 77        Given the image, provide the following information:
    + 78        - A count of how many living beings are in the image.
    + 79        - A list of the main objects present in the image.
    + 80        - A description the atmosphere of the environment.
    + 81        - A list of detailed descriptions all living beings you find in the image.
    + 82        {'- ' + question if question else ''}"""
    + 83        ).strip()
    + 84
    + 85    @retry()
    + 86    def caption(self, filename: AnyPath, load_dir: AnyPath | None, question: str | None = None) -> str:
    + 87        """Generate a caption for the provided image.
    + 88        :param filename: File name of the image for which the caption is to be generated.
    + 89        :param load_dir: Optional directory path for loading related resources.
    + 90        :return: A dictionary containing the generated caption.
    + 91        """
    + 92        final_path: str = os.path.join(load_dir, filename) if load_dir else os.getcwd()
    + 93        check_argument(len((final_path := str(find_file(final_path) or ""))) > 0, f"Invalid image path: {final_path}")
    + 94        vision_prompt = self.template()
    + 95        load_image_chain = TransformChain(
    + 96            input_variables=["image_path"], output_variables=["image"], transform=self._encode_image
    + 97        )
    + 98        vision_chain = load_image_chain | self.create_image_caption_chain | OpenAIVision._OUT_PARSER
    + 99        args: dict[str, str] = {"image_path": f"{final_path}", "prompt": vision_prompt}
    +100        return str(vision_chain.invoke(args))
    +
    + + +
    +
    +
    + Base64Image: TypeAlias = +dict[str, str] + + +
    + + + + +
    +
    +
    + MessageContent: TypeAlias = +str | list[str] | dict + + +
    + + + + +
    +
    + +
    + + class + OpenAIVision: + + + +
    + +
     38class OpenAIVision:
    + 39    """Provide a base class for OpenAI vision features. This class implements the AIVision protocol."""
    + 40
    + 41    _OUT_PARSER = JsonOutputParser(pydantic_object=ImageResult)
    + 42
    + 43    @staticmethod
    + 44    def _encode_image(inputs: dict) -> dict[str, str]:
    + 45        """Load an image from file and encode it as a base64 string.
    + 46        :param inputs: Dictionary containing the file path under a specific key.
    + 47        :return: Dictionary with the base64 encoded image string.
    + 48        """
    + 49        image_path = inputs["image_path"]
    + 50        check_argument(file_is_not_empty(image_path))
    + 51        image_base64: str = encode_image(image_path)
    + 52        return {"image": image_base64}
    + 53
    + 54    @staticmethod
    + 55    @chain
    + 56    def create_image_caption_chain(inputs: dict) -> MessageContent:
    + 57        """Invoke the image caption chain with image and prompt to generate a caption.
    + 58        :param inputs: Dictionary containing the image and prompt information.
    + 59        :return: MessageContent object with the generated caption.
    + 60        """
    + 61        model: BaseChatModel = ChatOpenAI(temperature=0.8, model="gpt-4o-mini", max_tokens=1024)
    + 62        msg: BaseMessage = model.invoke(
    + 63            [
    + 64                HumanMessage(
    + 65                    content=[
    + 66                        {"type": "text", "text": inputs["prompt"]},
    + 67                        {"type": "text", "text": OpenAIVision._OUT_PARSER.get_format_instructions()},
    + 68                        {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{inputs['image']}"}},
    + 69                    ]
    + 70                )
    + 71            ]
    + 72        )
    + 73        return msg.content
    + 74
    + 75    def template(self, question: str | None = None) -> str:
    + 76        return dedent(
    + 77            f"""
    + 78        Given the image, provide the following information:
    + 79        - A count of how many living beings are in the image.
    + 80        - A list of the main objects present in the image.
    + 81        - A description the atmosphere of the environment.
    + 82        - A list of detailed descriptions all living beings you find in the image.
    + 83        {'- ' + question if question else ''}"""
    + 84        ).strip()
    + 85
    + 86    @retry()
    + 87    def caption(self, filename: AnyPath, load_dir: AnyPath | None, question: str | None = None) -> str:
    + 88        """Generate a caption for the provided image.
    + 89        :param filename: File name of the image for which the caption is to be generated.
    + 90        :param load_dir: Optional directory path for loading related resources.
    + 91        :return: A dictionary containing the generated caption.
    + 92        """
    + 93        final_path: str = os.path.join(load_dir, filename) if load_dir else os.getcwd()
    + 94        check_argument(len((final_path := str(find_file(final_path) or ""))) > 0, f"Invalid image path: {final_path}")
    + 95        vision_prompt = self.template()
    + 96        load_image_chain = TransformChain(
    + 97            input_variables=["image_path"], output_variables=["image"], transform=self._encode_image
    + 98        )
    + 99        vision_chain = load_image_chain | self.create_image_caption_chain | OpenAIVision._OUT_PARSER
    +100        args: dict[str, str] = {"image_path": f"{final_path}", "prompt": vision_prompt}
    +101        return str(vision_chain.invoke(args))
    +
    + + +

    Provide a base class for OpenAI vision features. This class implements the AIVision protocol.

    +
    + + +
    +
    + + def + create_image_caption_chain(unknown): + + +
    + + +

    RunnableLambda converts a python callable into a Runnable.

    + +

    Wrapping a callable in a RunnableLambda makes the callable usable +within either a sync or async context.

    + +

    RunnableLambda can be composed as any other Runnable and provides +seamless integration with LangChain tracing.

    + +

    RunnableLambda is best suited for code that does not need to support +streaming. If you need to support streaming (i.e., be able to operate +on chunks of inputs and yield chunks of outputs), use RunnableGenerator +instead.

    + +

    Note that if a RunnableLambda returns an instance of Runnable, that +instance is invoked (or streamed) during execution.

    + +

    Examples:

    + +
    ```python
    +
    + +

    This is a RunnableLambda

    + +

    from langchain_core.runnables import RunnableLambda

    + +

    def add_one(x: int) -> int: + return x + 1

    + +

    runnable = RunnableLambda(add_one)

    + +

    runnable.invoke(1) # returns 2 +runnable.batch([1, 2, 3]) # returns [2, 3, 4]

    + +

    Async is supported by default by delegating to the sync implementation

    + +

    await runnable.ainvoke(1) # returns 2 +await runnable.abatch([1, 2, 3]) # returns [2, 3, 4]

    + +

    Alternatively, can provide both synd and sync implementations

    + +

    async def add_one_async(x: int) -> int: + return x + 1

    + +

    runnable = RunnableLambda(add_one, afunc=add_one_async) +runnable.invoke(1) # Uses add_one +await runnable.ainvoke(1) # Uses add_one_async +```

    +
    + + +
    +
    + +
    + + def + template(self, question: str | None = None) -> str: + + + +
    + +
    75    def template(self, question: str | None = None) -> str:
    +76        return dedent(
    +77            f"""
    +78        Given the image, provide the following information:
    +79        - A count of how many living beings are in the image.
    +80        - A list of the main objects present in the image.
    +81        - A description the atmosphere of the environment.
    +82        - A list of detailed descriptions all living beings you find in the image.
    +83        {'- ' + question if question else ''}"""
    +84        ).strip()
    +
    + + + + +
    +
    + +
    +
    @retry()
    + + def + caption( self, filename: Union[pathlib.Path, str, NoneType], load_dir: Union[pathlib.Path, str, NoneType], question: str | None = None) -> str: + + + +
    + +
     86    @retry()
    + 87    def caption(self, filename: AnyPath, load_dir: AnyPath | None, question: str | None = None) -> str:
    + 88        """Generate a caption for the provided image.
    + 89        :param filename: File name of the image for which the caption is to be generated.
    + 90        :param load_dir: Optional directory path for loading related resources.
    + 91        :return: A dictionary containing the generated caption.
    + 92        """
    + 93        final_path: str = os.path.join(load_dir, filename) if load_dir else os.getcwd()
    + 94        check_argument(len((final_path := str(find_file(final_path) or ""))) > 0, f"Invalid image path: {final_path}")
    + 95        vision_prompt = self.template()
    + 96        load_image_chain = TransformChain(
    + 97            input_variables=["image_path"], output_variables=["image"], transform=self._encode_image
    + 98        )
    + 99        vision_chain = load_image_chain | self.create_image_caption_chain | OpenAIVision._OUT_PARSER
    +100        args: dict[str, str] = {"image_path": f"{final_path}", "prompt": vision_prompt}
    +101        return str(vision_chain.invoke(args))
    +
    + + +

    Generate a caption for the provided image.

    + +
    Parameters
    + +
      +
    • filename: File name of the image for which the caption is to be generated.
    • +
    • load_dir: Optional directory path for loading related resources.
    • +
    + +
    Returns
    + +
    +

    A dictionary containing the generated caption.

    +
    +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/docs/api/askai/main/askai/core/engine/openai/temperature.html b/docs/api/askai/main/askai/core/engine/openai/temperature.html index 48e9372c..b8ff140b 100644 --- a/docs/api/askai/main/askai/core/engine/openai/temperature.html +++ b/docs/api/askai/main/askai/core/engine/openai/temperature.html @@ -3,7 +3,7 @@ - + main.askai.core.engine.openai.temperature API documentation @@ -116,59 +116,60 @@

    17 18 19class Temperature(Enumeration): -20 """Provide some recommended temperature x top_p combinations for ChatGPT prompts. -21 Ref:. https://community.openai.com/t/cheat-sheet-mastering-temperature-and-top-p-in-chatgpt-api/172683 -22 -23 - Lower temperature (e.g., 0.1 - 0.4): Produces more focused, conservative, and consistent responses. -24 This is useful when the marketer needs factual information, precise answers, or messaging that adheres closely -25 to a specific format or brand guideline. -26 -27 - Moderate temperature (e.g., 0.5 - 0.7): Strikes a balance between creativity and consistency. -28 This setting can be useful for general content generation, where a blend of accuracy and inventiveness -29 is desired. -30 -31 - Higher temperature (e.g., 0.8 - 1.0): Generates more creative, diverse, and unexpected outputs. -32 Marketers may prefer this setting when brainstorming innovative campaign ideas, crafting engaging social media -33 content, or seeking fresh perspectives on a topic. -34 """ -35 -36 # fmt: off -37 -38 COLDEST = 0.0, 0. -39 -40 # Generates data analysis scripts that are more likely to be correct and efficient. Output is more deterministic -41 # and focused. -42 DATA_ANALYSIS = 0.2, 0.1 -43 -44 # Generates code that adheres to established patterns and conventions. Output is more deterministic and focused. -45 # Useful for generating syntactically correct code. -46 CODE_GENERATION = 0.2, 0.1 -47 -48 # Generates code comments that are more likely to be concise and relevant. Output is more deterministic -49 # and adheres to conventions. -50 CODE_COMMENT_GENERATION = 0.3, 0.2 -51 -52 # Generates conversational responses that balance coherence and diversity. Output is more natural and engaging. -53 CHATBOT_RESPONSES = 0.5, 0.5 -54 -55 # Generates code that explores alternative solutions and creative approaches. Output is less constrained by -56 # established patterns. -57 EXPLORATORY_CODE_WRITING = 0.6, 0.7 -58 -59 # Generates creative and diverse text for storytelling. Output is more exploratory and less constrained by patterns. -60 CREATIVE_WRITING = 0.7, 0.8 -61 -62 HOTTEST = 1.0, 1.0 -63 -64 # fmt: on -65 -66 @property -67 def temp(self) -> float: -68 return self.value[0] -69 -70 @property -71 def top_p(self) -> float: -72 return self.value[1] +20 """Provides recommended temperature and top_p combinations for ChatGPT prompts. This enumeration is designed to +21 help users select appropriate temperature settings for different types of prompts, balancing creativity and +22 consistency according to the task at hand. +23 Reference: https://community.openai.com/t/cheat-sheet-mastering-temperature-and-top-p-in-chatgpt-api/172683 +24 +25 - Lower temperature (e.g., 0.1 - 0.4): Produces more focused, conservative, and consistent responses. +26 Useful for scenarios requiring factual information, precise answers, or adherence to specific formats or brand +27 guidelines. +28 +29 - Moderate temperature (e.g., 0.5 - 0.7): Strikes a balance between creativity and consistency. +30 Ideal for general content generation where a blend of accuracy and inventiveness is desired. +31 +32 - Higher temperature (e.g., 0.8 - 1.0): Generates more creative, diverse, and unexpected outputs. +33 Suitable for brainstorming innovative ideas, crafting engaging social media content, or exploring fresh +34 perspectives on a topic. +35 """ +36 +37 # fmt: off +38 +39 COLDEST = 0.0, 0. +40 +41 # Generates data analysis scripts that are more likely to be correct and efficient. Output is more deterministic +42 # and focused. +43 DATA_ANALYSIS = 0.2, 0.1 +44 +45 # Generates code that adheres to established patterns and conventions. Output is more deterministic and focused. +46 # Useful for generating syntactically correct code. +47 CODE_GENERATION = 0.2, 0.1 +48 +49 # Generates code comments that are more likely to be concise and relevant. Output is more deterministic +50 # and adheres to conventions. +51 CODE_COMMENT_GENERATION = 0.3, 0.2 +52 +53 # Generates conversational responses that balance coherence and diversity. Output is more natural and engaging. +54 CHATBOT_RESPONSES = 0.5, 0.5 +55 +56 # Generates code that explores alternative solutions and creative approaches. Output is less constrained by +57 # established patterns. +58 EXPLORATORY_CODE_WRITING = 0.6, 0.7 +59 +60 # Generates creative and diverse text for storytelling. Output is more exploratory and less constrained by patterns. +61 CREATIVE_WRITING = 0.7, 0.8 +62 +63 HOTTEST = 1.0, 1.0 +64 +65 # fmt: on +66 +67 @property +68 def temp(self) -> float: +69 return self.value[0] +70 +71 @property +72 def top_p(self) -> float: +73 return self.value[1]

    @@ -185,75 +186,77 @@

    20class Temperature(Enumeration):
    -21    """Provide some recommended temperature x top_p combinations for ChatGPT prompts.
    -22    Ref:. https://community.openai.com/t/cheat-sheet-mastering-temperature-and-top-p-in-chatgpt-api/172683
    -23
    -24    - Lower temperature (e.g., 0.1 - 0.4): Produces more focused, conservative, and consistent responses.
    -25    This is useful when the marketer needs factual information, precise answers, or messaging that adheres closely
    -26    to a specific format or brand guideline.
    -27
    -28    - Moderate temperature (e.g., 0.5 - 0.7): Strikes a balance between creativity and consistency.
    -29    This setting can be useful for general content generation, where a blend of accuracy and inventiveness
    -30    is desired.
    -31
    -32    - Higher temperature (e.g., 0.8 - 1.0): Generates more creative, diverse, and unexpected outputs.
    -33    Marketers may prefer this setting when brainstorming innovative campaign ideas, crafting engaging social media
    -34    content, or seeking fresh perspectives on a topic.
    -35    """
    -36
    -37    # fmt: off
    -38
    -39    COLDEST                     = 0.0, 0.
    -40
    -41    # Generates data analysis scripts that are more likely to be correct and efficient. Output is more deterministic
    -42    # and focused.
    -43    DATA_ANALYSIS               = 0.2, 0.1
    -44
    -45    # Generates code that adheres to established patterns and conventions. Output is more deterministic and focused.
    -46    # Useful for generating syntactically correct code.
    -47    CODE_GENERATION             = 0.2, 0.1
    -48
    -49    # Generates code comments that are more likely to be concise and relevant. Output is more deterministic
    -50    # and adheres to conventions.
    -51    CODE_COMMENT_GENERATION     = 0.3, 0.2
    -52
    -53    # Generates conversational responses that balance coherence and diversity. Output is more natural and engaging.
    -54    CHATBOT_RESPONSES           = 0.5, 0.5
    -55
    -56    # Generates code that explores alternative solutions and creative approaches. Output is less constrained by
    -57    # established patterns.
    -58    EXPLORATORY_CODE_WRITING    = 0.6, 0.7
    -59
    -60    # Generates creative and diverse text for storytelling. Output is more exploratory and less constrained by patterns.
    -61    CREATIVE_WRITING            = 0.7, 0.8
    -62
    -63    HOTTEST                     = 1.0, 1.0
    -64
    -65    # fmt: on
    -66
    -67    @property
    -68    def temp(self) -> float:
    -69        return self.value[0]
    -70
    -71    @property
    -72    def top_p(self) -> float:
    -73        return self.value[1]
    +21    """Provides recommended temperature and top_p combinations for ChatGPT prompts. This enumeration is designed to
    +22    help users select appropriate temperature settings for different types of prompts, balancing creativity and
    +23    consistency according to the task at hand.
    +24    Reference: https://community.openai.com/t/cheat-sheet-mastering-temperature-and-top-p-in-chatgpt-api/172683
    +25
    +26    - Lower temperature (e.g., 0.1 - 0.4): Produces more focused, conservative, and consistent responses.
    +27      Useful for scenarios requiring factual information, precise answers, or adherence to specific formats or brand
    +28      guidelines.
    +29
    +30    - Moderate temperature (e.g., 0.5 - 0.7): Strikes a balance between creativity and consistency.
    +31      Ideal for general content generation where a blend of accuracy and inventiveness is desired.
    +32
    +33    - Higher temperature (e.g., 0.8 - 1.0): Generates more creative, diverse, and unexpected outputs.
    +34      Suitable for brainstorming innovative ideas, crafting engaging social media content, or exploring fresh
    +35      perspectives on a topic.
    +36    """
    +37
    +38    # fmt: off
    +39
    +40    COLDEST                     = 0.0, 0.
    +41
    +42    # Generates data analysis scripts that are more likely to be correct and efficient. Output is more deterministic
    +43    # and focused.
    +44    DATA_ANALYSIS               = 0.2, 0.1
    +45
    +46    # Generates code that adheres to established patterns and conventions. Output is more deterministic and focused.
    +47    # Useful for generating syntactically correct code.
    +48    CODE_GENERATION             = 0.2, 0.1
    +49
    +50    # Generates code comments that are more likely to be concise and relevant. Output is more deterministic
    +51    # and adheres to conventions.
    +52    CODE_COMMENT_GENERATION     = 0.3, 0.2
    +53
    +54    # Generates conversational responses that balance coherence and diversity. Output is more natural and engaging.
    +55    CHATBOT_RESPONSES           = 0.5, 0.5
    +56
    +57    # Generates code that explores alternative solutions and creative approaches. Output is less constrained by
    +58    # established patterns.
    +59    EXPLORATORY_CODE_WRITING    = 0.6, 0.7
    +60
    +61    # Generates creative and diverse text for storytelling. Output is more exploratory and less constrained by patterns.
    +62    CREATIVE_WRITING            = 0.7, 0.8
    +63
    +64    HOTTEST                     = 1.0, 1.0
    +65
    +66    # fmt: on
    +67
    +68    @property
    +69    def temp(self) -> float:
    +70        return self.value[0]
    +71
    +72    @property
    +73    def top_p(self) -> float:
    +74        return self.value[1]
     
    -

    Provide some recommended temperature x top_p combinations for ChatGPT prompts. -Ref:. https://community.openai.com/t/cheat-sheet-mastering-temperature-and-top-p-in-chatgpt-api/172683

    +

    Provides recommended temperature and top_p combinations for ChatGPT prompts. This enumeration is designed to +help users select appropriate temperature settings for different types of prompts, balancing creativity and +consistency according to the task at hand. +Reference: https://community.openai.com/t/cheat-sheet-mastering-temperature-and-top-p-in-chatgpt-api/172683

    • Lower temperature (e.g., 0.1 - 0.4): Produces more focused, conservative, and consistent responses. -This is useful when the marketer needs factual information, precise answers, or messaging that adheres closely -to a specific format or brand guideline.

    • +Useful for scenarios requiring factual information, precise answers, or adherence to specific formats or brand +guidelines.

    • Moderate temperature (e.g., 0.5 - 0.7): Strikes a balance between creativity and consistency. -This setting can be useful for general content generation, where a blend of accuracy and inventiveness -is desired.

    • +Ideal for general content generation where a blend of accuracy and inventiveness is desired.

    • Higher temperature (e.g., 0.8 - 1.0): Generates more creative, diverse, and unexpected outputs. -Marketers may prefer this setting when brainstorming innovative campaign ideas, crafting engaging social media -content, or seeking fresh perspectives on a topic.

    • +Suitable for brainstorming innovative ideas, crafting engaging social media content, or exploring fresh +perspectives on a topic.

    @@ -363,9 +366,9 @@

    -
    67    @property
    -68    def temp(self) -> float:
    -69        return self.value[0]
    +            
    68    @property
    +69    def temp(self) -> float:
    +70        return self.value[0]
     
    @@ -381,9 +384,9 @@

    -
    71    @property
    -72    def top_p(self) -> float:
    -73        return self.value[1]
    +            
    72    @property
    +73    def top_p(self) -> float:
    +74        return self.value[1]
     
    diff --git a/docs/api/askai/main/askai/core/enums.html b/docs/api/askai/main/askai/core/enums.html index 9bbde3ed..85a83f5f 100644 --- a/docs/api/askai/main/askai/core/enums.html +++ b/docs/api/askai/main/askai/core/enums.html @@ -3,7 +3,7 @@ - + main.askai.core.enums API documentation @@ -32,6 +32,7 @@

    Submodules

  • acc_response
  • router_mode
  • routing_model
  • +
  • verbosity
  • @@ -57,7 +58,7 @@

     1# _*_ coding: utf-8 _*_
      2#
    - 3# hspylib-askai v1.0.11
    + 3# hspylib-askai v1.0.13
      4#
      5# Package: main.askai.core.enums
      6"""Package initialization."""
    @@ -65,9 +66,10 @@ 

    8__all__ = [ 9 'acc_response', 10 'router_mode', -11 'routing_model' -12] -13__version__ = '1.0.11' +11 'routing_model', +12 'verbosity' +13] +14__version__ = '1.0.13'

    diff --git a/docs/api/askai/main/askai/core/enums/acc_response.html b/docs/api/askai/main/askai/core/enums/acc_response.html index af75a59f..69ccc973 100644 --- a/docs/api/askai/main/askai/core/enums/acc_response.html +++ b/docs/api/askai/main/askai/core/enums/acc_response.html @@ -3,7 +3,7 @@ - + main.askai.core.enums.acc_response API documentation @@ -48,6 +48,9 @@

    API Documentation

  • BAD
  • +
  • + INTERRUPT +
  • matches
  • @@ -75,6 +78,9 @@

    API Documentation

  • is_good
  • +
  • + is_interrupt +
  • passed
  • @@ -112,97 +118,122 @@

    -
     1#!/usr/bin/env python3
    - 2# -*- coding: utf-8 -*-
    - 3
    - 4"""
    - 5   @project: HsPyLib-AskAI
    - 6   @package: askai.core.enums.acc_response
    - 7      @file: acc_response.py
    - 8   @created: Tue, 23 Apr 2024
    - 9    @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior
    -10      @site: https://github.com/yorevs/askai
    -11   @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
    -12
    -13   Copyright (c) 2024, HomeSetup
    -14"""
    -15from hspylib.core.enums.enumeration import Enumeration
    -16from typing import Literal
    -17
    -18import re
    -19
    -20
    -21class AccResponse(Enumeration):
    -22    """Keep track of the accuracy responses (color classifications)."""
    -23
    -24    # fmt: off
    -25
    -26    EXCELLENT   = 'Blue'
    -27
    -28    GOOD        = 'Green'
    -29
    -30    MODERATE    = 'Yellow'
    -31
    -32    INCOMPLETE  = 'Orange'
    -33
    -34    BAD         = 'Red'
    -35
    -36    # fmt: on
    -37
    -38    @classmethod
    -39    def matches(cls, output: str) -> re.Match:
    -40        return re.search(cls._re(), output.replace("\n", " "), flags=re.IGNORECASE)
    -41
    -42    @classmethod
    -43    def _re(cls) -> str:
    -44        return rf"^\$?({'|'.join(cls.values())})[:,-]\s*[0-9]+%\s+(.+)"
    -45
    -46    @classmethod
    -47    def strip_code(cls, message: str) -> str:
    -48        """Strip the color code from the message"""
    -49        mat = cls.matches(message)
    -50        return str(mat.group(2)).strip() if mat else message.strip()
    -51
    -52    @classmethod
    -53    def of_status(cls, status: str, reasoning: str | None) -> "AccResponse":
    -54        resp = cls.of_value(status.title())
    -55        if reasoning and (mat := re.match(r'(^[0-9]{1,3})%\s+(.*)', reasoning)):
    -56            resp.rate = float(mat.group(1))
    -57            resp.reasoning = mat.group(2)
    -58        return resp
    -59
    -60    def __init__(self, color: Literal['Blue', 'Green', 'Yellow', 'Orange', 'Red']):
    -61        self.color = color
    -62        self.reasoning: str | None = None
    -63        self.rate: float | None = None
    -64
    -65    def __str__(self):
    -66        details: str = f"{' -> ' + str(self.rate) + '% ' + self.reasoning if self.reasoning else ''}"
    -67        return f"{self.name}{details}"
    -68
    -69    @property
    -70    def is_bad(self) -> bool:
    -71        return self in [self.BAD, self.INCOMPLETE]
    -72
    -73    @property
    -74    def is_moderate(self) -> bool:
    -75        return self == self.MODERATE
    -76
    -77    @property
    -78    def is_good(self) -> bool:
    -79        return self in [self.GOOD, self.EXCELLENT]
    -80
    -81    def passed(self, threshold: 'AccResponse') -> bool:
    -82        """whether the response matches a 'PASS' classification."""
    -83        if isinstance(threshold, AccResponse):
    -84            idx_self, idx_threshold = None, None
    -85            for i, v in enumerate(AccResponse.values()):
    -86                if v == self.value:
    -87                    idx_self = i
    -88                if v == threshold.value:
    -89                    idx_threshold = i
    -90            return idx_self is not None and idx_threshold is not None and idx_self <= idx_threshold
    -91        return False
    +                        
      1#!/usr/bin/env python3
    +  2# -*- coding: utf-8 -*-
    +  3
    +  4"""
    +  5   @project: HsPyLib-AskAI
    +  6   @package: askai.core.enums.acc_response
    +  7      @file: acc_response.py
    +  8   @created: Tue, 23 Apr 2024
    +  9    @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior
    + 10      @site: https://github.com/yorevs/askai
    + 11   @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
    + 12
    + 13   Copyright (c) 2024, HomeSetup
    + 14"""
    + 15from hspylib.core.enums.enumeration import Enumeration
    + 16from typing import Literal
    + 17
    + 18import re
    + 19
    + 20
    + 21class AccResponse(Enumeration):
    + 22    """Track and classify accuracy responses based on color classifications. This class provides an enumeration of
    + 23    possible accuracy responses, which are typically represented by different colors.
    + 24    """
    + 25
    + 26    # fmt: off
    + 27
    + 28    EXCELLENT   = 'Blue'
    + 29
    + 30    GOOD        = 'Green'
    + 31
    + 32    MODERATE    = 'Yellow'
    + 33
    + 34    INCOMPLETE  = 'Orange'
    + 35
    + 36    BAD         = 'Red'
    + 37
    + 38    INTERRUPT   = 'Black'
    + 39
    + 40    # fmt: on
    + 41
    + 42    @classmethod
    + 43    def matches(cls, output: str) -> re.Match:
    + 44        """Find a match in the given output string.
    + 45        :param output: The string to search for a match.
    + 46        :return: A match object if a match is found.
    + 47        :raises: re.error if an error occurs during the matching process.
    + 48        """
    + 49        return re.search(cls._re(), output.replace("\n", " "), flags=re.IGNORECASE)
    + 50
    + 51    @classmethod
    + 52    def _re(cls) -> str:
    + 53        """TODO"""
    + 54        return rf"^\$?({'|'.join(cls.values())})[:,-]\s*[0-9]+%\s+(.+)"
    + 55
    + 56    @classmethod
    + 57    def strip_code(cls, message: str) -> str:
    + 58        """Strip the color code from the message.
    + 59        :param message: The message from which to strip color codes.
    + 60        :return: The message with color codes removed.
    + 61        """
    + 62        mat = cls.matches(message)
    + 63        return str(mat.group(2)).strip() if mat else message.strip()
    + 64
    + 65    @classmethod
    + 66    def of_status(cls, status: str, reasoning: str | None) -> "AccResponse":
    + 67        """Create an AccResponse instance based on status and optional reasoning.
    + 68        :param status: The status as a string.
    + 69        :param reasoning: Optional reasoning for the status, formatted as '<percentage>% <details>'.
    + 70        :return: An instance of AccResponse with the given status and reasoning.
    + 71        """
    + 72        resp = cls.of_value(status.title())
    + 73        if reasoning and (mat := re.match(r"(^[0-9]{1,3})%\s+(.*)", reasoning)):
    + 74            resp.rate = float(mat.group(1))
    + 75            resp.reasoning = mat.group(2)
    + 76        return resp
    + 77
    + 78    def __init__(self, color: Literal["Blue", "Green", "Yellow", "Orange", "Red"]):
    + 79        self.color = color
    + 80        self.reasoning: str | None = None
    + 81        self.rate: float | None = None
    + 82
    + 83    def __str__(self):
    + 84        details: str = f"{' -> ' + str(self.rate) + '% ' + self.reasoning if self.reasoning else ''}"
    + 85        return f"{self.name}{details}"
    + 86
    + 87    @property
    + 88    def is_bad(self) -> bool:
    + 89        return self in [self.BAD, self.INCOMPLETE]
    + 90
    + 91    @property
    + 92    def is_moderate(self) -> bool:
    + 93        return self == self.MODERATE
    + 94
    + 95    @property
    + 96    def is_good(self) -> bool:
    + 97        return self in [self.GOOD, self.EXCELLENT]
    + 98
    + 99    @property
    +100    def is_interrupt(self) -> bool:
    +101        return self == self.INTERRUPT
    +102
    +103    def passed(self, threshold: "AccResponse") -> bool:
    +104        """Determine whether the response matches a 'PASS' classification.
    +105        :param threshold: The threshold or criteria used to determine a 'PASS' classification.
    +106        :return: True if the response meets or exceeds the 'PASS' threshold, otherwise False.
    +107        """
    +108        if isinstance(threshold, AccResponse):
    +109            idx_self, idx_threshold = None, None
    +110            for i, v in enumerate(AccResponse.values()):
    +111                if v == self.value:
    +112                    idx_self = i
    +113                if v == threshold.value:
    +114                    idx_threshold = i
    +115            return idx_self is not None and idx_threshold is not None and idx_self <= idx_threshold
    +116        return False
     
    @@ -218,81 +249,107 @@

    -
    22class AccResponse(Enumeration):
    -23    """Keep track of the accuracy responses (color classifications)."""
    -24
    -25    # fmt: off
    -26
    -27    EXCELLENT   = 'Blue'
    -28
    -29    GOOD        = 'Green'
    -30
    -31    MODERATE    = 'Yellow'
    -32
    -33    INCOMPLETE  = 'Orange'
    -34
    -35    BAD         = 'Red'
    -36
    -37    # fmt: on
    -38
    -39    @classmethod
    -40    def matches(cls, output: str) -> re.Match:
    -41        return re.search(cls._re(), output.replace("\n", " "), flags=re.IGNORECASE)
    -42
    -43    @classmethod
    -44    def _re(cls) -> str:
    -45        return rf"^\$?({'|'.join(cls.values())})[:,-]\s*[0-9]+%\s+(.+)"
    -46
    -47    @classmethod
    -48    def strip_code(cls, message: str) -> str:
    -49        """Strip the color code from the message"""
    -50        mat = cls.matches(message)
    -51        return str(mat.group(2)).strip() if mat else message.strip()
    -52
    -53    @classmethod
    -54    def of_status(cls, status: str, reasoning: str | None) -> "AccResponse":
    -55        resp = cls.of_value(status.title())
    -56        if reasoning and (mat := re.match(r'(^[0-9]{1,3})%\s+(.*)', reasoning)):
    -57            resp.rate = float(mat.group(1))
    -58            resp.reasoning = mat.group(2)
    -59        return resp
    -60
    -61    def __init__(self, color: Literal['Blue', 'Green', 'Yellow', 'Orange', 'Red']):
    -62        self.color = color
    -63        self.reasoning: str | None = None
    -64        self.rate: float | None = None
    -65
    -66    def __str__(self):
    -67        details: str = f"{' -> ' + str(self.rate) + '% ' + self.reasoning if self.reasoning else ''}"
    -68        return f"{self.name}{details}"
    -69
    -70    @property
    -71    def is_bad(self) -> bool:
    -72        return self in [self.BAD, self.INCOMPLETE]
    -73
    -74    @property
    -75    def is_moderate(self) -> bool:
    -76        return self == self.MODERATE
    -77
    -78    @property
    -79    def is_good(self) -> bool:
    -80        return self in [self.GOOD, self.EXCELLENT]
    -81
    -82    def passed(self, threshold: 'AccResponse') -> bool:
    -83        """whether the response matches a 'PASS' classification."""
    -84        if isinstance(threshold, AccResponse):
    -85            idx_self, idx_threshold = None, None
    -86            for i, v in enumerate(AccResponse.values()):
    -87                if v == self.value:
    -88                    idx_self = i
    -89                if v == threshold.value:
    -90                    idx_threshold = i
    -91            return idx_self is not None and idx_threshold is not None and idx_self <= idx_threshold
    -92        return False
    +            
     22class AccResponse(Enumeration):
    + 23    """Track and classify accuracy responses based on color classifications. This class provides an enumeration of
    + 24    possible accuracy responses, which are typically represented by different colors.
    + 25    """
    + 26
    + 27    # fmt: off
    + 28
    + 29    EXCELLENT   = 'Blue'
    + 30
    + 31    GOOD        = 'Green'
    + 32
    + 33    MODERATE    = 'Yellow'
    + 34
    + 35    INCOMPLETE  = 'Orange'
    + 36
    + 37    BAD         = 'Red'
    + 38
    + 39    INTERRUPT   = 'Black'
    + 40
    + 41    # fmt: on
    + 42
    + 43    @classmethod
    + 44    def matches(cls, output: str) -> re.Match:
    + 45        """Find a match in the given output string.
    + 46        :param output: The string to search for a match.
    + 47        :return: A match object if a match is found.
    + 48        :raises: re.error if an error occurs during the matching process.
    + 49        """
    + 50        return re.search(cls._re(), output.replace("\n", " "), flags=re.IGNORECASE)
    + 51
    + 52    @classmethod
    + 53    def _re(cls) -> str:
    + 54        """TODO"""
    + 55        return rf"^\$?({'|'.join(cls.values())})[:,-]\s*[0-9]+%\s+(.+)"
    + 56
    + 57    @classmethod
    + 58    def strip_code(cls, message: str) -> str:
    + 59        """Strip the color code from the message.
    + 60        :param message: The message from which to strip color codes.
    + 61        :return: The message with color codes removed.
    + 62        """
    + 63        mat = cls.matches(message)
    + 64        return str(mat.group(2)).strip() if mat else message.strip()
    + 65
    + 66    @classmethod
    + 67    def of_status(cls, status: str, reasoning: str | None) -> "AccResponse":
    + 68        """Create an AccResponse instance based on status and optional reasoning.
    + 69        :param status: The status as a string.
    + 70        :param reasoning: Optional reasoning for the status, formatted as '<percentage>% <details>'.
    + 71        :return: An instance of AccResponse with the given status and reasoning.
    + 72        """
    + 73        resp = cls.of_value(status.title())
    + 74        if reasoning and (mat := re.match(r"(^[0-9]{1,3})%\s+(.*)", reasoning)):
    + 75            resp.rate = float(mat.group(1))
    + 76            resp.reasoning = mat.group(2)
    + 77        return resp
    + 78
    + 79    def __init__(self, color: Literal["Blue", "Green", "Yellow", "Orange", "Red"]):
    + 80        self.color = color
    + 81        self.reasoning: str | None = None
    + 82        self.rate: float | None = None
    + 83
    + 84    def __str__(self):
    + 85        details: str = f"{' -> ' + str(self.rate) + '% ' + self.reasoning if self.reasoning else ''}"
    + 86        return f"{self.name}{details}"
    + 87
    + 88    @property
    + 89    def is_bad(self) -> bool:
    + 90        return self in [self.BAD, self.INCOMPLETE]
    + 91
    + 92    @property
    + 93    def is_moderate(self) -> bool:
    + 94        return self == self.MODERATE
    + 95
    + 96    @property
    + 97    def is_good(self) -> bool:
    + 98        return self in [self.GOOD, self.EXCELLENT]
    + 99
    +100    @property
    +101    def is_interrupt(self) -> bool:
    +102        return self == self.INTERRUPT
    +103
    +104    def passed(self, threshold: "AccResponse") -> bool:
    +105        """Determine whether the response matches a 'PASS' classification.
    +106        :param threshold: The threshold or criteria used to determine a 'PASS' classification.
    +107        :return: True if the response meets or exceeds the 'PASS' threshold, otherwise False.
    +108        """
    +109        if isinstance(threshold, AccResponse):
    +110            idx_self, idx_threshold = None, None
    +111            for i, v in enumerate(AccResponse.values()):
    +112                if v == self.value:
    +113                    idx_self = i
    +114                if v == threshold.value:
    +115                    idx_threshold = i
    +116            return idx_self is not None and idx_threshold is not None and idx_self <= idx_threshold
    +117        return False
     
    -

    Keep track of the accuracy responses (color classifications).

    +

    Track and classify accuracy responses based on color classifications. This class provides an enumeration of +possible accuracy responses, which are typically represented by different colors.

    @@ -355,6 +412,18 @@

    +

    +
    +
    + INTERRUPT = +INTERRUPT + + +
    + + + +
    @@ -368,13 +437,38 @@

    -
    39    @classmethod
    -40    def matches(cls, output: str) -> re.Match:
    -41        return re.search(cls._re(), output.replace("\n", " "), flags=re.IGNORECASE)
    +            
    43    @classmethod
    +44    def matches(cls, output: str) -> re.Match:
    +45        """Find a match in the given output string.
    +46        :param output: The string to search for a match.
    +47        :return: A match object if a match is found.
    +48        :raises: re.error if an error occurs during the matching process.
    +49        """
    +50        return re.search(cls._re(), output.replace("\n", " "), flags=re.IGNORECASE)
     
    - +

    Find a match in the given output string.

    + +
    Parameters
    + +
      +
    • output: The string to search for a match.
    • +
    + +
    Returns
    + +
    +

    A match object if a match is found.

    +
    + +
    Raises
    + +
      +
    • re.error if an error occurs during the matching process.
    • +
    +
    +
    @@ -389,15 +483,30 @@

    -
    47    @classmethod
    -48    def strip_code(cls, message: str) -> str:
    -49        """Strip the color code from the message"""
    -50        mat = cls.matches(message)
    -51        return str(mat.group(2)).strip() if mat else message.strip()
    +            
    57    @classmethod
    +58    def strip_code(cls, message: str) -> str:
    +59        """Strip the color code from the message.
    +60        :param message: The message from which to strip color codes.
    +61        :return: The message with color codes removed.
    +62        """
    +63        mat = cls.matches(message)
    +64        return str(mat.group(2)).strip() if mat else message.strip()
     
    -

    Strip the color code from the message

    +

    Strip the color code from the message.

    + +
    Parameters
    + +
      +
    • message: The message from which to strip color codes.
    • +
    + +
    Returns
    + +
    +

    The message with color codes removed.

    +
    @@ -414,17 +523,37 @@

    -
    53    @classmethod
    -54    def of_status(cls, status: str, reasoning: str | None) -> "AccResponse":
    -55        resp = cls.of_value(status.title())
    -56        if reasoning and (mat := re.match(r'(^[0-9]{1,3})%\s+(.*)', reasoning)):
    -57            resp.rate = float(mat.group(1))
    -58            resp.reasoning = mat.group(2)
    -59        return resp
    +            
    66    @classmethod
    +67    def of_status(cls, status: str, reasoning: str | None) -> "AccResponse":
    +68        """Create an AccResponse instance based on status and optional reasoning.
    +69        :param status: The status as a string.
    +70        :param reasoning: Optional reasoning for the status, formatted as '<percentage>% <details>'.
    +71        :return: An instance of AccResponse with the given status and reasoning.
    +72        """
    +73        resp = cls.of_value(status.title())
    +74        if reasoning and (mat := re.match(r"(^[0-9]{1,3})%\s+(.*)", reasoning)):
    +75            resp.rate = float(mat.group(1))
    +76            resp.reasoning = mat.group(2)
    +77        return resp
     
    - +

    Create an AccResponse instance based on status and optional reasoning.

    + +
    Parameters
    + +
      +
    • status: The status as a string.
    • +
    • reasoning: Optional reasoning for the status, formatted as '%
      '.
    • +
    + +
    Returns
    + +
    +

    An instance of AccResponse with the given status and reasoning.

    +
    +
    +
    @@ -469,9 +598,9 @@

    -
    70    @property
    -71    def is_bad(self) -> bool:
    -72        return self in [self.BAD, self.INCOMPLETE]
    +            
    88    @property
    +89    def is_bad(self) -> bool:
    +90        return self in [self.BAD, self.INCOMPLETE]
     
    @@ -487,9 +616,9 @@

    -
    74    @property
    -75    def is_moderate(self) -> bool:
    -76        return self == self.MODERATE
    +            
    92    @property
    +93    def is_moderate(self) -> bool:
    +94        return self == self.MODERATE
     
    @@ -505,9 +634,27 @@

    -
    78    @property
    -79    def is_good(self) -> bool:
    -80        return self in [self.GOOD, self.EXCELLENT]
    +            
    96    @property
    +97    def is_good(self) -> bool:
    +98        return self in [self.GOOD, self.EXCELLENT]
    +
    + + + + +
    +
    + +
    + is_interrupt: bool + + + +
    + +
    100    @property
    +101    def is_interrupt(self) -> bool:
    +102        return self == self.INTERRUPT
     
    @@ -525,21 +672,36 @@

    -
    82    def passed(self, threshold: 'AccResponse') -> bool:
    -83        """whether the response matches a 'PASS' classification."""
    -84        if isinstance(threshold, AccResponse):
    -85            idx_self, idx_threshold = None, None
    -86            for i, v in enumerate(AccResponse.values()):
    -87                if v == self.value:
    -88                    idx_self = i
    -89                if v == threshold.value:
    -90                    idx_threshold = i
    -91            return idx_self is not None and idx_threshold is not None and idx_self <= idx_threshold
    -92        return False
    +            
    104    def passed(self, threshold: "AccResponse") -> bool:
    +105        """Determine whether the response matches a 'PASS' classification.
    +106        :param threshold: The threshold or criteria used to determine a 'PASS' classification.
    +107        :return: True if the response meets or exceeds the 'PASS' threshold, otherwise False.
    +108        """
    +109        if isinstance(threshold, AccResponse):
    +110            idx_self, idx_threshold = None, None
    +111            for i, v in enumerate(AccResponse.values()):
    +112                if v == self.value:
    +113                    idx_self = i
    +114                if v == threshold.value:
    +115                    idx_threshold = i
    +116            return idx_self is not None and idx_threshold is not None and idx_self <= idx_threshold
    +117        return False
     
    -

    whether the response matches a 'PASS' classification.

    +

    Determine whether the response matches a 'PASS' classification.

    + +
    Parameters
    + +
      +
    • threshold: The threshold or criteria used to determine a 'PASS' classification.
    • +
    + +
    Returns
    + +
    +

    True if the response meets or exceeds the 'PASS' threshold, otherwise False.

    +
    diff --git a/docs/api/askai/main/askai/core/enums/rag_response.html b/docs/api/askai/main/askai/core/enums/rag_response.html deleted file mode 100644 index 0137bae1..00000000 --- a/docs/api/askai/main/askai/core/enums/rag_response.html +++ /dev/null @@ -1,688 +0,0 @@ - - - - - - - main.askai.core.enums.rag_response API documentation - - - - - - - - - -
    -
    -

    -main.askai.core.enums.rag_response

    - -

    @project: HsPyLib-AskAI -@package: askai.core.model - @file: rag_response.py -@created: Tue, 23 Apr 2024 - @author: Hugo Saporetti Junior - @site: https://github.com/yorevs/askai -@license: MIT - Please refer to https://opensource.org/licenses/MIT

    - -

    Copyright (c) 2024, HomeSetup

    -
    - - - - - -
     1#!/usr/bin/env python3
    - 2# -*- coding: utf-8 -*-
    - 3
    - 4"""
    - 5   @project: HsPyLib-AskAI
    - 6   @package: askai.core.model
    - 7      @file: rag_response.py
    - 8   @created: Tue, 23 Apr 2024
    - 9    @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior
    -10      @site: https://github.com/yorevs/askai
    -11   @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
    -12
    -13   Copyright (c) 2024, HomeSetup
    -14"""
    -15
    -16from hspylib.core.enums.enumeration import Enumeration
    -17
    -18import re
    -19
    -20
    -21class RagResponse(Enumeration):
    -22    """Keep track of the RAG model responses (classifications)."""
    -23
    -24    # fmt: off
    -25
    -26    GOOD        = 'Green'
    -27
    -28    MODERATE    = 'Yellow'
    -29
    -30    LOW         = 'Orange'
    -31
    -32    BAD         = 'Red'
    -33
    -34    # fmt: on
    -35
    -36    @classmethod
    -37    def matches(cls, output: str) -> re.Match:
    -38        return re.search(cls._re(), output.replace("\n", " "), re.IGNORECASE)
    -39
    -40    @classmethod
    -41    def _re(cls) -> str:
    -42        return rf"^\$?({'|'.join(cls.values())})[:,-]\s*(.+)"
    -43
    -44    @classmethod
    -45    def strip_code(cls, message: str) -> str:
    -46        """Strip the color code from the message"""
    -47        mat = cls.matches(message)
    -48        return str(mat.group(2)).strip() if mat else message.strip()
    -49
    -50    @classmethod
    -51    def of_status(cls, status: str) -> "RagResponse":
    -52        return cls.of_value(status.title())
    -53
    -54    @property
    -55    def is_bad(self) -> bool:
    -56        return self == self.BAD
    -57
    -58    @property
    -59    def is_moderate(self) -> bool:
    -60        return self == self.MODERATE
    -61
    -62    @property
    -63    def is_low(self) -> bool:
    -64        return self == self.LOW
    -65
    -66    @property
    -67    def is_good(self) -> bool:
    -68        return self == self.GOOD
    -69
    -70    def passed(self, threshold: 'RagResponse') -> bool:
    -71        """whether the response matches a 'PASS' classification."""
    -72        if isinstance(threshold, RagResponse):
    -73            idx_self, idx_threshold = None, None
    -74            for i, v in enumerate(RagResponse.values()):
    -75                if v == self.value:
    -76                    idx_self = i
    -77                if v == threshold.value:
    -78                    idx_threshold = i
    -79            return idx_self is not None and idx_threshold is not None and idx_self <= idx_threshold
    -80        return False
    -
    - - -
    -
    - -
    - - class - RagResponse(hspylib.core.enums.enumeration.Enumeration): - - - -
    - -
    22class RagResponse(Enumeration):
    -23    """Keep track of the RAG model responses (classifications)."""
    -24
    -25    # fmt: off
    -26
    -27    GOOD        = 'Green'
    -28
    -29    MODERATE    = 'Yellow'
    -30
    -31    LOW         = 'Orange'
    -32
    -33    BAD         = 'Red'
    -34
    -35    # fmt: on
    -36
    -37    @classmethod
    -38    def matches(cls, output: str) -> re.Match:
    -39        return re.search(cls._re(), output.replace("\n", " "), re.IGNORECASE)
    -40
    -41    @classmethod
    -42    def _re(cls) -> str:
    -43        return rf"^\$?({'|'.join(cls.values())})[:,-]\s*(.+)"
    -44
    -45    @classmethod
    -46    def strip_code(cls, message: str) -> str:
    -47        """Strip the color code from the message"""
    -48        mat = cls.matches(message)
    -49        return str(mat.group(2)).strip() if mat else message.strip()
    -50
    -51    @classmethod
    -52    def of_status(cls, status: str) -> "RagResponse":
    -53        return cls.of_value(status.title())
    -54
    -55    @property
    -56    def is_bad(self) -> bool:
    -57        return self == self.BAD
    -58
    -59    @property
    -60    def is_moderate(self) -> bool:
    -61        return self == self.MODERATE
    -62
    -63    @property
    -64    def is_low(self) -> bool:
    -65        return self == self.LOW
    -66
    -67    @property
    -68    def is_good(self) -> bool:
    -69        return self == self.GOOD
    -70
    -71    def passed(self, threshold: 'RagResponse') -> bool:
    -72        """whether the response matches a 'PASS' classification."""
    -73        if isinstance(threshold, RagResponse):
    -74            idx_self, idx_threshold = None, None
    -75            for i, v in enumerate(RagResponse.values()):
    -76                if v == self.value:
    -77                    idx_self = i
    -78                if v == threshold.value:
    -79                    idx_threshold = i
    -80            return idx_self is not None and idx_threshold is not None and idx_self <= idx_threshold
    -81        return False
    -
    - - -

    Keep track of the RAG model responses (classifications).

    -
    - - -
    -
    - GOOD = -GOOD - - -
    - - - - -
    -
    -
    - MODERATE = -MODERATE - - -
    - - - - -
    -
    -
    - LOW = -LOW - - -
    - - - - -
    -
    -
    - BAD = -BAD - - -
    - - - - -
    -
    - -
    -
    @classmethod
    - - def - matches(cls, output: str) -> re.Match: - - - -
    - -
    37    @classmethod
    -38    def matches(cls, output: str) -> re.Match:
    -39        return re.search(cls._re(), output.replace("\n", " "), re.IGNORECASE)
    -
    - - - - -
    -
    - -
    -
    @classmethod
    - - def - strip_code(cls, message: str) -> str: - - - -
    - -
    45    @classmethod
    -46    def strip_code(cls, message: str) -> str:
    -47        """Strip the color code from the message"""
    -48        mat = cls.matches(message)
    -49        return str(mat.group(2)).strip() if mat else message.strip()
    -
    - - -

    Strip the color code from the message

    -
    - - -
    -
    - -
    -
    @classmethod
    - - def - of_status(cls, status: str) -> RagResponse: - - - -
    - -
    51    @classmethod
    -52    def of_status(cls, status: str) -> "RagResponse":
    -53        return cls.of_value(status.title())
    -
    - - - - -
    -
    - -
    - is_bad: bool - - - -
    - -
    55    @property
    -56    def is_bad(self) -> bool:
    -57        return self == self.BAD
    -
    - - - - -
    -
    - -
    - is_moderate: bool - - - -
    - -
    59    @property
    -60    def is_moderate(self) -> bool:
    -61        return self == self.MODERATE
    -
    - - - - -
    -
    - -
    - is_low: bool - - - -
    - -
    63    @property
    -64    def is_low(self) -> bool:
    -65        return self == self.LOW
    -
    - - - - -
    -
    - -
    - is_good: bool - - - -
    - -
    67    @property
    -68    def is_good(self) -> bool:
    -69        return self == self.GOOD
    -
    - - - - -
    -
    - -
    - - def - passed(self, threshold: RagResponse) -> bool: - - - -
    - -
    71    def passed(self, threshold: 'RagResponse') -> bool:
    -72        """whether the response matches a 'PASS' classification."""
    -73        if isinstance(threshold, RagResponse):
    -74            idx_self, idx_threshold = None, None
    -75            for i, v in enumerate(RagResponse.values()):
    -76                if v == self.value:
    -77                    idx_self = i
    -78                if v == threshold.value:
    -79                    idx_threshold = i
    -80            return idx_self is not None and idx_threshold is not None and idx_self <= idx_threshold
    -81        return False
    -
    - - -

    whether the response matches a 'PASS' classification.

    -
    - - -
    -
    -
    Inherited Members
    -
    -
    hspylib.core.enums.enumeration.Enumeration
    -
    names
    -
    values
    -
    value_of
    -
    of_value
    -
    compose
    -
    key
    - -
    -
    enum.Enum
    -
    name
    -
    value
    - -
    -
    -
    -
    -
    - - \ No newline at end of file diff --git a/docs/api/askai/main/askai/core/enums/router_mode.html b/docs/api/askai/main/askai/core/enums/router_mode.html index 9f7dd53f..20af379b 100644 --- a/docs/api/askai/main/askai/core/enums/router_mode.html +++ b/docs/api/askai/main/askai/core/enums/router_mode.html @@ -3,7 +3,7 @@ - + main.askai.core.enums.router_mode API documentation @@ -115,62 +115,77 @@

    13 Copyright (c) 2024, HomeSetup 14""" 15from askai.core.askai_configs import configs -16from askai.core.features.router.ai_processor import AIProcessor -17from askai.core.features.router.procs.qna import qna -18from askai.core.features.router.procs.qstring import qstring -19from askai.core.features.router.procs.rag import rag -20from askai.core.features.router.procs.task_splitter import splitter +16from askai.core.features.processors.ai_processor import AIProcessor +17from askai.core.features.processors.qna import qna +18from askai.core.features.processors.qstring import qstring +19from askai.core.features.processors.rag import rag +20from askai.core.features.processors.task_splitter import splitter 21from hspylib.core.enums.enumeration import Enumeration 22from typing import Optional 23 24 25class RouterMode(Enumeration): -26 """The available router modes used to provide an answer to the user.""" -27 -28 # fmt: on +26 """Enumeration of available router modes used to determine the type of response provided to the user. This class +27 defines the different modes that the router can operate in, each affecting how answers are generated and delivered. +28 """ 29 -30 TASK_SPLIT = 'Task Splitter', splitter +30 # fmt: on 31 -32 QNA = 'Questions and Answers', qna +32 TASK_SPLIT = "Task Splitter", splitter 33 -34 QSTRING = 'Non-Interactive', qstring +34 QNA = "Questions and Answers", qna 35 -36 RAG = 'Retrieval-Augmented Generation', rag +36 QSTRING = "Non-Interactive", qstring 37 -38 # fmt: off +38 RAG = "Retrieval-Augmented Generation", rag 39 -40 @classmethod -41 def modes(cls) -> list[str]: -42 """Return a list containing al available agent modes.""" -43 return RouterMode.names() -44 -45 @staticmethod -46 def default() -> 'RouterMode': -47 """Return the default routing mode.""" -48 return RouterMode.TASK_SPLIT if configs.is_interactive else RouterMode.QSTRING -49 -50 @classmethod -51 def of_name(cls, name: str) -> 'RouterMode': -52 return cls[name] if name.casefold() != 'default' else cls.default() -53 -54 def __str__(self): -55 return self.value[0] -56 -57 @property -58 def name(self) -> str: -59 return self.value[0] -60 -61 @property -62 def processor(self) -> AIProcessor: -63 return self.value[1] +40 # fmt: off +41 +42 @classmethod +43 def modes(cls) -> list[str]: +44 """Return a list containing all available agent modes. +45 :return: A list of available agent modes as strings. +46 """ +47 return RouterMode.names() +48 +49 @staticmethod +50 def default() -> 'RouterMode': +51 """Return the default routing mode. +52 :return: The default RouterMode instance. +53 """ +54 return RouterMode.TASK_SPLIT if configs.is_interactive else RouterMode.QSTRING +55 +56 @classmethod +57 def of_name(cls, name: str) -> 'RouterMode': +58 """Retrieve the RouterMode instance corresponding to the given name. +59 :param name: The name of the router mode to retrieve. +60 :return: The RouterMode instance that matches the given name. +61 :raises ValueError: If no matching RouterMode is found. +62 """ +63 return cls[name] if name.casefold() != 'default' else cls.default() 64 -65 @property -66 def is_default(self) -> bool: -67 return self == RouterMode.default() -68 -69 def process(self, question: str, **kwargs) -> Optional[str]: -70 """Invoke the processor associated with the mode.""" -71 return self.processor.process(question, **kwargs) +65 def __str__(self): +66 return self.value[0] +67 +68 @property +69 def name(self) -> str: +70 return self.value[0] +71 +72 @property +73 def processor(self) -> AIProcessor: +74 return self.value[1] +75 +76 @property +77 def is_default(self) -> bool: +78 return self == RouterMode.default() +79 +80 def process(self, question: str, **kwargs) -> Optional[str]: +81 """Invoke the processor associated with the current mode to handle the given question. +82 :param question: The question to be processed. +83 :param kwargs: Additional arguments to be passed to the processor. +84 :return: The processed response as a string, or None if no response is generated. +85 """ +86 return self.processor.process(question, **kwargs)

    @@ -187,56 +202,72 @@

    26class RouterMode(Enumeration):
    -27    """The available router modes used to provide an answer to the user."""
    -28
    -29    # fmt: on
    +27    """Enumeration of available router modes used to determine the type of response provided to the user. This class
    +28    defines the different modes that the router can operate in, each affecting how answers are generated and delivered.
    +29    """
     30
    -31    TASK_SPLIT          = 'Task Splitter', splitter
    +31    # fmt: on
     32
    -33    QNA                 = 'Questions and Answers', qna
    +33    TASK_SPLIT = "Task Splitter", splitter
     34
    -35    QSTRING             = 'Non-Interactive', qstring
    +35    QNA = "Questions and Answers", qna
     36
    -37    RAG                 = 'Retrieval-Augmented Generation', rag
    +37    QSTRING = "Non-Interactive", qstring
     38
    -39    # fmt: off
    +39    RAG = "Retrieval-Augmented Generation", rag
     40
    -41    @classmethod
    -42    def modes(cls) -> list[str]:
    -43        """Return a list containing al available agent modes."""
    -44        return RouterMode.names()
    -45
    -46    @staticmethod
    -47    def default() -> 'RouterMode':
    -48        """Return the default routing mode."""
    -49        return RouterMode.TASK_SPLIT if configs.is_interactive else RouterMode.QSTRING
    -50
    -51    @classmethod
    -52    def of_name(cls, name: str) -> 'RouterMode':
    -53        return cls[name] if name.casefold() != 'default' else cls.default()
    -54
    -55    def __str__(self):
    -56        return self.value[0]
    -57
    -58    @property
    -59    def name(self) -> str:
    -60        return self.value[0]
    -61
    -62    @property
    -63    def processor(self) -> AIProcessor:
    -64        return self.value[1]
    +41    # fmt: off
    +42
    +43    @classmethod
    +44    def modes(cls) -> list[str]:
    +45        """Return a list containing all available agent modes.
    +46        :return: A list of available agent modes as strings.
    +47        """
    +48        return RouterMode.names()
    +49
    +50    @staticmethod
    +51    def default() -> 'RouterMode':
    +52        """Return the default routing mode.
    +53        :return: The default RouterMode instance.
    +54        """
    +55        return RouterMode.TASK_SPLIT if configs.is_interactive else RouterMode.QSTRING
    +56
    +57    @classmethod
    +58    def of_name(cls, name: str) -> 'RouterMode':
    +59        """Retrieve the RouterMode instance corresponding to the given name.
    +60        :param name: The name of the router mode to retrieve.
    +61        :return: The RouterMode instance that matches the given name.
    +62        :raises ValueError: If no matching RouterMode is found.
    +63        """
    +64        return cls[name] if name.casefold() != 'default' else cls.default()
     65
    -66    @property
    -67    def is_default(self) -> bool:
    -68        return self == RouterMode.default()
    -69
    -70    def process(self, question: str, **kwargs) -> Optional[str]:
    -71        """Invoke the processor associated with the mode."""
    -72        return self.processor.process(question, **kwargs)
    +66    def __str__(self):
    +67        return self.value[0]
    +68
    +69    @property
    +70    def name(self) -> str:
    +71        return self.value[0]
    +72
    +73    @property
    +74    def processor(self) -> AIProcessor:
    +75        return self.value[1]
    +76
    +77    @property
    +78    def is_default(self) -> bool:
    +79        return self == RouterMode.default()
    +80
    +81    def process(self, question: str, **kwargs) -> Optional[str]:
    +82        """Invoke the processor associated with the current mode to handle the given question.
    +83        :param question: The question to be processed.
    +84        :param kwargs: Additional arguments to be passed to the processor.
    +85        :return: The processed response as a string, or None if no response is generated.
    +86        """
    +87        return self.processor.process(question, **kwargs)
     
    -

    The available router modes used to provide an answer to the user.

    +

    Enumeration of available router modes used to determine the type of response provided to the user. This class +defines the different modes that the router can operate in, each affecting how answers are generated and delivered.

    @@ -300,14 +331,22 @@

    -
    41    @classmethod
    -42    def modes(cls) -> list[str]:
    -43        """Return a list containing al available agent modes."""
    -44        return RouterMode.names()
    +            
    43    @classmethod
    +44    def modes(cls) -> list[str]:
    +45        """Return a list containing all available agent modes.
    +46        :return: A list of available agent modes as strings.
    +47        """
    +48        return RouterMode.names()
     
    -

    Return a list containing al available agent modes.

    +

    Return a list containing all available agent modes.

    + +
    Returns
    + +
    +

    A list of available agent modes as strings.

    +
    @@ -324,14 +363,22 @@

    -
    46    @staticmethod
    -47    def default() -> 'RouterMode':
    -48        """Return the default routing mode."""
    -49        return RouterMode.TASK_SPLIT if configs.is_interactive else RouterMode.QSTRING
    +            
    50    @staticmethod
    +51    def default() -> 'RouterMode':
    +52        """Return the default routing mode.
    +53        :return: The default RouterMode instance.
    +54        """
    +55        return RouterMode.TASK_SPLIT if configs.is_interactive else RouterMode.QSTRING
     

    Return the default routing mode.

    + +
    Returns
    + +
    +

    The default RouterMode instance.

    +
    @@ -348,13 +395,38 @@

    -
    51    @classmethod
    -52    def of_name(cls, name: str) -> 'RouterMode':
    -53        return cls[name] if name.casefold() != 'default' else cls.default()
    +            
    57    @classmethod
    +58    def of_name(cls, name: str) -> 'RouterMode':
    +59        """Retrieve the RouterMode instance corresponding to the given name.
    +60        :param name: The name of the router mode to retrieve.
    +61        :return: The RouterMode instance that matches the given name.
    +62        :raises ValueError: If no matching RouterMode is found.
    +63        """
    +64        return cls[name] if name.casefold() != 'default' else cls.default()
     
    - +

    Retrieve the RouterMode instance corresponding to the given name.

    + +
    Parameters
    + +
      +
    • name: The name of the router mode to retrieve.
    • +
    + +
    Returns
    + +
    +

    The RouterMode instance that matches the given name.

    +
    + +
    Raises
    + +
      +
    • ValueError: If no matching RouterMode is found.
    • +
    +
    +
    @@ -366,9 +438,9 @@

    -
    58    @property
    -59    def name(self) -> str:
    -60        return self.value[0]
    +            
    69    @property
    +70    def name(self) -> str:
    +71        return self.value[0]
     
    @@ -380,15 +452,15 @@

    - processor: askai.core.features.router.ai_processor.AIProcessor + processor: askai.core.features.processors.ai_processor.AIProcessor
    -
    62    @property
    -63    def processor(self) -> AIProcessor:
    -64        return self.value[1]
    +            
    73    @property
    +74    def processor(self) -> AIProcessor:
    +75        return self.value[1]
     
    @@ -404,9 +476,9 @@

    -
    66    @property
    -67    def is_default(self) -> bool:
    -68        return self == RouterMode.default()
    +            
    77    @property
    +78    def is_default(self) -> bool:
    +79        return self == RouterMode.default()
     
    @@ -424,13 +496,30 @@

    -
    70    def process(self, question: str, **kwargs) -> Optional[str]:
    -71        """Invoke the processor associated with the mode."""
    -72        return self.processor.process(question, **kwargs)
    +            
    81    def process(self, question: str, **kwargs) -> Optional[str]:
    +82        """Invoke the processor associated with the current mode to handle the given question.
    +83        :param question: The question to be processed.
    +84        :param kwargs: Additional arguments to be passed to the processor.
    +85        :return: The processed response as a string, or None if no response is generated.
    +86        """
    +87        return self.processor.process(question, **kwargs)
     
    -

    Invoke the processor associated with the mode.

    +

    Invoke the processor associated with the current mode to handle the given question.

    + +
    Parameters
    + +
      +
    • question: The question to be processed.
    • +
    • kwargs: Additional arguments to be passed to the processor.
    • +
    + +
    Returns
    + +
    +

    The processed response as a string, or None if no response is generated.

    +
    diff --git a/docs/api/askai/main/askai/core/enums/routing_model.html b/docs/api/askai/main/askai/core/enums/routing_model.html index e498c60a..be67f36f 100644 --- a/docs/api/askai/main/askai/core/enums/routing_model.html +++ b/docs/api/askai/main/askai/core/enums/routing_model.html @@ -3,7 +3,7 @@ - + main.askai.core.enums.routing_model API documentation @@ -140,95 +140,112 @@

    20 21 22class RoutingModel(Enumeration): - 23 """The model used to provide the final answer to the user.""" - 24 - 25 # fmt: on - 26 - 27 NEUTRAL = ( - 28 "ASK_000", "Select this model when no other model fits.") + 23 """Enumeration representing the model used to provide the final answer to the user. + 24 This class defines the different models that can be used in the routing process to generate and deliver the + 25 final response. + 26 """ + 27 + 28 # fmt: on 29 - 30 # ASSISTIVE_TECH_HELPER - 31 ASSISTIVE_TECH_HELPER = "ASK_001", ( - 32 "Select this model when you receive requests for **assistive technologies**, such as **speech-to-text**") - 33 - 34 # TERMINAL_COMMAND - 35 TERMINAL_COMMAND = "ASK_002", ( - 36 "Select this model for executing shell commands, managing terminal operations, listing folder contents, " - 37 "reading files, and manipulating system resources directly through the command line interface.") - 38 - 39 # CONTENT_MASTER - 40 CONTENT_MASTER = "ASK_003", ( - 41 "Select this model exclusively for creating, generating, and saving any type of content, including text, code, " - 42 "images, and others. This model should always be used when the task involves generating or saving content.") - 43 - 44 # TEXT_ANALYZER - 45 TEXT_ANALYZER = "ASK_004", ( - 46 "Select this model for extracting and processing information from within individual documents and files " - 47 "located at the user file system, focusing on text or content analysis within a single file.") + 30 NEUTRAL = ("ASK_000", "Select this model when no other model fits.") + 31 + 32 # ASSISTIVE_TECH_HELPER + 33 ASSISTIVE_TECH_HELPER = "ASK_001", ( + 34 "Select this model when you receive requests for **assistive technologies**, such as **speech-to-text**" + 35 ) + 36 + 37 # TERMINAL_COMMAND + 38 TERMINAL_COMMAND = "ASK_002", ( + 39 "Select this model for executing shell commands, managing terminal operations, listing folder contents, " + 40 "reading files, and manipulating system resources directly through the command line interface." + 41 ) + 42 + 43 # CONTENT_MASTER + 44 CONTENT_MASTER = "ASK_003", ( + 45 "Select this model exclusively for creating, generating, and saving any type of content, including text, code, " + 46 "images, and others. This model should always be used when the task involves generating or saving content." + 47 ) 48 - 49 # DATA_ANALYSIS - 50 DATA_ANALYSIS = "ASK_005", ( - 51 "Select this model for analyzing datasets, performing statistical analysis, and generating reports. Media " - 52 "Management and Playback: Select this model for organizing, categorizing, and playing multimedia content.") - 53 - 54 # CHAT_MASTER - 55 CHAT_MASTER = "ASK_006", ( - 56 "Select this model for providing conversational responses or engaging in general chat.") - 57 - 58 # MEDIA_MANAGEMENT_AND_PLAYBACK - 59 MEDIA_MANAGEMENT_AND_PLAYBACK = "ASK_007", ( - 60 "Select this model for organizing, categorizing, and playing multimedia content.") - 61 - 62 # IMAGE_PROCESSOR - 63 IMAGE_PROCESSOR = "ASK_008", ( - 64 "Select this model for image captioning, face recognition, and visual analysis tasks.") - 65 - 66 # SUMMARIZE_AND_QUERY - 67 SUMMARIZE_AND_QUERY = "ASK_009", ( - 68 "Select this model upon receiving an explicit user request for 'summarization of files and folders'.") - 69 - 70 # WEB_FETCH - 71 WEB_FETCH = "ASK_010", ( - 72 "Select this model for retrieving information about current events from the web.") + 49 # TEXT_ANALYZER + 50 TEXT_ANALYZER = "ASK_004", ( + 51 "Select this model for extracting and processing information from within individual documents and files " + 52 "located at the user file system, focusing on text or content analysis within a single file." + 53 ) + 54 + 55 # DATA_ANALYSIS + 56 DATA_ANALYSIS = "ASK_005", ( + 57 "Select this model for analyzing datasets, performing statistical analysis, and generating reports. Media " + 58 "Management and Playback: Select this model for organizing, categorizing, and playing multimedia content." + 59 ) + 60 + 61 # CHAT_MASTER + 62 CHAT_MASTER = "ASK_006", ("Select this model for providing conversational responses or engaging in general chat.") + 63 + 64 # MEDIA_MANAGEMENT_AND_PLAYBACK + 65 MEDIA_MANAGEMENT_AND_PLAYBACK = "ASK_007", ( + 66 "Select this model for organizing, categorizing, and playing multimedia content." + 67 ) + 68 + 69 # IMAGE_PROCESSOR + 70 IMAGE_PROCESSOR = "ASK_008", ( + 71 "Select this model for image captioning, face recognition, and visual analysis tasks." + 72 ) 73 - 74 # WELL_KNOWN - 75 FINAL_ANSWER = "ASK_011", ( - 76 "Select this model to respond to well-known queries, where you database is enough to " - 77 "provide a clear and accurate answer.") + 74 # SUMMARIZE_AND_QUERY + 75 SUMMARIZE_AND_QUERY = "ASK_009", ( + 76 "Select this model upon receiving an explicit user request for 'summarization of files and folders'." + 77 ) 78 - 79 # REFINE_ANSWER - 80 REFINER = "ASK_012", ( - 81 "Select this model to respond to improve the AI response. This should be only selected upon a clear request.") - 82 - 83 @classmethod - 84 def of_model(cls, model_id: str) -> 'RoutingModel': - 85 """Returning teh matching model ID instance.""" - 86 for v in cls.values(): - 87 if v[0] == model_id: - 88 return cls.of_value(v) - 89 - 90 @classmethod - 91 def enlist(cls, separator: str = os.linesep) -> str: - 92 """Return a list of selectable models.""" - 93 model_list: str = separator.join(f"{v[0]}: {v[1]}" for v in cls.values()) - 94 log.debug("Routing Models: %s", model_list) - 95 return model_list - 96 - 97 def __str__(self): - 98 return f"{self.name}::{self.model}" - 99 -100 def __repr__(self): -101 return str(self) -102 -103 @property -104 def model(self) -> str: -105 return self.value[0] -106 -107 @property -108 def description(self) -> str: -109 return self.value[1] -110 -111 # fmt: on + 79 # WEB_FETCH + 80 WEB_FETCH = "ASK_010", ("Select this model for retrieving information about current events from the web.") + 81 + 82 # WELL_KNOWN + 83 FINAL_ANSWER = "ASK_011", ( + 84 "Select this model to respond to well-known queries, where you database is enough to " + 85 "provide a clear and accurate answer." + 86 ) + 87 + 88 # REFINE_ANSWER + 89 REFINER = "ASK_012", ( + 90 "Select this model to respond to improve the AI response. This should be only selected upon a clear request." + 91 ) + 92 + 93 # fmt: on + 94 + 95 @classmethod + 96 def of_model(cls, model_id: str) -> "RoutingModel": + 97 """Return the RoutingModel instance that matches the given model ID. + 98 :param model_id: The ID of the model to retrieve. + 99 :return: The RoutingModel instance corresponding to the specified model ID. +100 :raises ValueError: If no matching RoutingModel is found. +101 """ +102 for v in cls.values(): +103 if v[0] == model_id: +104 return cls.of_value(v) +105 +106 @classmethod +107 def enlist(cls, separator: str = os.linesep) -> str: +108 """Return a list of selectable models as a formatted string. +109 :param separator: The separator used to delimit the models in the list (default is the system's line separator). +110 :return: A string containing the list of selectable models, separated by the specified separator. +111 """ +112 model_list: str = separator.join(f"{v[0]}: {v[1]}" for v in cls.values()) +113 log.debug("Routing Models: %s", model_list) +114 return model_list +115 +116 def __str__(self): +117 return f"{self.name}::{self.model}" +118 +119 def __repr__(self): +120 return str(self) +121 +122 @property +123 def model(self) -> str: +124 return self.value[0] +125 +126 @property +127 def description(self) -> str: +128 return self.value[1]

    @@ -245,99 +262,118 @@

     23class RoutingModel(Enumeration):
    - 24    """The model used to provide the final answer to the user."""
    - 25
    - 26    # fmt: on
    - 27
    - 28    NEUTRAL = (
    - 29        "ASK_000", "Select this model when no other model fits.")
    + 24    """Enumeration representing the model used to provide the final answer to the user.
    + 25    This class defines the different models that can be used in the routing process to generate and deliver the
    + 26    final response.
    + 27    """
    + 28
    + 29    # fmt: on
      30
    - 31    # ASSISTIVE_TECH_HELPER
    - 32    ASSISTIVE_TECH_HELPER = "ASK_001", (
    - 33        "Select this model when you receive requests for **assistive technologies**, such as **speech-to-text**")
    - 34
    - 35    # TERMINAL_COMMAND
    - 36    TERMINAL_COMMAND = "ASK_002", (
    - 37        "Select this model for executing shell commands, managing terminal operations, listing folder contents, "
    - 38        "reading files, and manipulating system resources directly through the command line interface.")
    - 39
    - 40    # CONTENT_MASTER
    - 41    CONTENT_MASTER = "ASK_003", (
    - 42        "Select this model exclusively for creating, generating, and saving any type of content, including text, code, "
    - 43        "images, and others. This model should always be used when the task involves generating or saving content.")
    - 44
    - 45    # TEXT_ANALYZER
    - 46    TEXT_ANALYZER = "ASK_004", (
    - 47        "Select this model for extracting and processing information from within individual documents and files "
    - 48        "located at the user file system, focusing on text or content analysis within a single file.")
    + 31    NEUTRAL = ("ASK_000", "Select this model when no other model fits.")
    + 32
    + 33    # ASSISTIVE_TECH_HELPER
    + 34    ASSISTIVE_TECH_HELPER = "ASK_001", (
    + 35        "Select this model when you receive requests for **assistive technologies**, such as **speech-to-text**"
    + 36    )
    + 37
    + 38    # TERMINAL_COMMAND
    + 39    TERMINAL_COMMAND = "ASK_002", (
    + 40        "Select this model for executing shell commands, managing terminal operations, listing folder contents, "
    + 41        "reading files, and manipulating system resources directly through the command line interface."
    + 42    )
    + 43
    + 44    # CONTENT_MASTER
    + 45    CONTENT_MASTER = "ASK_003", (
    + 46        "Select this model exclusively for creating, generating, and saving any type of content, including text, code, "
    + 47        "images, and others. This model should always be used when the task involves generating or saving content."
    + 48    )
      49
    - 50    # DATA_ANALYSIS
    - 51    DATA_ANALYSIS = "ASK_005", (
    - 52        "Select this model for analyzing datasets, performing statistical analysis, and generating reports. Media "
    - 53        "Management and Playback: Select this model for organizing, categorizing, and playing multimedia content.")
    - 54
    - 55    # CHAT_MASTER
    - 56    CHAT_MASTER = "ASK_006", (
    - 57        "Select this model for providing conversational responses or engaging in general chat.")
    - 58
    - 59    # MEDIA_MANAGEMENT_AND_PLAYBACK
    - 60    MEDIA_MANAGEMENT_AND_PLAYBACK = "ASK_007", (
    - 61        "Select this model for organizing, categorizing, and playing multimedia content.")
    - 62
    - 63    # IMAGE_PROCESSOR
    - 64    IMAGE_PROCESSOR = "ASK_008", (
    - 65        "Select this model for image captioning, face recognition, and visual analysis tasks.")
    - 66
    - 67    # SUMMARIZE_AND_QUERY
    - 68    SUMMARIZE_AND_QUERY = "ASK_009", (
    - 69        "Select this model upon receiving an explicit user request for 'summarization of files and folders'.")
    - 70
    - 71    # WEB_FETCH
    - 72    WEB_FETCH = "ASK_010", (
    - 73        "Select this model for retrieving information about current events from the web.")
    + 50    # TEXT_ANALYZER
    + 51    TEXT_ANALYZER = "ASK_004", (
    + 52        "Select this model for extracting and processing information from within individual documents and files "
    + 53        "located at the user file system, focusing on text or content analysis within a single file."
    + 54    )
    + 55
    + 56    # DATA_ANALYSIS
    + 57    DATA_ANALYSIS = "ASK_005", (
    + 58        "Select this model for analyzing datasets, performing statistical analysis, and generating reports. Media "
    + 59        "Management and Playback: Select this model for organizing, categorizing, and playing multimedia content."
    + 60    )
    + 61
    + 62    # CHAT_MASTER
    + 63    CHAT_MASTER = "ASK_006", ("Select this model for providing conversational responses or engaging in general chat.")
    + 64
    + 65    # MEDIA_MANAGEMENT_AND_PLAYBACK
    + 66    MEDIA_MANAGEMENT_AND_PLAYBACK = "ASK_007", (
    + 67        "Select this model for organizing, categorizing, and playing multimedia content."
    + 68    )
    + 69
    + 70    # IMAGE_PROCESSOR
    + 71    IMAGE_PROCESSOR = "ASK_008", (
    + 72        "Select this model for image captioning, face recognition, and visual analysis tasks."
    + 73    )
      74
    - 75    # WELL_KNOWN
    - 76    FINAL_ANSWER = "ASK_011", (
    - 77        "Select this model to respond to well-known queries, where you database is enough to "
    - 78        "provide a clear and accurate answer.")
    + 75    # SUMMARIZE_AND_QUERY
    + 76    SUMMARIZE_AND_QUERY = "ASK_009", (
    + 77        "Select this model upon receiving an explicit user request for 'summarization of files and folders'."
    + 78    )
      79
    - 80    # REFINE_ANSWER
    - 81    REFINER = "ASK_012", (
    - 82        "Select this model to respond to improve the AI response. This should be only selected upon a clear request.")
    - 83
    - 84    @classmethod
    - 85    def of_model(cls, model_id: str) -> 'RoutingModel':
    - 86        """Returning teh matching model ID instance."""
    - 87        for v in cls.values():
    - 88            if v[0] == model_id:
    - 89                return cls.of_value(v)
    - 90
    - 91    @classmethod
    - 92    def enlist(cls, separator: str = os.linesep) -> str:
    - 93        """Return a list of selectable models."""
    - 94        model_list: str = separator.join(f"{v[0]}: {v[1]}" for v in cls.values())
    - 95        log.debug("Routing Models: %s", model_list)
    - 96        return model_list
    - 97
    - 98    def __str__(self):
    - 99        return f"{self.name}::{self.model}"
    -100
    -101    def __repr__(self):
    -102        return str(self)
    -103
    -104    @property
    -105    def model(self) -> str:
    -106        return self.value[0]
    -107
    -108    @property
    -109    def description(self) -> str:
    -110        return self.value[1]
    -111
    -112    # fmt: on
    + 80    # WEB_FETCH
    + 81    WEB_FETCH = "ASK_010", ("Select this model for retrieving information about current events from the web.")
    + 82
    + 83    # WELL_KNOWN
    + 84    FINAL_ANSWER = "ASK_011", (
    + 85        "Select this model to respond to well-known queries, where you database is enough to "
    + 86        "provide a clear and accurate answer."
    + 87    )
    + 88
    + 89    # REFINE_ANSWER
    + 90    REFINER = "ASK_012", (
    + 91        "Select this model to respond to improve the AI response. This should be only selected upon a clear request."
    + 92    )
    + 93
    + 94    # fmt: on
    + 95
    + 96    @classmethod
    + 97    def of_model(cls, model_id: str) -> "RoutingModel":
    + 98        """Return the RoutingModel instance that matches the given model ID.
    + 99        :param model_id: The ID of the model to retrieve.
    +100        :return: The RoutingModel instance corresponding to the specified model ID.
    +101        :raises ValueError: If no matching RoutingModel is found.
    +102        """
    +103        for v in cls.values():
    +104            if v[0] == model_id:
    +105                return cls.of_value(v)
    +106
    +107    @classmethod
    +108    def enlist(cls, separator: str = os.linesep) -> str:
    +109        """Return a list of selectable models as a formatted string.
    +110        :param separator: The separator used to delimit the models in the list (default is the system's line separator).
    +111        :return: A string containing the list of selectable models, separated by the specified separator.
    +112        """
    +113        model_list: str = separator.join(f"{v[0]}: {v[1]}" for v in cls.values())
    +114        log.debug("Routing Models: %s", model_list)
    +115        return model_list
    +116
    +117    def __str__(self):
    +118        return f"{self.name}::{self.model}"
    +119
    +120    def __repr__(self):
    +121        return str(self)
    +122
    +123    @property
    +124    def model(self) -> str:
    +125        return self.value[0]
    +126
    +127    @property
    +128    def description(self) -> str:
    +129        return self.value[1]
     
    -

    The model used to provide the final answer to the user.

    +

    Enumeration representing the model used to provide the final answer to the user. +This class defines the different models that can be used in the routing process to generate and deliver the +final response.

    @@ -509,16 +545,38 @@

    -
    84    @classmethod
    -85    def of_model(cls, model_id: str) -> 'RoutingModel':
    -86        """Returning teh matching model ID instance."""
    -87        for v in cls.values():
    -88            if v[0] == model_id:
    -89                return cls.of_value(v)
    +            
     96    @classmethod
    + 97    def of_model(cls, model_id: str) -> "RoutingModel":
    + 98        """Return the RoutingModel instance that matches the given model ID.
    + 99        :param model_id: The ID of the model to retrieve.
    +100        :return: The RoutingModel instance corresponding to the specified model ID.
    +101        :raises ValueError: If no matching RoutingModel is found.
    +102        """
    +103        for v in cls.values():
    +104            if v[0] == model_id:
    +105                return cls.of_value(v)
     
    -

    Returning teh matching model ID instance.

    +

    Return the RoutingModel instance that matches the given model ID.

    + +
    Parameters
    + +
      +
    • model_id: The ID of the model to retrieve.
    • +
    + +
    Returns
    + +
    +

    The RoutingModel instance corresponding to the specified model ID.

    +
    + +
    Raises
    + +
      +
    • ValueError: If no matching RoutingModel is found.
    • +
    @@ -535,16 +593,31 @@

    -
    91    @classmethod
    -92    def enlist(cls, separator: str = os.linesep) -> str:
    -93        """Return a list of selectable models."""
    -94        model_list: str = separator.join(f"{v[0]}: {v[1]}" for v in cls.values())
    -95        log.debug("Routing Models: %s", model_list)
    -96        return model_list
    +            
    107    @classmethod
    +108    def enlist(cls, separator: str = os.linesep) -> str:
    +109        """Return a list of selectable models as a formatted string.
    +110        :param separator: The separator used to delimit the models in the list (default is the system's line separator).
    +111        :return: A string containing the list of selectable models, separated by the specified separator.
    +112        """
    +113        model_list: str = separator.join(f"{v[0]}: {v[1]}" for v in cls.values())
    +114        log.debug("Routing Models: %s", model_list)
    +115        return model_list
     
    -

    Return a list of selectable models.

    +

    Return a list of selectable models as a formatted string.

    + +
    Parameters
    + +
      +
    • separator: The separator used to delimit the models in the list (default is the system's line separator).
    • +
    + +
    Returns
    + +
    +

    A string containing the list of selectable models, separated by the specified separator.

    +
    @@ -558,9 +631,9 @@

    -
    104    @property
    -105    def model(self) -> str:
    -106        return self.value[0]
    +            
    123    @property
    +124    def model(self) -> str:
    +125        return self.value[0]
     
    @@ -576,9 +649,9 @@

    -
    108    @property
    -109    def description(self) -> str:
    -110        return self.value[1]
    +            
    127    @property
    +128    def description(self) -> str:
    +129        return self.value[1]
     
    diff --git a/docs/api/askai/main/askai/core/features/router/tools/vision.html b/docs/api/askai/main/askai/core/enums/verbosity.html similarity index 64% rename from docs/api/askai/main/askai/core/features/router/tools/vision.html rename to docs/api/askai/main/askai/core/enums/verbosity.html index c07b8486..9ea88edf 100644 --- a/docs/api/askai/main/askai/core/features/router/tools/vision.html +++ b/docs/api/askai/main/askai/core/enums/verbosity.html @@ -3,8 +3,8 @@ - - main.askai.core.features.router.tools.vision API documentation + + main.askai.core.enums.verbosity API documentation @@ -16,11 +16,11 @@

    @@ -47,109 +71,251 @@

    API Documentation

    -main.askai.core.features.router.tools.vision

    +main.askai.core.enums.verbosity

    - - - - -
     1from askai.core.askai_events import events
    - 2from askai.core.askai_messages import msg
    - 3from askai.core.features.validation.accuracy import resolve_x_refs
    - 4from askai.core.support.shared_instances import shared
    - 5from hspylib.core.config.path_object import PathObject
    - 6from PIL import Image
    - 7from transformers import BlipForConditionalGeneration, BlipProcessor
    - 8from typing import Optional
    - 9
    -10import torch
    -11
    -12
    -13def image_captioner(path_name: str) -> Optional[str]:
    -14    """This tool is used to describe an image."""
    -15    caption: str | None = None
    -16    posix_path: PathObject = PathObject.of(path_name)
    -17    if not posix_path.exists:
    -18        # Attempt to resolve cross-references
    -19        if history := str(shared.context.flat("HISTORY") or ""):
    -20            if (x_referenced := resolve_x_refs(path_name, history)) and x_referenced != shared.UNCERTAIN_ID:
    -21                x_ref_path: PathObject = PathObject.of(x_referenced)
    -22                posix_path: PathObject = x_ref_path if x_ref_path.exists else posix_path
    -23
    -24    if posix_path.exists:
    -25        events.reply.emit(message=msg.describe_image(str(posix_path)))
    -26        # specify model to be used
    -27        hf_model = "Salesforce/blip-image-captioning-base"
    -28        # use GPU if it's available
    -29        device = "cuda" if torch.cuda.is_available() else "cpu"
    -30        # preprocessor will prepare images for the model
    -31        processor = BlipProcessor.from_pretrained(hf_model)
    -32        # then we initialize the model itself
    -33        model = BlipForConditionalGeneration.from_pretrained(hf_model).to(device)
    -34        # download the image and convert to PIL object
    -35        image = Image.open(str(posix_path)).convert("RGB")
    -36        inputs = processor(image, return_tensors="pt").to(device)
    -37        # generate the caption
    -38        out = model.generate(**inputs, max_new_tokens=20)
    -39        # get the caption
    -40        caption = processor.decode(out[0], skip_special_tokens=True)
    -41        caption = caption.title() if caption else 'I dont know'
    -42
    -43    return caption
    +                        
    +
    +                        
    +
    +                        
     1from hspylib.core.enums.enumeration import Enumeration
    + 2
    + 3
    + 4class Verbosity(Enumeration):
    + 5    """Represents different verbosity levels for logging or output.
    + 6    Attributes:
    + 7        MINIMUM (int): The minimum verbosity level.
    + 8        LOW (int): A low verbosity level.
    + 9        NORMAL (int): The normal verbosity level.
    +10        DETAILED (int): A detailed verbosity level.
    +11        FULL (int): The full verbosity level.
    +12    """
    +13
    +14    # fmt: off
    +15    MINIMUM     = 1
    +16    LOW         = 2
    +17    NORMAL      = 3
    +18    DETAILED    = 4
    +19    FULL        = 5
    +20    # fmt: on
    +21
    +22    @property
    +23    def val(self) -> int:
    +24        """Gets the integer value of the verbosity level.
    +25        :return: The integer representation of the verbosity level.
    +26        """
    +27        return int(self.value)
    +28
    +29    def match(self, level: "Verbosity") -> bool:
    +30        """Checks if the current verbosity level is less than or equal to the given level.
    +31        :param level: The verbosity level to compare against.
    +32        :return: True if the current level is less than or equal to the given level, otherwise False.
    +33        """
    +34        return self.val <= level.val
     
    -
    - +
    + +
    + + class + Verbosity(hspylib.core.enums.enumeration.Enumeration): + + + +
    + +
     5class Verbosity(Enumeration):
    + 6    """Represents different verbosity levels for logging or output.
    + 7    Attributes:
    + 8        MINIMUM (int): The minimum verbosity level.
    + 9        LOW (int): A low verbosity level.
    +10        NORMAL (int): The normal verbosity level.
    +11        DETAILED (int): A detailed verbosity level.
    +12        FULL (int): The full verbosity level.
    +13    """
    +14
    +15    # fmt: off
    +16    MINIMUM     = 1
    +17    LOW         = 2
    +18    NORMAL      = 3
    +19    DETAILED    = 4
    +20    FULL        = 5
    +21    # fmt: on
    +22
    +23    @property
    +24    def val(self) -> int:
    +25        """Gets the integer value of the verbosity level.
    +26        :return: The integer representation of the verbosity level.
    +27        """
    +28        return int(self.value)
    +29
    +30    def match(self, level: "Verbosity") -> bool:
    +31        """Checks if the current verbosity level is less than or equal to the given level.
    +32        :param level: The verbosity level to compare against.
    +33        :return: True if the current level is less than or equal to the given level, otherwise False.
    +34        """
    +35        return self.val <= level.val
    +
    + + +

    Represents different verbosity levels for logging or output. +Attributes: + MINIMUM (int): The minimum verbosity level. + LOW (int): A low verbosity level. + NORMAL (int): The normal verbosity level. + DETAILED (int): A detailed verbosity level. + FULL (int): The full verbosity level.

    +
    + + +
    +
    + MINIMUM = +MINIMUM + + +
    + + + + +
    +
    +
    + LOW = +LOW + + +
    + + + + +
    +
    +
    + NORMAL = +NORMAL + + +
    + + + + +
    +
    +
    + DETAILED = +DETAILED + + +
    + + + + +
    +
    +
    + FULL = +FULL + + +
    + + + + +
    +
    + +
    + val: int + + + +
    + +
    23    @property
    +24    def val(self) -> int:
    +25        """Gets the integer value of the verbosity level.
    +26        :return: The integer representation of the verbosity level.
    +27        """
    +28        return int(self.value)
    +
    + + +

    Gets the integer value of the verbosity level.

    + +
    Returns
    + +
    +

    The integer representation of the verbosity level.

    +
    +
    + + +
    +
    +
    def - image_captioner(path_name: str) -> Optional[str]: + match(self, level: Verbosity) -> bool: - +
    - -
    14def image_captioner(path_name: str) -> Optional[str]:
    -15    """This tool is used to describe an image."""
    -16    caption: str | None = None
    -17    posix_path: PathObject = PathObject.of(path_name)
    -18    if not posix_path.exists:
    -19        # Attempt to resolve cross-references
    -20        if history := str(shared.context.flat("HISTORY") or ""):
    -21            if (x_referenced := resolve_x_refs(path_name, history)) and x_referenced != shared.UNCERTAIN_ID:
    -22                x_ref_path: PathObject = PathObject.of(x_referenced)
    -23                posix_path: PathObject = x_ref_path if x_ref_path.exists else posix_path
    -24
    -25    if posix_path.exists:
    -26        events.reply.emit(message=msg.describe_image(str(posix_path)))
    -27        # specify model to be used
    -28        hf_model = "Salesforce/blip-image-captioning-base"
    -29        # use GPU if it's available
    -30        device = "cuda" if torch.cuda.is_available() else "cpu"
    -31        # preprocessor will prepare images for the model
    -32        processor = BlipProcessor.from_pretrained(hf_model)
    -33        # then we initialize the model itself
    -34        model = BlipForConditionalGeneration.from_pretrained(hf_model).to(device)
    -35        # download the image and convert to PIL object
    -36        image = Image.open(str(posix_path)).convert("RGB")
    -37        inputs = processor(image, return_tensors="pt").to(device)
    -38        # generate the caption
    -39        out = model.generate(**inputs, max_new_tokens=20)
    -40        # get the caption
    -41        caption = processor.decode(out[0], skip_special_tokens=True)
    -42        caption = caption.title() if caption else 'I dont know'
    -43
    -44    return caption
    +    
    +            
    30    def match(self, level: "Verbosity") -> bool:
    +31        """Checks if the current verbosity level is less than or equal to the given level.
    +32        :param level: The verbosity level to compare against.
    +33        :return: True if the current level is less than or equal to the given level, otherwise False.
    +34        """
    +35        return self.val <= level.val
     
    -

    This tool is used to describe an image.

    +

    Checks if the current verbosity level is less than or equal to the given level.

    + +
    Parameters
    + +
      +
    • level: The verbosity level to compare against.
    • +
    + +
    Returns
    + +
    +

    True if the current level is less than or equal to the given level, otherwise False.

    +
    +
    +
    +
    Inherited Members
    +
    +
    hspylib.core.enums.enumeration.Enumeration
    +
    names
    +
    values
    +
    value_of
    +
    of_value
    +
    compose
    +
    key
    + +
    +
    enum.Enum
    +
    name
    +
    value
    + +
    +
    +
    + \ No newline at end of file diff --git a/docs/api/askai/main/askai/core/features/rag.html b/docs/api/askai/main/askai/core/features/rag.html deleted file mode 100644 index 0b91986e..00000000 --- a/docs/api/askai/main/askai/core/features/rag.html +++ /dev/null @@ -1,254 +0,0 @@ - - - - - - - main.askai.core.features.rag API documentation - - - - - - - - - -
    -
    -

    -main.askai.core.features.rag

    - -

    Package initialization.

    -
    - - - - - -
     1# _*_ coding: utf-8 _*_
    - 2#
    - 3# "askai" v1.0.8
    - 4#
    - 5# Package: main.askai.core.features.rag
    - 6"""Package initialization."""
    - 7
    - 8__all__ = [
    - 9    'rag'
    -10]
    -11__version__ = '1.0.8'
    -
    - - -
    -
    - - \ No newline at end of file diff --git a/docs/api/askai/main/askai/core/features/rag/rag.html b/docs/api/askai/main/askai/core/features/rag/rag.html deleted file mode 100644 index 3190bb33..00000000 --- a/docs/api/askai/main/askai/core/features/rag/rag.html +++ /dev/null @@ -1,447 +0,0 @@ - - - - - - - main.askai.core.features.rag.rag API documentation - - - - - - - - - -
    -
    -

    -main.askai.core.features.rag.rag

    - -

    @project: HsPyLib-AskAI -@package: askai.core.features.rag.commons - @file: analysis.py -@created: Fri, 03 Apr 2024 - @author: Hugo Saporetti Junior" - @site: https://github.com/yorevs/askai -@license: MIT - Please refer to https://opensource.org/licenses/MIT

    - -

    Copyright (c) 2024, HomeSetup

    -
    - - - - - -
     1#!/usr/bin/env python3
    - 2# -*- coding: utf-8 -*-
    - 3
    - 4"""
    - 5   @project: HsPyLib-AskAI
    - 6   @package: askai.core.features.rag.commons
    - 7      @file: analysis.py
    - 8   @created: Fri, 03 Apr 2024
    - 9    @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior"
    -10      @site: https://github.com/yorevs/askai
    -11   @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
    -12
    -13   Copyright (c) 2024, HomeSetup
    -14"""
    -15
    -16from askai.core.askai_events import AskAiEvents
    -17from askai.core.askai_messages import msg
    -18from askai.core.askai_prompt import prompt
    -19from askai.core.component.geo_location import geo_location
    -20from askai.core.engine.openai.temperature import Temperature
    -21from askai.core.enums.rag_response import RagResponse
    -22from askai.core.support.langchain_support import lc_llm
    -23from askai.core.support.shared_instances import shared
    -24from askai.exception.exceptions import InaccurateResponse
    -25from langchain_core.messages import AIMessage
    -26from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, PromptTemplate
    -27from langchain_core.runnables.history import RunnableWithMessageHistory
    -28
    -29import logging as log
    -30
    -31
    -32def assert_accuracy(question: str, ai_response: str, pass_threshold: RagResponse = RagResponse.MODERATE) -> None:
    -33    """Function responsible for asserting that the question was properly answered.
    -34    :param question: The user question.
    -35    :param ai_response: The AI response to be analysed.
    -36    :param pass_threshold: The threshold color to be considered as a pass.
    -37    """
    -38    if ai_response and ai_response not in msg.accurate_responses:
    -39        issues_prompt = PromptTemplate(input_variables=["problems"], template=prompt.read_prompt("assert"))
    -40        assert_template = PromptTemplate(input_variables=["datetime", "input", "response"], template=prompt.read_prompt("rag"))
    -41        final_prompt = assert_template.format(datetime=geo_location.datetime, input=question, response=ai_response)
    -42        log.info("Assert::[QUESTION] '%s'  context: '%s'", question, ai_response)
    -43        llm = lc_llm.create_chat_model(Temperature.DATA_ANALYSIS.temp)
    -44        response: AIMessage = llm.invoke(final_prompt)
    -45
    -46        if response and (output := response.content):
    -47            if mat := RagResponse.matches(output):
    -48                status, details = mat.group(1), mat.group(2)
    -49                log.info("Accuracy check ->  status: '%s'  reason: '%s'", status, details)
    -50                AskAiEvents.ASKAI_BUS.events.reply.emit(message=msg.assert_acc(status, details), verbosity="debug")
    -51                if not RagResponse.of_status(status).passed(pass_threshold):
    -52                    shared.context.push("RAG", issues_prompt.format(problems=RagResponse.strip_code(output)))
    -53                    raise InaccurateResponse(f"AI Assistant failed to respond => '{response.content}'")
    -54                return
    -55        # At this point, the response was not Good enough
    -56        raise InaccurateResponse(f"AI Assistant didn't respond accurately. Response: '{response}'")
    -57
    -58    AskAiEvents.ASKAI_BUS.events.reply.emit(message=msg.no_output("query"))
    -59
    -60
    -61def resolve_x_refs(ref_name: str, context: str | None = None) -> str:
    -62    """Replace all cross references by their actual values.
    -63    :param ref_name: The cross-reference or variable name.
    -64    :param context: The context to analyze the references.
    -65    """
    -66    template = ChatPromptTemplate.from_messages(
    -67        [
    -68            ("system", prompt.read_prompt("x-references")),
    -69            MessagesPlaceholder("context", optional=True),
    -70            ("human", "{pathname}"),
    -71        ]
    -72    )
    -73    output = ref_name
    -74    if context or (context := str(shared.context.flat("HISTORY"))):
    -75        runnable = template | lc_llm.create_chat_model(Temperature.CODE_GENERATION.temp)
    -76        runnable = RunnableWithMessageHistory(
    -77            runnable, shared.context.flat, input_messages_key="pathname", history_messages_key="context"
    -78        )
    -79        log.info("Analysis::[QUERY] '%s'  context=%s", ref_name, context)
    -80        AskAiEvents.ASKAI_BUS.events.reply.emit(message=msg.x_reference(ref_name), verbosity="debug")
    -81        response = runnable.invoke({"pathname": ref_name}, config={"configurable": {"session_id": "HISTORY"}})
    -82        if response and (output := response.content) and shared.UNCERTAIN_ID != output:
    -83            output = response.content
    -84
    -85    return output
    -
    - - -
    -
    - -
    - - def - assert_accuracy( question: str, ai_response: str, pass_threshold: askai.core.enums.rag_response.RagResponse = MODERATE) -> None: - - - -
    - -
    33def assert_accuracy(question: str, ai_response: str, pass_threshold: RagResponse = RagResponse.MODERATE) -> None:
    -34    """Function responsible for asserting that the question was properly answered.
    -35    :param question: The user question.
    -36    :param ai_response: The AI response to be analysed.
    -37    :param pass_threshold: The threshold color to be considered as a pass.
    -38    """
    -39    if ai_response and ai_response not in msg.accurate_responses:
    -40        issues_prompt = PromptTemplate(input_variables=["problems"], template=prompt.read_prompt("assert"))
    -41        assert_template = PromptTemplate(input_variables=["datetime", "input", "response"], template=prompt.read_prompt("rag"))
    -42        final_prompt = assert_template.format(datetime=geo_location.datetime, input=question, response=ai_response)
    -43        log.info("Assert::[QUESTION] '%s'  context: '%s'", question, ai_response)
    -44        llm = lc_llm.create_chat_model(Temperature.DATA_ANALYSIS.temp)
    -45        response: AIMessage = llm.invoke(final_prompt)
    -46
    -47        if response and (output := response.content):
    -48            if mat := RagResponse.matches(output):
    -49                status, details = mat.group(1), mat.group(2)
    -50                log.info("Accuracy check ->  status: '%s'  reason: '%s'", status, details)
    -51                AskAiEvents.ASKAI_BUS.events.reply.emit(message=msg.assert_acc(status, details), verbosity="debug")
    -52                if not RagResponse.of_status(status).passed(pass_threshold):
    -53                    shared.context.push("RAG", issues_prompt.format(problems=RagResponse.strip_code(output)))
    -54                    raise InaccurateResponse(f"AI Assistant failed to respond => '{response.content}'")
    -55                return
    -56        # At this point, the response was not Good enough
    -57        raise InaccurateResponse(f"AI Assistant didn't respond accurately. Response: '{response}'")
    -58
    -59    AskAiEvents.ASKAI_BUS.events.reply.emit(message=msg.no_output("query"))
    -
    - - -

    Function responsible for asserting that the question was properly answered.

    - -
    Parameters
    - -
      -
    • question: The user question.
    • -
    • ai_response: The AI response to be analysed.
    • -
    • pass_threshold: The threshold color to be considered as a pass.
    • -
    -
    - - -
    -
    - -
    - - def - resolve_x_refs(ref_name: str, context: str | None = None) -> str: - - - -
    - -
    62def resolve_x_refs(ref_name: str, context: str | None = None) -> str:
    -63    """Replace all cross references by their actual values.
    -64    :param ref_name: The cross-reference or variable name.
    -65    :param context: The context to analyze the references.
    -66    """
    -67    template = ChatPromptTemplate.from_messages(
    -68        [
    -69            ("system", prompt.read_prompt("x-references")),
    -70            MessagesPlaceholder("context", optional=True),
    -71            ("human", "{pathname}"),
    -72        ]
    -73    )
    -74    output = ref_name
    -75    if context or (context := str(shared.context.flat("HISTORY"))):
    -76        runnable = template | lc_llm.create_chat_model(Temperature.CODE_GENERATION.temp)
    -77        runnable = RunnableWithMessageHistory(
    -78            runnable, shared.context.flat, input_messages_key="pathname", history_messages_key="context"
    -79        )
    -80        log.info("Analysis::[QUERY] '%s'  context=%s", ref_name, context)
    -81        AskAiEvents.ASKAI_BUS.events.reply.emit(message=msg.x_reference(ref_name), verbosity="debug")
    -82        response = runnable.invoke({"pathname": ref_name}, config={"configurable": {"session_id": "HISTORY"}})
    -83        if response and (output := response.content) and shared.UNCERTAIN_ID != output:
    -84            output = response.content
    -85
    -86    return output
    -
    - - -

    Replace all cross references by their actual values.

    - -
    Parameters
    - -
      -
    • ref_name: The cross-reference or variable name.
    • -
    • context: The context to analyze the references.
    • -
    -
    - - -
    -
    - - \ No newline at end of file diff --git a/docs/api/askai/main/askai/core/features/router.html b/docs/api/askai/main/askai/core/features/router.html index a4af10c6..b9dc7d74 100644 --- a/docs/api/askai/main/askai/core/features/router.html +++ b/docs/api/askai/main/askai/core/features/router.html @@ -3,7 +3,7 @@ - + main.askai.core.features.router API documentation @@ -29,12 +29,10 @@

    Submodules

    @@ -60,20 +58,18 @@

     1# _*_ coding: utf-8 _*_
      2#
    - 3# hspylib-askai v1.0.11
    + 3# hspylib-askai v1.0.13
      4#
      5# Package: main.askai.core.features.router
      6"""Package initialization."""
      7
      8__all__ = [
    - 9    'ai_processor', 
    -10    'model_selector', 
    -11    'procs', 
    -12    'task_agent', 
    -13    'task_toolkit', 
    -14    'tools'
    -15]
    -16__version__ = '1.0.11'
    + 9    'model_selector', 
    +10    'task_accuracy', 
    +11    'task_agent', 
    +12    'task_toolkit'
    +13]
    +14__version__ = '1.0.13'
     
    diff --git a/docs/api/askai/main/askai/core/features/router/model_selector.html b/docs/api/askai/main/askai/core/features/router/model_selector.html index 968e40cb..ea288d00 100644 --- a/docs/api/askai/main/askai/core/features/router/model_selector.html +++ b/docs/api/askai/main/askai/core/features/router/model_selector.html @@ -3,7 +3,7 @@ - + main.askai.core.features.router.model_selector API documentation @@ -104,39 +104,45 @@

    20from askai.core.support.langchain_support import lc_llm 21from hspylib.core.metaclass.singleton import Singleton 22from hspylib.core.object_mapper import object_mapper -23from langchain_core.prompts import PromptTemplate -24 +23from langchain_core.language_models import BaseChatModel +24from langchain_core.prompts import PromptTemplate 25 -26class ModelSelector(metaclass=Singleton): -27 """TODO""" -28 -29 INSTANCE: "ModelSelector" -30 -31 @property -32 def model_template(self) -> PromptTemplate: -33 """Retrieve the Routing Model Template.""" -34 return PromptTemplate( -35 input_variables=["datetime", "models", "question"], template=prompt.read_prompt("model-select.txt") -36 ) -37 -38 def select_model(self, query: str) -> ModelResult: -39 """Select the response model.""" -40 -41 final_prompt: str = self.model_template.format( -42 datetime=geo_location.datetime, models=RoutingModel.enlist(), question=query -43 ) -44 llm = lc_llm.create_chat_model(Temperature.DATA_ANALYSIS.temp) -45 if response := llm.invoke(final_prompt): -46 json_string: str = response.content # from AIMessage -47 model_result: ModelResult | str = object_mapper.of_json(json_string, ModelResult) -48 model_result: ModelResult = model_result if isinstance(model_result, ModelResult) else ModelResult.default() -49 else: -50 model_result: ModelResult = ModelResult.default() -51 -52 return model_result -53 -54 -55assert (selector := ModelSelector().INSTANCE) is not None +26 +27class ModelSelector(metaclass=Singleton): +28 """Utility class to query the LLM for selecting the appropriate model to process user requests. This class +29 facilitates the selection of the most suitable model based on the nature of the user's query, ensuring optimal +30 processing and response generation. +31 """ +32 +33 INSTANCE: "ModelSelector" +34 +35 @property +36 def model_template(self) -> PromptTemplate: +37 """Retrieve the Routing Model Template.""" +38 return PromptTemplate( +39 input_variables=["datetime", "models", "question"], template=prompt.read_prompt("model-select.txt") +40 ) +41 +42 def select_model(self, question: str) -> ModelResult: +43 """Select the appropriate response model based on the given human question. +44 :param question: The user's query used to determine the most suitable model. +45 :return: An instance of ModelResult representing the selected model. +46 """ +47 final_prompt: str = self.model_template.format( +48 datetime=geo_location.datetime, models=RoutingModel.enlist(), question=question +49 ) +50 llm: BaseChatModel = lc_llm.create_chat_model(Temperature.DATA_ANALYSIS.temp) +51 if response := llm.invoke(final_prompt): +52 json_string: str = response.content # from AIMessage +53 model_result: ModelResult | str = object_mapper.of_json(json_string, ModelResult) +54 model_result: ModelResult = model_result if isinstance(model_result, ModelResult) else ModelResult.default() +55 else: +56 model_result: ModelResult = ModelResult.default() +57 +58 return model_result +59 +60 +61assert (selector := ModelSelector().INSTANCE) is not None

    @@ -152,37 +158,44 @@

    -
    27class ModelSelector(metaclass=Singleton):
    -28    """TODO"""
    -29
    -30    INSTANCE: "ModelSelector"
    -31
    -32    @property
    -33    def model_template(self) -> PromptTemplate:
    -34        """Retrieve the Routing Model Template."""
    -35        return PromptTemplate(
    -36            input_variables=["datetime", "models", "question"], template=prompt.read_prompt("model-select.txt")
    -37        )
    -38
    -39    def select_model(self, query: str) -> ModelResult:
    -40        """Select the response model."""
    -41
    -42        final_prompt: str = self.model_template.format(
    -43            datetime=geo_location.datetime, models=RoutingModel.enlist(), question=query
    -44        )
    -45        llm = lc_llm.create_chat_model(Temperature.DATA_ANALYSIS.temp)
    -46        if response := llm.invoke(final_prompt):
    -47            json_string: str = response.content  # from AIMessage
    -48            model_result: ModelResult | str = object_mapper.of_json(json_string, ModelResult)
    -49            model_result: ModelResult = model_result if isinstance(model_result, ModelResult) else ModelResult.default()
    -50        else:
    -51            model_result: ModelResult = ModelResult.default()
    -52
    -53        return model_result
    +            
    28class ModelSelector(metaclass=Singleton):
    +29    """Utility class to query the LLM for selecting the appropriate model to process user requests. This class
    +30    facilitates the selection of the most suitable model based on the nature of the user's query, ensuring optimal
    +31    processing and response generation.
    +32    """
    +33
    +34    INSTANCE: "ModelSelector"
    +35
    +36    @property
    +37    def model_template(self) -> PromptTemplate:
    +38        """Retrieve the Routing Model Template."""
    +39        return PromptTemplate(
    +40            input_variables=["datetime", "models", "question"], template=prompt.read_prompt("model-select.txt")
    +41        )
    +42
    +43    def select_model(self, question: str) -> ModelResult:
    +44        """Select the appropriate response model based on the given human question.
    +45        :param question: The user's query used to determine the most suitable model.
    +46        :return: An instance of ModelResult representing the selected model.
    +47        """
    +48        final_prompt: str = self.model_template.format(
    +49            datetime=geo_location.datetime, models=RoutingModel.enlist(), question=question
    +50        )
    +51        llm: BaseChatModel = lc_llm.create_chat_model(Temperature.DATA_ANALYSIS.temp)
    +52        if response := llm.invoke(final_prompt):
    +53            json_string: str = response.content  # from AIMessage
    +54            model_result: ModelResult | str = object_mapper.of_json(json_string, ModelResult)
    +55            model_result: ModelResult = model_result if isinstance(model_result, ModelResult) else ModelResult.default()
    +56        else:
    +57            model_result: ModelResult = ModelResult.default()
    +58
    +59        return model_result
     
    -

    TODO

    +

    Utility class to query the LLM for selecting the appropriate model to process user requests. This class +facilitates the selection of the most suitable model based on the nature of the user's query, ensuring optimal +processing and response generation.

    @@ -238,12 +251,12 @@

    -
    32    @property
    -33    def model_template(self) -> PromptTemplate:
    -34        """Retrieve the Routing Model Template."""
    -35        return PromptTemplate(
    -36            input_variables=["datetime", "models", "question"], template=prompt.read_prompt("model-select.txt")
    -37        )
    +            
    36    @property
    +37    def model_template(self) -> PromptTemplate:
    +38        """Retrieve the Routing Model Template."""
    +39        return PromptTemplate(
    +40            input_variables=["datetime", "models", "question"], template=prompt.read_prompt("model-select.txt")
    +41        )
     
    @@ -257,31 +270,45 @@

    def - select_model(self, query: str) -> askai.core.model.model_result.ModelResult: + select_model(self, question: str) -> askai.core.model.model_result.ModelResult:
    -
    39    def select_model(self, query: str) -> ModelResult:
    -40        """Select the response model."""
    -41
    -42        final_prompt: str = self.model_template.format(
    -43            datetime=geo_location.datetime, models=RoutingModel.enlist(), question=query
    -44        )
    -45        llm = lc_llm.create_chat_model(Temperature.DATA_ANALYSIS.temp)
    -46        if response := llm.invoke(final_prompt):
    -47            json_string: str = response.content  # from AIMessage
    -48            model_result: ModelResult | str = object_mapper.of_json(json_string, ModelResult)
    -49            model_result: ModelResult = model_result if isinstance(model_result, ModelResult) else ModelResult.default()
    -50        else:
    -51            model_result: ModelResult = ModelResult.default()
    -52
    -53        return model_result
    +            
    43    def select_model(self, question: str) -> ModelResult:
    +44        """Select the appropriate response model based on the given human question.
    +45        :param question: The user's query used to determine the most suitable model.
    +46        :return: An instance of ModelResult representing the selected model.
    +47        """
    +48        final_prompt: str = self.model_template.format(
    +49            datetime=geo_location.datetime, models=RoutingModel.enlist(), question=question
    +50        )
    +51        llm: BaseChatModel = lc_llm.create_chat_model(Temperature.DATA_ANALYSIS.temp)
    +52        if response := llm.invoke(final_prompt):
    +53            json_string: str = response.content  # from AIMessage
    +54            model_result: ModelResult | str = object_mapper.of_json(json_string, ModelResult)
    +55            model_result: ModelResult = model_result if isinstance(model_result, ModelResult) else ModelResult.default()
    +56        else:
    +57            model_result: ModelResult = ModelResult.default()
    +58
    +59        return model_result
     
    -

    Select the response model.

    +

    Select the appropriate response model based on the given human question.

    + +
    Parameters
    + +
      +
    • question: The user's query used to determine the most suitable model.
    • +
    + +
    Returns
    + +
    +

    An instance of ModelResult representing the selected model.

    +
    diff --git a/docs/api/askai/main/askai/core/features/router/procs/task_splitter.html b/docs/api/askai/main/askai/core/features/router/procs/task_splitter.html deleted file mode 100644 index aefa4a57..00000000 --- a/docs/api/askai/main/askai/core/features/router/procs/task_splitter.html +++ /dev/null @@ -1,656 +0,0 @@ - - - - - - - main.askai.core.features.router.procs.task_splitter API documentation - - - - - - - - - -
    -
    -

    -main.askai.core.features.router.procs.task_splitter

    - -

    @project: HsPyLib-AskAI -@package: askai.core.features.router - @file: task_splitter.py -@created: Mon, 01 Apr 2024 - @author: Hugo Saporetti Junior - @site: https://github.com/yorevs/askai -@license: MIT - Please refer to https://opensource.org/licenses/MIT

    - -

    Copyright (c) 2024, HomeSetup

    -
    - - - - - -
      1#!/usr/bin/env python3
    -  2# -*- coding: utf-8 -*-
    -  3
    -  4"""
    -  5   @project: HsPyLib-AskAI
    -  6   @package: askai.core.features.router
    -  7      @file: task_splitter.py
    -  8   @created: Mon, 01 Apr 2024
    -  9    @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior
    - 10      @site: https://github.com/yorevs/askai
    - 11   @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
    - 12
    - 13   Copyright (c) 2024, HomeSetup
    - 14"""
    - 15from askai.core.askai_configs import configs
    - 16from askai.core.askai_events import events
    - 17from askai.core.askai_messages import msg
    - 18from askai.core.askai_prompt import prompt
    - 19from askai.core.component.geo_location import geo_location
    - 20from askai.core.engine.openai.temperature import Temperature
    - 21from askai.core.features.router.task_agent import agent
    - 22from askai.core.model.action_plan import ActionPlan
    - 23from askai.core.model.model_result import ModelResult
    - 24from askai.core.support.langchain_support import lc_llm
    - 25from askai.core.support.shared_instances import shared
    - 26from askai.exception.exceptions import InaccurateResponse
    - 27from hspylib.core.exception.exceptions import InvalidArgumentError
    - 28from hspylib.core.metaclass.singleton import Singleton
    - 29from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, PromptTemplate
    - 30from langchain_core.runnables.history import RunnableWithMessageHistory
    - 31from pathlib import Path
    - 32from retry import retry
    - 33from textwrap import dedent
    - 34from typing import Any, Optional, Type, TypeAlias
    - 35
    - 36import logging as log
    - 37import os
    - 38import PIL
    - 39
    - 40AgentResponse: TypeAlias = dict[str, Any]
    - 41
    - 42
    - 43class TaskSplitter(metaclass=Singleton):
    - 44    """Processor to provide a divide and conquer set of tasks to fulfill an objective. This is responsible for the
    - 45    orchestration and execution of the smaller tasks."""
    - 46
    - 47    INSTANCE: "TaskSplitter"
    - 48
    - 49    HUMAN_PROMPT: str = dedent("""Human Question: '{input}'""").strip()
    - 50
    - 51    # Allow the router to retry on the errors bellow.
    - 52    RETRIABLE_ERRORS: tuple[Type[Exception], ...] = (
    - 53        InaccurateResponse,
    - 54        InvalidArgumentError,
    - 55        ValueError,
    - 56        AttributeError,
    - 57        PIL.UnidentifiedImageError,
    - 58    )
    - 59
    - 60    def __init__(self):
    - 61        self._approved: bool = False
    - 62
    - 63    @property
    - 64    def template(self) -> ChatPromptTemplate:
    - 65        """Retrieve the processor Template."""
    - 66
    - 67        rag: str = str(shared.context.flat("EVALUATION"))
    - 68        template = PromptTemplate(
    - 69            input_variables=["os_type", "shell", "datetime", "home"], template=prompt.read_prompt("task-split.txt")
    - 70        )
    - 71        return ChatPromptTemplate.from_messages(
    - 72            [
    - 73                (
    - 74                    "system",
    - 75                    template.format(
    - 76                        os_type=prompt.os_type, shell=prompt.shell,
    - 77                        datetime=geo_location.datetime, home=Path.home()
    - 78                    ),
    - 79                ),
    - 80                MessagesPlaceholder("chat_history"),
    - 81                ("assistant", rag),
    - 82                ("human", self.HUMAN_PROMPT),
    - 83            ]
    - 84        )
    - 85
    - 86    def process(self, question: str, **_) -> Optional[str]:
    - 87        """Process the user question by splitting complex tasks into smaller single actionable tasks.
    - 88        :param question: The user question to process.
    - 89        """
    - 90
    - 91        os.chdir(Path.home())
    - 92        shared.context.forget("EVALUATION")  # Erase previous scratchpad.
    - 93        model: ModelResult = ModelResult.default()  # Hard-coding the result model for now.
    - 94
    - 95        @retry(exceptions=self.RETRIABLE_ERRORS, tries=configs.max_router_retries, backoff=0)
    - 96        def _process_wrapper() -> Optional[str]:
    - 97            """Wrapper to allow accuracy retries."""
    - 98            log.info("Router::[QUESTION] '%s'", question)
    - 99            runnable = self.template | lc_llm.create_chat_model(Temperature.CODE_GENERATION.temp)
    -100            runnable = RunnableWithMessageHistory(
    -101                runnable, shared.context.flat, input_messages_key="input", history_messages_key="chat_history"
    -102            )
    -103            if response := runnable.invoke({"input": question}, config={"configurable": {"session_id": "HISTORY"}}):
    -104                log.info("Router::[RESPONSE] Received from AI: \n%s.", str(response.content))
    -105                plan: ActionPlan = ActionPlan.create(question, response, model)
    -106                events.reply.emit(message=msg.action_plan(str(plan)), verbosity="debug")
    -107                output = agent.invoke(question, plan)
    -108            else:
    -109                # Most of the times, this indicates a failure.
    -110                output = response
    -111            return output
    -112
    -113        return _process_wrapper()
    -114
    -115
    -116assert (splitter := TaskSplitter().INSTANCE) is not None
    -
    - - -
    -
    -
    - AgentResponse: TypeAlias = -dict[str, typing.Any] - - -
    - - - - -
    -
    - -
    - - class - TaskSplitter(typing.Type): - - - -
    - -
     44class TaskSplitter(metaclass=Singleton):
    - 45    """Processor to provide a divide and conquer set of tasks to fulfill an objective. This is responsible for the
    - 46    orchestration and execution of the smaller tasks."""
    - 47
    - 48    INSTANCE: "TaskSplitter"
    - 49
    - 50    HUMAN_PROMPT: str = dedent("""Human Question: '{input}'""").strip()
    - 51
    - 52    # Allow the router to retry on the errors bellow.
    - 53    RETRIABLE_ERRORS: tuple[Type[Exception], ...] = (
    - 54        InaccurateResponse,
    - 55        InvalidArgumentError,
    - 56        ValueError,
    - 57        AttributeError,
    - 58        PIL.UnidentifiedImageError,
    - 59    )
    - 60
    - 61    def __init__(self):
    - 62        self._approved: bool = False
    - 63
    - 64    @property
    - 65    def template(self) -> ChatPromptTemplate:
    - 66        """Retrieve the processor Template."""
    - 67
    - 68        rag: str = str(shared.context.flat("EVALUATION"))
    - 69        template = PromptTemplate(
    - 70            input_variables=["os_type", "shell", "datetime", "home"], template=prompt.read_prompt("task-split.txt")
    - 71        )
    - 72        return ChatPromptTemplate.from_messages(
    - 73            [
    - 74                (
    - 75                    "system",
    - 76                    template.format(
    - 77                        os_type=prompt.os_type, shell=prompt.shell,
    - 78                        datetime=geo_location.datetime, home=Path.home()
    - 79                    ),
    - 80                ),
    - 81                MessagesPlaceholder("chat_history"),
    - 82                ("assistant", rag),
    - 83                ("human", self.HUMAN_PROMPT),
    - 84            ]
    - 85        )
    - 86
    - 87    def process(self, question: str, **_) -> Optional[str]:
    - 88        """Process the user question by splitting complex tasks into smaller single actionable tasks.
    - 89        :param question: The user question to process.
    - 90        """
    - 91
    - 92        os.chdir(Path.home())
    - 93        shared.context.forget("EVALUATION")  # Erase previous scratchpad.
    - 94        model: ModelResult = ModelResult.default()  # Hard-coding the result model for now.
    - 95
    - 96        @retry(exceptions=self.RETRIABLE_ERRORS, tries=configs.max_router_retries, backoff=0)
    - 97        def _process_wrapper() -> Optional[str]:
    - 98            """Wrapper to allow accuracy retries."""
    - 99            log.info("Router::[QUESTION] '%s'", question)
    -100            runnable = self.template | lc_llm.create_chat_model(Temperature.CODE_GENERATION.temp)
    -101            runnable = RunnableWithMessageHistory(
    -102                runnable, shared.context.flat, input_messages_key="input", history_messages_key="chat_history"
    -103            )
    -104            if response := runnable.invoke({"input": question}, config={"configurable": {"session_id": "HISTORY"}}):
    -105                log.info("Router::[RESPONSE] Received from AI: \n%s.", str(response.content))
    -106                plan: ActionPlan = ActionPlan.create(question, response, model)
    -107                events.reply.emit(message=msg.action_plan(str(plan)), verbosity="debug")
    -108                output = agent.invoke(question, plan)
    -109            else:
    -110                # Most of the times, this indicates a failure.
    -111                output = response
    -112            return output
    -113
    -114        return _process_wrapper()
    -
    - - -

    Processor to provide a divide and conquer set of tasks to fulfill an objective. This is responsible for the -orchestration and execution of the smaller tasks.

    -
    - - -
    - -
    - - TaskSplitter(*args, **kwargs) - - - -
    - -
    35    def __call__(mcs, *args, **kwargs) -> Any:
    -36        """Invoke the class constructor or return the instance if it exists."""
    -37        if not Singleton.has_instance(mcs):
    -38            try:
    -39                instance = super().__call__(*args, **kwargs)
    -40                check_not_none(instance, f"Unable to create Singleton instance: {mcs}")
    -41                setattr(mcs, "INSTANCE", instance)
    -42                Singleton._instances[mcs.__name__] = instance
    -43                log.debug("Created a new Singleton instance: %s.%s", mcs.__module__, mcs.__name__)
    -44                return instance
    -45            except Exception as err:
    -46                raise HSBaseException(f"Failed to create singleton instance: '{mcs.__name__}'", err) from err
    -47        return Singleton._instances[mcs.__name__]
    -
    - - -

    Invoke the class constructor or return the instance if it exists.

    -
    - - -
    -
    -
    - INSTANCE: TaskSplitter - - -
    - - - - -
    -
    -
    - HUMAN_PROMPT: str = -"Human Question: '{input}'" - - -
    - - - - -
    -
    -
    - RETRIABLE_ERRORS: tuple[typing.Type[Exception], ...] = - - (<class 'askai.exception.exceptions.InaccurateResponse'>, <class 'hspylib.core.exception.exceptions.InvalidArgumentError'>, <class 'ValueError'>, <class 'AttributeError'>, <class 'PIL.UnidentifiedImageError'>) - - -
    - - - - -
    -
    - -
    - template: langchain_core.prompts.chat.ChatPromptTemplate - - - -
    - -
    64    @property
    -65    def template(self) -> ChatPromptTemplate:
    -66        """Retrieve the processor Template."""
    -67
    -68        rag: str = str(shared.context.flat("EVALUATION"))
    -69        template = PromptTemplate(
    -70            input_variables=["os_type", "shell", "datetime", "home"], template=prompt.read_prompt("task-split.txt")
    -71        )
    -72        return ChatPromptTemplate.from_messages(
    -73            [
    -74                (
    -75                    "system",
    -76                    template.format(
    -77                        os_type=prompt.os_type, shell=prompt.shell,
    -78                        datetime=geo_location.datetime, home=Path.home()
    -79                    ),
    -80                ),
    -81                MessagesPlaceholder("chat_history"),
    -82                ("assistant", rag),
    -83                ("human", self.HUMAN_PROMPT),
    -84            ]
    -85        )
    -
    - - -

    Retrieve the processor Template.

    -
    - - -
    -
    - -
    - - def - process(self, question: str, **_) -> Optional[str]: - - - -
    - -
     87    def process(self, question: str, **_) -> Optional[str]:
    - 88        """Process the user question by splitting complex tasks into smaller single actionable tasks.
    - 89        :param question: The user question to process.
    - 90        """
    - 91
    - 92        os.chdir(Path.home())
    - 93        shared.context.forget("EVALUATION")  # Erase previous scratchpad.
    - 94        model: ModelResult = ModelResult.default()  # Hard-coding the result model for now.
    - 95
    - 96        @retry(exceptions=self.RETRIABLE_ERRORS, tries=configs.max_router_retries, backoff=0)
    - 97        def _process_wrapper() -> Optional[str]:
    - 98            """Wrapper to allow accuracy retries."""
    - 99            log.info("Router::[QUESTION] '%s'", question)
    -100            runnable = self.template | lc_llm.create_chat_model(Temperature.CODE_GENERATION.temp)
    -101            runnable = RunnableWithMessageHistory(
    -102                runnable, shared.context.flat, input_messages_key="input", history_messages_key="chat_history"
    -103            )
    -104            if response := runnable.invoke({"input": question}, config={"configurable": {"session_id": "HISTORY"}}):
    -105                log.info("Router::[RESPONSE] Received from AI: \n%s.", str(response.content))
    -106                plan: ActionPlan = ActionPlan.create(question, response, model)
    -107                events.reply.emit(message=msg.action_plan(str(plan)), verbosity="debug")
    -108                output = agent.invoke(question, plan)
    -109            else:
    -110                # Most of the times, this indicates a failure.
    -111                output = response
    -112            return output
    -113
    -114        return _process_wrapper()
    -
    - - -

    Process the user question by splitting complex tasks into smaller single actionable tasks.

    - -
    Parameters
    - -
      -
    • question: The user question to process.
    • -
    -
    - - -
    -
    -
    - - \ No newline at end of file diff --git a/docs/api/askai/main/askai/core/features/validation/accuracy.html b/docs/api/askai/main/askai/core/features/router/task_accuracy.html similarity index 79% rename from docs/api/askai/main/askai/core/features/validation/accuracy.html rename to docs/api/askai/main/askai/core/features/router/task_accuracy.html index 580359d6..060cd9b0 100644 --- a/docs/api/askai/main/askai/core/features/validation/accuracy.html +++ b/docs/api/askai/main/askai/core/features/router/task_accuracy.html @@ -3,8 +3,8 @@ - - main.askai.core.features.validation.accuracy API documentation + + main.askai.core.features.router.task_accuracy API documentation @@ -16,11 +16,11 @@

  • EVALUATION_GUIDE
  • +
  • + RAG +
  • assert_accuracy
  • @@ -53,7 +56,7 @@

    API Documentation

    -main.askai.core.features.validation.accuracy

    +main.askai.core.features.router.task_accuracy

    @project: HsPyLib-AskAI @package: askai.core.features.rag.commons @@ -66,9 +69,9 @@

    Copyright (c) 2024, HomeSetup

    - + - +
      1#!/usr/bin/env python3
       2# -*- coding: utf-8 -*-
    @@ -88,96 +91,98 @@ 

    16from askai.core.askai_events import events 17from askai.core.askai_messages import msg 18from askai.core.askai_prompt import prompt - 19from askai.core.component.geo_location import geo_location - 20from askai.core.engine.openai.temperature import Temperature - 21from askai.core.enums.acc_response import AccResponse + 19from askai.core.engine.openai.temperature import Temperature + 20from askai.core.enums.acc_response import AccResponse + 21from askai.core.model.ai_reply import AIReply 22from askai.core.support.langchain_support import lc_llm - 23from askai.core.support.shared_instances import shared - 24from askai.exception.exceptions import InaccurateResponse - 25from langchain_core.messages import AIMessage - 26from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, PromptTemplate - 27from langchain_core.runnables.history import RunnableWithMessageHistory - 28from textwrap import dedent - 29 - 30import logging as log - 31 - 32EVALUATION_GUIDE: str = dedent( - 33 """ - 34**Accuracy Evaluation Guidelines:** - 35 - 361. Review and analyze your past responses to ensure optimal accuracy. - 372. Constructively self-criticize your overall responses regularly. - 383. Reflect on past decisions and strategies to refine your approach. - 394. Try something different. - 40""" - 41).strip() - 42 + 23from askai.core.support.rag_provider import RAGProvider + 24from askai.core.support.shared_instances import shared + 25from askai.exception.exceptions import InaccurateResponse, InterruptionRequest + 26from langchain_core.messages import AIMessage + 27from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, PromptTemplate + 28from langchain_core.runnables.history import RunnableWithMessageHistory + 29from textwrap import dedent + 30 + 31import logging as log + 32 + 33EVALUATION_GUIDE: str = dedent( + 34 """ + 35**Accuracy Evaluation Guidelines:** + 36 + 371. Analyze past responses to ensure accuracy. + 382. Regularly self-critique overall responses. + 393. Reflect on past strategies to refine your approach. + 404. Experiment with different methods or solutions. + 41""" + 42).strip() 43 - 44def assert_accuracy( - 45 question: str, - 46 ai_response: str, - 47 pass_threshold: AccResponse = AccResponse.MODERATE - 48) -> AccResponse: - 49 """Function responsible for asserting that the question was properly answered. - 50 :param question: The user question. - 51 :param ai_response: The AI response to be analysed. - 52 :param pass_threshold: The threshold color to be considered as a pass. - 53 """ - 54 if ai_response and ai_response not in msg.accurate_responses: - 55 issues_prompt = PromptTemplate( - 56 input_variables=["problems"], template=prompt.read_prompt("assert") - 57 ) - 58 assert_template = PromptTemplate( - 59 input_variables=["datetime", "input", "response"], template=prompt.read_prompt("accuracy") - 60 ) - 61 final_prompt = assert_template.format(datetime=geo_location.datetime, input=question, response=ai_response) - 62 log.info("Assert::[QUESTION] '%s' context: '%s'", question, ai_response) - 63 llm = lc_llm.create_chat_model(Temperature.COLDEST.temp) - 64 response: AIMessage = llm.invoke(final_prompt) - 65 - 66 if response and (output := response.content): - 67 if mat := AccResponse.matches(output): - 68 status, details = mat.group(1), mat.group(2) - 69 log.info("Accuracy check -> status: '%s' reason: '%s'", status, details) - 70 events.reply.emit(message=msg.assert_acc(status, details), verbosity="debug") - 71 if not (rag_resp := AccResponse.of_status(status, details)).passed(pass_threshold): - 72 # Include the guidelines for the first mistake. - 73 if not shared.context.get("EVALUATION"): - 74 shared.context.push("EVALUATION", EVALUATION_GUIDE) - 75 shared.context.push("EVALUATION", issues_prompt.format(problems=AccResponse.strip_code(output))) - 76 raise InaccurateResponse(f"AI Assistant failed to respond => '{response.content}'") - 77 return rag_resp - 78 # At this point, the response was not Good. - 79 raise InaccurateResponse(f"AI Assistant didn't respond accurately. Response: '{response}'") - 80 - 81 events.reply.emit(message=msg.no_output("query")) - 82 + 44RAG: RAGProvider = RAGProvider("accuracy.csv") + 45 + 46 + 47def assert_accuracy(question: str, ai_response: str, pass_threshold: AccResponse = AccResponse.MODERATE) -> AccResponse: + 48 """Assert that the AI's response to the question meets the required accuracy threshold. + 49 :param question: The user's question. + 50 :param ai_response: The AI's response to be analyzed for accuracy. + 51 :param pass_threshold: The accuracy threshold, represented by a color, that must be met or exceeded for the + 52 response to be considered a pass (default is AccResponse.MODERATE). + 53 :return: The accuracy classification of the AI's response as an AccResponse enum value. + 54 """ + 55 if ai_response and ai_response not in msg.accurate_responses: + 56 issues_prompt = PromptTemplate(input_variables=["problems"], template=prompt.read_prompt("assert")) + 57 assert_template = PromptTemplate( + 58 input_variables=["rag", "input", "response"], template=prompt.read_prompt("accuracy") + 59 ) + 60 final_prompt = assert_template.format(rag=RAG.get_rag_examples(question), input=question, response=ai_response) + 61 log.info("Assert::[QUESTION] '%s' context: '%s'", question, ai_response) + 62 llm = lc_llm.create_chat_model(Temperature.COLDEST.temp) + 63 response: AIMessage = llm.invoke(final_prompt) + 64 + 65 if response and (output := response.content): + 66 if mat := AccResponse.matches(output): + 67 status, details = mat.group(1), mat.group(2) + 68 log.info("Accuracy check -> status: '%s' reason: '%s'", status, details) + 69 events.reply.emit(reply=AIReply.debug(msg.assert_acc(status, details))) + 70 if (rag_resp := AccResponse.of_status(status, details)).is_interrupt: + 71 # AI flags that it can't continue interacting. + 72 log.warning(msg.interruption_requested(output)) + 73 raise InterruptionRequest(ai_response) + 74 elif not rag_resp.passed(pass_threshold): + 75 # Include the guidelines for the first mistake. + 76 if not shared.context.get("EVALUATION"): + 77 shared.context.push("EVALUATION", EVALUATION_GUIDE) + 78 shared.context.push("EVALUATION", issues_prompt.format(problems=AccResponse.strip_code(output))) + 79 raise InaccurateResponse(f"AI Assistant failed to respond => '{response.content}'") + 80 return rag_resp + 81 # At this point, the response was not Good. + 82 raise InaccurateResponse(f"AI Assistant didn't respond accurately. Response: '{response}'") 83 - 84def resolve_x_refs(ref_name: str, context: str | None = None) -> str: - 85 """Replace all cross references by their actual values. - 86 :param ref_name: The cross-reference or variable name. - 87 :param context: The context to analyze the references. - 88 """ - 89 template = ChatPromptTemplate.from_messages( - 90 [ - 91 ("system", prompt.read_prompt("x-references")), - 92 MessagesPlaceholder("context", optional=True), - 93 ("human", "{pathname}"), - 94 ] - 95 ) - 96 output = ref_name - 97 if context or (context := str(shared.context.flat("HISTORY"))): - 98 runnable = template | lc_llm.create_chat_model(Temperature.CODE_GENERATION.temp) - 99 runnable = RunnableWithMessageHistory( -100 runnable, shared.context.flat, input_messages_key="pathname", history_messages_key="context" -101 ) -102 log.info("Analysis::[QUERY] '%s' context=%s", ref_name, context) -103 events.reply.emit(message=msg.x_reference(ref_name), verbosity="debug") -104 response = runnable.invoke({"pathname": ref_name}, config={"configurable": {"session_id": "HISTORY"}}) -105 if response and (output := response.content) and shared.UNCERTAIN_ID != output: -106 output = response.content -107 -108 return output + 84 + 85def resolve_x_refs(ref_name: str, context: str | None = None) -> str: + 86 """Replace all cross-references with their actual values. + 87 :param ref_name: The name of the cross-reference or variable to resolve. + 88 :param context: The context in which to analyze and resolve the references (optional). + 89 :return: The string with all cross-references replaced by their corresponding values. + 90 """ + 91 template = ChatPromptTemplate.from_messages( + 92 [ + 93 ("system", prompt.read_prompt("x-references")), + 94 MessagesPlaceholder("context", optional=True), + 95 ("human", "{pathname}"), + 96 ] + 97 ) + 98 output = ref_name + 99 if context or (context := str(shared.context.flat("HISTORY"))): +100 runnable = template | lc_llm.create_chat_model(Temperature.CODE_GENERATION.temp) +101 runnable = RunnableWithMessageHistory( +102 runnable, shared.context.flat, input_messages_key="pathname", history_messages_key="context" +103 ) +104 log.info("Analysis::[QUERY] '%s' context=%s", ref_name, context) +105 events.reply.emit(reply=AIReply.debug(msg.x_reference(ref_name))) +106 response = runnable.invoke({"pathname": ref_name}, config={"configurable": {"session_id": "HISTORY"}}) +107 if response and (output := response.content) and shared.UNCERTAIN_ID != output: +108 output = response.content +109 +110 return output

    @@ -186,7 +191,7 @@

    EVALUATION_GUIDE: str = - '**Accuracy Evaluation Guidelines:**\n\n1. Review and analyze your past responses to ensure optimal accuracy.\n2. Constructively self-criticize your overall responses regularly.\n3. Reflect on past decisions and strategies to refine your approach.\n4. Try something different.' + '**Accuracy Evaluation Guidelines:**\n\n1. Analyze past responses to ensure accuracy.\n2. Regularly self-critique overall responses.\n3. Reflect on past strategies to refine your approach.\n4. Experiment with different methods or solutions.'
    @@ -194,6 +199,18 @@

    + +
    +
    + RAG: askai.core.support.rag_provider.RAGProvider = +<askai.core.support.rag_provider.RAGProvider object> + + +
    + + + +
    @@ -206,56 +223,61 @@

    -
    45def assert_accuracy(
    -46    question: str,
    -47    ai_response: str,
    -48    pass_threshold: AccResponse = AccResponse.MODERATE
    -49) -> AccResponse:
    -50    """Function responsible for asserting that the question was properly answered.
    -51    :param question: The user question.
    -52    :param ai_response: The AI response to be analysed.
    -53    :param pass_threshold: The threshold color to be considered as a pass.
    -54    """
    -55    if ai_response and ai_response not in msg.accurate_responses:
    -56        issues_prompt = PromptTemplate(
    -57            input_variables=["problems"], template=prompt.read_prompt("assert")
    -58        )
    -59        assert_template = PromptTemplate(
    -60            input_variables=["datetime", "input", "response"], template=prompt.read_prompt("accuracy")
    -61        )
    -62        final_prompt = assert_template.format(datetime=geo_location.datetime, input=question, response=ai_response)
    -63        log.info("Assert::[QUESTION] '%s'  context: '%s'", question, ai_response)
    -64        llm = lc_llm.create_chat_model(Temperature.COLDEST.temp)
    -65        response: AIMessage = llm.invoke(final_prompt)
    -66
    -67        if response and (output := response.content):
    -68            if mat := AccResponse.matches(output):
    -69                status, details = mat.group(1), mat.group(2)
    -70                log.info("Accuracy check ->  status: '%s'  reason: '%s'", status, details)
    -71                events.reply.emit(message=msg.assert_acc(status, details), verbosity="debug")
    -72                if not (rag_resp := AccResponse.of_status(status, details)).passed(pass_threshold):
    -73                    # Include the guidelines for the first mistake.
    -74                    if not shared.context.get("EVALUATION"):
    -75                        shared.context.push("EVALUATION", EVALUATION_GUIDE)
    -76                    shared.context.push("EVALUATION", issues_prompt.format(problems=AccResponse.strip_code(output)))
    -77                    raise InaccurateResponse(f"AI Assistant failed to respond => '{response.content}'")
    -78                return rag_resp
    -79        # At this point, the response was not Good.
    -80        raise InaccurateResponse(f"AI Assistant didn't respond accurately. Response: '{response}'")
    -81
    -82    events.reply.emit(message=msg.no_output("query"))
    +            
    48def assert_accuracy(question: str, ai_response: str, pass_threshold: AccResponse = AccResponse.MODERATE) -> AccResponse:
    +49    """Assert that the AI's response to the question meets the required accuracy threshold.
    +50    :param question: The user's question.
    +51    :param ai_response: The AI's response to be analyzed for accuracy.
    +52    :param pass_threshold: The accuracy threshold, represented by a color, that must be met or exceeded for the
    +53                           response to be considered a pass (default is AccResponse.MODERATE).
    +54    :return: The accuracy classification of the AI's response as an AccResponse enum value.
    +55    """
    +56    if ai_response and ai_response not in msg.accurate_responses:
    +57        issues_prompt = PromptTemplate(input_variables=["problems"], template=prompt.read_prompt("assert"))
    +58        assert_template = PromptTemplate(
    +59            input_variables=["rag", "input", "response"], template=prompt.read_prompt("accuracy")
    +60        )
    +61        final_prompt = assert_template.format(rag=RAG.get_rag_examples(question), input=question, response=ai_response)
    +62        log.info("Assert::[QUESTION] '%s'  context: '%s'", question, ai_response)
    +63        llm = lc_llm.create_chat_model(Temperature.COLDEST.temp)
    +64        response: AIMessage = llm.invoke(final_prompt)
    +65
    +66        if response and (output := response.content):
    +67            if mat := AccResponse.matches(output):
    +68                status, details = mat.group(1), mat.group(2)
    +69                log.info("Accuracy check ->  status: '%s'  reason: '%s'", status, details)
    +70                events.reply.emit(reply=AIReply.debug(msg.assert_acc(status, details)))
    +71                if (rag_resp := AccResponse.of_status(status, details)).is_interrupt:
    +72                    # AI flags that it can't continue interacting.
    +73                    log.warning(msg.interruption_requested(output))
    +74                    raise InterruptionRequest(ai_response)
    +75                elif not rag_resp.passed(pass_threshold):
    +76                    # Include the guidelines for the first mistake.
    +77                    if not shared.context.get("EVALUATION"):
    +78                        shared.context.push("EVALUATION", EVALUATION_GUIDE)
    +79                    shared.context.push("EVALUATION", issues_prompt.format(problems=AccResponse.strip_code(output)))
    +80                    raise InaccurateResponse(f"AI Assistant failed to respond => '{response.content}'")
    +81                return rag_resp
    +82        # At this point, the response was not Good.
    +83        raise InaccurateResponse(f"AI Assistant didn't respond accurately. Response: '{response}'")
     
    -

    Function responsible for asserting that the question was properly answered.

    +

    Assert that the AI's response to the question meets the required accuracy threshold.

    Parameters
      -
    • question: The user question.
    • -
    • ai_response: The AI response to be analysed.
    • -
    • pass_threshold: The threshold color to be considered as a pass.
    • +
    • question: The user's question.
    • +
    • ai_response: The AI's response to be analyzed for accuracy.
    • +
    • pass_threshold: The accuracy threshold, represented by a color, that must be met or exceeded for the +response to be considered a pass (default is AccResponse.MODERATE).
    + +
    Returns
    + +
    +

    The accuracy classification of the AI's response as an AccResponse enum value.

    +
    @@ -271,42 +293,49 @@
    Parameters
    -
     85def resolve_x_refs(ref_name: str, context: str | None = None) -> str:
    - 86    """Replace all cross references by their actual values.
    - 87    :param ref_name: The cross-reference or variable name.
    - 88    :param context: The context to analyze the references.
    - 89    """
    - 90    template = ChatPromptTemplate.from_messages(
    - 91        [
    - 92            ("system", prompt.read_prompt("x-references")),
    - 93            MessagesPlaceholder("context", optional=True),
    - 94            ("human", "{pathname}"),
    - 95        ]
    - 96    )
    - 97    output = ref_name
    - 98    if context or (context := str(shared.context.flat("HISTORY"))):
    - 99        runnable = template | lc_llm.create_chat_model(Temperature.CODE_GENERATION.temp)
    -100        runnable = RunnableWithMessageHistory(
    -101            runnable, shared.context.flat, input_messages_key="pathname", history_messages_key="context"
    -102        )
    -103        log.info("Analysis::[QUERY] '%s'  context=%s", ref_name, context)
    -104        events.reply.emit(message=msg.x_reference(ref_name), verbosity="debug")
    -105        response = runnable.invoke({"pathname": ref_name}, config={"configurable": {"session_id": "HISTORY"}})
    -106        if response and (output := response.content) and shared.UNCERTAIN_ID != output:
    -107            output = response.content
    -108
    -109    return output
    +            
     86def resolve_x_refs(ref_name: str, context: str | None = None) -> str:
    + 87    """Replace all cross-references with their actual values.
    + 88    :param ref_name: The name of the cross-reference or variable to resolve.
    + 89    :param context: The context in which to analyze and resolve the references (optional).
    + 90    :return: The string with all cross-references replaced by their corresponding values.
    + 91    """
    + 92    template = ChatPromptTemplate.from_messages(
    + 93        [
    + 94            ("system", prompt.read_prompt("x-references")),
    + 95            MessagesPlaceholder("context", optional=True),
    + 96            ("human", "{pathname}"),
    + 97        ]
    + 98    )
    + 99    output = ref_name
    +100    if context or (context := str(shared.context.flat("HISTORY"))):
    +101        runnable = template | lc_llm.create_chat_model(Temperature.CODE_GENERATION.temp)
    +102        runnable = RunnableWithMessageHistory(
    +103            runnable, shared.context.flat, input_messages_key="pathname", history_messages_key="context"
    +104        )
    +105        log.info("Analysis::[QUERY] '%s'  context=%s", ref_name, context)
    +106        events.reply.emit(reply=AIReply.debug(msg.x_reference(ref_name)))
    +107        response = runnable.invoke({"pathname": ref_name}, config={"configurable": {"session_id": "HISTORY"}})
    +108        if response and (output := response.content) and shared.UNCERTAIN_ID != output:
    +109            output = response.content
    +110
    +111    return output
     
    -

    Replace all cross references by their actual values.

    +

    Replace all cross-references with their actual values.

    Parameters
      -
    • ref_name: The cross-reference or variable name.
    • -
    • context: The context to analyze the references.
    • +
    • ref_name: The name of the cross-reference or variable to resolve.
    • +
    • context: The context in which to analyze and resolve the references (optional).
    + +
    Returns
    + +
    +

    The string with all cross-references replaced by their corresponding values.

    +
    diff --git a/docs/api/askai/main/askai/core/features/router/task_agent.html b/docs/api/askai/main/askai/core/features/router/task_agent.html index 0820b363..44cef075 100644 --- a/docs/api/askai/main/askai/core/features/router/task_agent.html +++ b/docs/api/askai/main/askai/core/features/router/task_agent.html @@ -3,7 +3,7 @@ - + main.askai.core.features.router.task_agent API documentation @@ -39,15 +39,9 @@

    API Documentation

  • INSTANCE
  • -
  • - wrap_answer -
  • agent_template
  • -
  • - lc_agent -
  • invoke
  • @@ -75,168 +69,110 @@

    -
      1import logging as log
    -  2import os
    -  3from typing import Optional
    -  4
    -  5from hspylib.core.config.path_object import PathObject
    -  6from hspylib.core.metaclass.singleton import Singleton
    -  7from langchain.agents import AgentExecutor, create_structured_chat_agent
    -  8from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
    -  9from langchain_core.runnables import Runnable
    - 10from langchain_core.runnables.utils import Output
    - 11
    - 12from askai.core.askai_configs import configs
    - 13from askai.core.askai_events import events
    - 14from askai.core.askai_messages import msg
    - 15from askai.core.askai_prompt import prompt
    - 16from askai.core.engine.openai.temperature import Temperature
    - 17from askai.core.enums.acc_response import AccResponse
    - 18from askai.core.enums.routing_model import RoutingModel
    - 19from askai.core.features.router.task_toolkit import features
    - 20from askai.core.features.router.tools.general import final_answer
    - 21from askai.core.features.validation.accuracy import assert_accuracy
    - 22from askai.core.model.action_plan import ActionPlan
    - 23from askai.core.model.model_result import ModelResult
    - 24from askai.core.support.langchain_support import lc_llm
    - 25from askai.core.support.shared_instances import shared
    - 26
    - 27
    - 28class TaskAgent(metaclass=Singleton):
    - 29    """This Langchain agent is responsible for executing the routers tasks using the available tools."""
    - 30
    - 31    INSTANCE: "TaskAgent"
    - 32
    - 33    @staticmethod
    - 34    def wrap_answer(
    - 35        query: str,
    - 36        answer: str,
    - 37        model_result: ModelResult = ModelResult.default(),
    - 38        rag: AccResponse | None = None
    - 39    ) -> str:
    - 40        """Provide a final answer to the user.
    - 41        :param query: The user question.
    - 42        :param answer: The AI response.
    - 43        :param model_result: The selected routing model.
    - 44        :param rag: The final accuracy check (RAG) response.
    - 45        """
    - 46        output: str = answer
    - 47        model: RoutingModel = RoutingModel.of_model(model_result.mid)
    - 48        events.reply.emit(message=msg.model_select(str(model)), verbosity="debug")
    - 49        args = {'user': shared.username, 'idiom': shared.idiom, 'context': answer, 'question': query}
    - 50
    - 51        match model, configs.is_speak:
    - 52            case RoutingModel.TERMINAL_COMMAND, True:
    - 53                output = final_answer("taius-stt", [k for k in args.keys()], **args)
    - 54            case RoutingModel.ASSISTIVE_TECH_HELPER, _:
    - 55                output = final_answer("taius-stt", [k for k in args.keys()], **args)
    - 56            case RoutingModel.CHAT_MASTER, _:
    - 57                output = final_answer("taius-jarvis", [k for k in args.keys()], **args)
    - 58            case RoutingModel.REFINER, _:
    - 59                if rag:
    - 60                    ctx: str = str(shared.context.flat("HISTORY"))
    - 61                    args = {'improvements': rag.reasoning, 'context': ctx, 'response': answer, 'question': query}
    - 62                    output = final_answer("taius-refiner", [k for k in args.keys()], **args)
    - 63            case _:
    - 64                # Default is to leave the last AI response intact.
    - 65                pass
    - 66
    - 67        shared.context.push("HISTORY", query)
    - 68        shared.context.push("HISTORY", output, "assistant")
    - 69        shared.memory.save_context({"input": query}, {"output": output})
    - 70
    - 71        return output
    - 72
    - 73    def __init__(self):
    - 74        self._lc_agent: Runnable | None = None
    - 75
    - 76    @property
    - 77    def agent_template(self) -> ChatPromptTemplate:
    - 78        """Retrieve the Structured Agent Template.
    - 79        Ref: https://smith.langchain.com/hub/hwchase17/structured-chat-agent
    - 80        """
    - 81        prompt_file: PathObject = PathObject.of(prompt.append_path(f"langchain/structured-chat-agent"))
    - 82        final_prompt: str = prompt.read_prompt(prompt_file.filename, prompt_file.abs_dir)
    - 83        return ChatPromptTemplate.from_messages(
    - 84            [
    - 85                ("system", final_prompt),
    - 86                MessagesPlaceholder(variable_name="chat_history", optional=True),
    - 87                ("user", "{input}"),
    - 88                ("assistant", "{agent_scratchpad}"),
    - 89            ]
    - 90        )
    - 91
    - 92    @property
    - 93    def lc_agent(self) -> Runnable:
    - 94        return self._create_lc_agent()
    - 95
    - 96    def invoke(self, query: str, plan: ActionPlan) -> str:
    - 97        """Invoke the agent to respond the given query, using the specified action plan.
    - 98        :param query: The user question.
    - 99        :param plan: The AI action plan.
    -100        """
    -101        shared.context.push("HISTORY", query)
    -102        output: str = ""
    -103        accumulated: list[str] = []
    -104        if tasks := plan.tasks:
    -105            if plan.speak:
    -106                events.reply.emit(message=plan.speak)
    -107            for idx, action in enumerate(tasks, start=1):
    -108                path_str: str = 'Path: ' + action.path \
    -109                    if hasattr(action, 'path') and action.path.upper() not in ['N/A', 'NONE'] \
    -110                    else ''
    -111                task: str = f"{action.task}  {path_str}"
    -112                events.reply.emit(message=msg.task(task), verbosity="debug")
    -113                if (response := self._exec_action(task)) and (output := response["output"]):
    -114                    log.info("Router::[RESPONSE] Received from AI: \n%s.", output)
    -115                    if len(tasks) > 1:
    -116                        assert_accuracy(task, output, AccResponse.MODERATE)
    -117                        # Push intermediary steps to the chat history.
    -118                        shared.context.push("HISTORY", task, "assistant")
    -119                        shared.context.push("HISTORY", output, "assistant")
    -120                        shared.memory.save_context({"input": task}, {"output": output})
    -121                        if len(tasks) > idx:
    -122                            # Print intermediary steps.
    -123                            events.reply.emit(message=output)
    -124                    accumulated.append(output)
    -125                else:
    -126                    output = msg.no_output("AI")
    -127                    accumulated.append(output)
    -128        else:
    -129            output = plan.speak
    -130            accumulated.append(output)
    -131
    -132        if (rag := assert_accuracy(query, os.linesep.join(accumulated), AccResponse.MODERATE)).is_moderate:
    -133            plan.model.mid = RoutingModel.REFINER.model
    -134
    -135        return self.wrap_answer(plan.primary_goal, output, plan.model, rag)
    -136
    -137    def _create_lc_agent(self, temperature: Temperature = Temperature.CODE_GENERATION) -> Runnable:
    -138        """Create the LangChain agent.
    -139        :param temperature: The LLM temperature (randomness).
    -140        """
    -141        if self._lc_agent is None:
    -142            tools = features.tools()
    -143            llm = lc_llm.create_chat_model(temperature.temp)
    -144            chat_memory = shared.memory
    -145            lc_agent = create_structured_chat_agent(llm, tools, self.agent_template)
    -146            self._lc_agent: Runnable = AgentExecutor(
    -147                agent=lc_agent,
    -148                tools=tools,
    -149                max_iteraction=configs.max_router_retries,
    -150                memory=chat_memory,
    -151                handle_parsing_errors=True,
    -152                max_execution_time=configs.max_agent_execution_time_seconds,
    -153                verbose=configs.is_debug,
    -154            )
    -155
    -156        return self._lc_agent
    -157
    -158    def _exec_action(self, action: str) -> Optional[Output]:
    -159        return self.lc_agent.invoke({"input": action})
    -160
    -161
    -162assert (agent := TaskAgent().INSTANCE) is not None
    +                        
      1from askai.core.askai_configs import configs
    +  2from askai.core.askai_events import events
    +  3from askai.core.askai_messages import msg
    +  4from askai.core.askai_prompt import prompt
    +  5from askai.core.engine.openai.temperature import Temperature
    +  6from askai.core.enums.acc_response import AccResponse
    +  7from askai.core.features.router.task_accuracy import assert_accuracy
    +  8from askai.core.features.router.task_toolkit import features
    +  9from askai.core.model.ai_reply import AIReply
    + 10from askai.core.support.langchain_support import lc_llm
    + 11from askai.core.support.shared_instances import shared
    + 12from functools import lru_cache
    + 13from hspylib.core.config.path_object import PathObject
    + 14from hspylib.core.metaclass.singleton import Singleton
    + 15from langchain.agents import AgentExecutor, create_structured_chat_agent
    + 16from langchain.memory.chat_memory import BaseChatMemory
    + 17from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
    + 18from langchain_core.runnables import Runnable
    + 19from langchain_core.runnables.utils import Output
    + 20from typing import AnyStr, Optional
    + 21
    + 22import logging as log
    + 23
    + 24
    + 25class TaskAgent(metaclass=Singleton):
    + 26    """A LangChain agent responsible for executing router tasks using the available tools. This agent manages and
    + 27    performs tasks by leveraging various tools, ensuring efficient and accurate task execution in the routing process.
    + 28    """
    + 29
    + 30    INSTANCE: "TaskAgent"
    + 31
    + 32    def __init__(self):
    + 33        self._lc_agent: Runnable | None = None
    + 34
    + 35    @property
    + 36    def agent_template(self) -> ChatPromptTemplate:
    + 37        """Retrieve the Structured Agent Template for use in the chat agent. This template is used to structure the
    + 38        interactions of the chat agent.
    + 39        Reference: https://smith.langchain.com/hub/hwchase17/structured-chat-agent
    + 40        :return: An instance of ChatPromptTemplate representing the structured agent template.
    + 41        """
    + 42        prompt_file: PathObject = PathObject.of(prompt.append_path(f"langchain/structured-chat-agent"))
    + 43        final_prompt: str = prompt.read_prompt(prompt_file.filename, prompt_file.abs_dir)
    + 44        return ChatPromptTemplate.from_messages(
    + 45            [
    + 46                ("system", final_prompt),
    + 47                MessagesPlaceholder(variable_name="chat_history", optional=True),
    + 48                ("user", "{input}"),
    + 49                ("assistant", "{agent_scratchpad}"),
    + 50            ]
    + 51        )
    + 52
    + 53    def invoke(self, task: str) -> str:
    + 54        """Invoke the agent to respond to the given query using the specified action plan.
    + 55        :param task: The AI task that outlines the steps to generate the response.
    + 56        :return: The agent's response as a string.
    + 57        """
    + 58        events.reply.emit(reply=AIReply.debug(msg.task(task)))
    + 59        if (response := self._exec_task(task)) and (output := response["output"]):
    + 60            log.info("Router::[RESPONSE] Received from AI: \n%s.", output)
    + 61            shared.context.push("HISTORY", task, "assistant")
    + 62            shared.context.push("HISTORY", output, "assistant")
    + 63            shared.memory.save_context({"input": task}, {"output": output})
    + 64            assert_accuracy(task, output, AccResponse.MODERATE)
    + 65        else:
    + 66            output = msg.no_output("AI")
    + 67
    + 68        return output
    + 69
    + 70    @lru_cache
    + 71    def _create_lc_agent(self, temperature: Temperature = Temperature.COLDEST) -> Runnable:
    + 72        """Create and return a LangChain agent.
    + 73        :param temperature: The LLM temperature, which controls the randomness of the responses (default is
    + 74                            Temperature.COLDEST).
    + 75        :return: An instance of a Runnable representing the LangChain agent.
    + 76        """
    + 77        tools = features.tools()
    + 78        llm = lc_llm.create_chat_model(temperature.temp)
    + 79        chat_memory: BaseChatMemory = shared.memory
    + 80        lc_agent = create_structured_chat_agent(llm, tools, self.agent_template)
    + 81        self._lc_agent: Runnable = AgentExecutor(
    + 82            agent=lc_agent,
    + 83            tools=tools,
    + 84            max_iterations=configs.max_router_retries,
    + 85            memory=chat_memory,
    + 86            handle_parsing_errors=True,
    + 87            max_execution_time=configs.max_agent_execution_time_seconds,
    + 88            verbose=configs.is_debug,
    + 89        )
    + 90
    + 91        return self._lc_agent
    + 92
    + 93    @lru_cache
    + 94    def _exec_task(self, task: AnyStr) -> Optional[Output]:
    + 95        """Execute the specified agent task.
    + 96        :param task: The task to be executed by the agent.
    + 97        :return: An instance of Output containing the result of the task, or None if the task fails or produces
    + 98        no output.
    + 99        """
    +100        lc_agent: Runnable = self._create_lc_agent()
    +101        return lc_agent.invoke({"input": task})
    +102
    +103
    +104assert (agent := TaskAgent().INSTANCE) is not None
     
    @@ -252,142 +188,88 @@

    -
     29class TaskAgent(metaclass=Singleton):
    - 30    """This Langchain agent is responsible for executing the routers tasks using the available tools."""
    - 31
    - 32    INSTANCE: "TaskAgent"
    - 33
    - 34    @staticmethod
    - 35    def wrap_answer(
    - 36        query: str,
    - 37        answer: str,
    - 38        model_result: ModelResult = ModelResult.default(),
    - 39        rag: AccResponse | None = None
    - 40    ) -> str:
    - 41        """Provide a final answer to the user.
    - 42        :param query: The user question.
    - 43        :param answer: The AI response.
    - 44        :param model_result: The selected routing model.
    - 45        :param rag: The final accuracy check (RAG) response.
    - 46        """
    - 47        output: str = answer
    - 48        model: RoutingModel = RoutingModel.of_model(model_result.mid)
    - 49        events.reply.emit(message=msg.model_select(str(model)), verbosity="debug")
    - 50        args = {'user': shared.username, 'idiom': shared.idiom, 'context': answer, 'question': query}
    - 51
    - 52        match model, configs.is_speak:
    - 53            case RoutingModel.TERMINAL_COMMAND, True:
    - 54                output = final_answer("taius-stt", [k for k in args.keys()], **args)
    - 55            case RoutingModel.ASSISTIVE_TECH_HELPER, _:
    - 56                output = final_answer("taius-stt", [k for k in args.keys()], **args)
    - 57            case RoutingModel.CHAT_MASTER, _:
    - 58                output = final_answer("taius-jarvis", [k for k in args.keys()], **args)
    - 59            case RoutingModel.REFINER, _:
    - 60                if rag:
    - 61                    ctx: str = str(shared.context.flat("HISTORY"))
    - 62                    args = {'improvements': rag.reasoning, 'context': ctx, 'response': answer, 'question': query}
    - 63                    output = final_answer("taius-refiner", [k for k in args.keys()], **args)
    - 64            case _:
    - 65                # Default is to leave the last AI response intact.
    - 66                pass
    - 67
    - 68        shared.context.push("HISTORY", query)
    - 69        shared.context.push("HISTORY", output, "assistant")
    - 70        shared.memory.save_context({"input": query}, {"output": output})
    - 71
    - 72        return output
    - 73
    - 74    def __init__(self):
    - 75        self._lc_agent: Runnable | None = None
    - 76
    - 77    @property
    - 78    def agent_template(self) -> ChatPromptTemplate:
    - 79        """Retrieve the Structured Agent Template.
    - 80        Ref: https://smith.langchain.com/hub/hwchase17/structured-chat-agent
    - 81        """
    - 82        prompt_file: PathObject = PathObject.of(prompt.append_path(f"langchain/structured-chat-agent"))
    - 83        final_prompt: str = prompt.read_prompt(prompt_file.filename, prompt_file.abs_dir)
    - 84        return ChatPromptTemplate.from_messages(
    - 85            [
    - 86                ("system", final_prompt),
    - 87                MessagesPlaceholder(variable_name="chat_history", optional=True),
    - 88                ("user", "{input}"),
    - 89                ("assistant", "{agent_scratchpad}"),
    - 90            ]
    - 91        )
    - 92
    - 93    @property
    - 94    def lc_agent(self) -> Runnable:
    - 95        return self._create_lc_agent()
    - 96
    - 97    def invoke(self, query: str, plan: ActionPlan) -> str:
    - 98        """Invoke the agent to respond the given query, using the specified action plan.
    - 99        :param query: The user question.
    -100        :param plan: The AI action plan.
    -101        """
    -102        shared.context.push("HISTORY", query)
    -103        output: str = ""
    -104        accumulated: list[str] = []
    -105        if tasks := plan.tasks:
    -106            if plan.speak:
    -107                events.reply.emit(message=plan.speak)
    -108            for idx, action in enumerate(tasks, start=1):
    -109                path_str: str = 'Path: ' + action.path \
    -110                    if hasattr(action, 'path') and action.path.upper() not in ['N/A', 'NONE'] \
    -111                    else ''
    -112                task: str = f"{action.task}  {path_str}"
    -113                events.reply.emit(message=msg.task(task), verbosity="debug")
    -114                if (response := self._exec_action(task)) and (output := response["output"]):
    -115                    log.info("Router::[RESPONSE] Received from AI: \n%s.", output)
    -116                    if len(tasks) > 1:
    -117                        assert_accuracy(task, output, AccResponse.MODERATE)
    -118                        # Push intermediary steps to the chat history.
    -119                        shared.context.push("HISTORY", task, "assistant")
    -120                        shared.context.push("HISTORY", output, "assistant")
    -121                        shared.memory.save_context({"input": task}, {"output": output})
    -122                        if len(tasks) > idx:
    -123                            # Print intermediary steps.
    -124                            events.reply.emit(message=output)
    -125                    accumulated.append(output)
    -126                else:
    -127                    output = msg.no_output("AI")
    -128                    accumulated.append(output)
    -129        else:
    -130            output = plan.speak
    -131            accumulated.append(output)
    -132
    -133        if (rag := assert_accuracy(query, os.linesep.join(accumulated), AccResponse.MODERATE)).is_moderate:
    -134            plan.model.mid = RoutingModel.REFINER.model
    -135
    -136        return self.wrap_answer(plan.primary_goal, output, plan.model, rag)
    -137
    -138    def _create_lc_agent(self, temperature: Temperature = Temperature.CODE_GENERATION) -> Runnable:
    -139        """Create the LangChain agent.
    -140        :param temperature: The LLM temperature (randomness).
    -141        """
    -142        if self._lc_agent is None:
    -143            tools = features.tools()
    -144            llm = lc_llm.create_chat_model(temperature.temp)
    -145            chat_memory = shared.memory
    -146            lc_agent = create_structured_chat_agent(llm, tools, self.agent_template)
    -147            self._lc_agent: Runnable = AgentExecutor(
    -148                agent=lc_agent,
    -149                tools=tools,
    -150                max_iteraction=configs.max_router_retries,
    -151                memory=chat_memory,
    -152                handle_parsing_errors=True,
    -153                max_execution_time=configs.max_agent_execution_time_seconds,
    -154                verbose=configs.is_debug,
    -155            )
    -156
    -157        return self._lc_agent
    -158
    -159    def _exec_action(self, action: str) -> Optional[Output]:
    -160        return self.lc_agent.invoke({"input": action})
    +            
     26class TaskAgent(metaclass=Singleton):
    + 27    """A LangChain agent responsible for executing router tasks using the available tools. This agent manages and
    + 28    performs tasks by leveraging various tools, ensuring efficient and accurate task execution in the routing process.
    + 29    """
    + 30
    + 31    INSTANCE: "TaskAgent"
    + 32
    + 33    def __init__(self):
    + 34        self._lc_agent: Runnable | None = None
    + 35
    + 36    @property
    + 37    def agent_template(self) -> ChatPromptTemplate:
    + 38        """Retrieve the Structured Agent Template for use in the chat agent. This template is used to structure the
    + 39        interactions of the chat agent.
    + 40        Reference: https://smith.langchain.com/hub/hwchase17/structured-chat-agent
    + 41        :return: An instance of ChatPromptTemplate representing the structured agent template.
    + 42        """
    + 43        prompt_file: PathObject = PathObject.of(prompt.append_path(f"langchain/structured-chat-agent"))
    + 44        final_prompt: str = prompt.read_prompt(prompt_file.filename, prompt_file.abs_dir)
    + 45        return ChatPromptTemplate.from_messages(
    + 46            [
    + 47                ("system", final_prompt),
    + 48                MessagesPlaceholder(variable_name="chat_history", optional=True),
    + 49                ("user", "{input}"),
    + 50                ("assistant", "{agent_scratchpad}"),
    + 51            ]
    + 52        )
    + 53
    + 54    def invoke(self, task: str) -> str:
    + 55        """Invoke the agent to respond to the given query using the specified action plan.
    + 56        :param task: The AI task that outlines the steps to generate the response.
    + 57        :return: The agent's response as a string.
    + 58        """
    + 59        events.reply.emit(reply=AIReply.debug(msg.task(task)))
    + 60        if (response := self._exec_task(task)) and (output := response["output"]):
    + 61            log.info("Router::[RESPONSE] Received from AI: \n%s.", output)
    + 62            shared.context.push("HISTORY", task, "assistant")
    + 63            shared.context.push("HISTORY", output, "assistant")
    + 64            shared.memory.save_context({"input": task}, {"output": output})
    + 65            assert_accuracy(task, output, AccResponse.MODERATE)
    + 66        else:
    + 67            output = msg.no_output("AI")
    + 68
    + 69        return output
    + 70
    + 71    @lru_cache
    + 72    def _create_lc_agent(self, temperature: Temperature = Temperature.COLDEST) -> Runnable:
    + 73        """Create and return a LangChain agent.
    + 74        :param temperature: The LLM temperature, which controls the randomness of the responses (default is
    + 75                            Temperature.COLDEST).
    + 76        :return: An instance of a Runnable representing the LangChain agent.
    + 77        """
    + 78        tools = features.tools()
    + 79        llm = lc_llm.create_chat_model(temperature.temp)
    + 80        chat_memory: BaseChatMemory = shared.memory
    + 81        lc_agent = create_structured_chat_agent(llm, tools, self.agent_template)
    + 82        self._lc_agent: Runnable = AgentExecutor(
    + 83            agent=lc_agent,
    + 84            tools=tools,
    + 85            max_iterations=configs.max_router_retries,
    + 86            memory=chat_memory,
    + 87            handle_parsing_errors=True,
    + 88            max_execution_time=configs.max_agent_execution_time_seconds,
    + 89            verbose=configs.is_debug,
    + 90        )
    + 91
    + 92        return self._lc_agent
    + 93
    + 94    @lru_cache
    + 95    def _exec_task(self, task: AnyStr) -> Optional[Output]:
    + 96        """Execute the specified agent task.
    + 97        :param task: The task to be executed by the agent.
    + 98        :return: An instance of Output containing the result of the task, or None if the task fails or produces
    + 99        no output.
    +100        """
    +101        lc_agent: Runnable = self._create_lc_agent()
    +102        return lc_agent.invoke({"input": task})
     
    -

    This Langchain agent is responsible for executing the routers tasks using the available tools.

    +

    A LangChain agent responsible for executing router tasks using the available tools. This agent manages and +performs tasks by leveraging various tools, ensuring efficient and accurate task execution in the routing process.

    @@ -432,74 +314,6 @@

    -

    -
    - -
    -
    @staticmethod
    - - def - wrap_answer( query: str, answer: str, model_result: askai.core.model.model_result.ModelResult = ModelResult(mid='ASK_000', goal='Default model', reason='Provide the answer as received by the AI'), rag: askai.core.enums.acc_response.AccResponse | None = None) -> str: - - - -
    - -
    34    @staticmethod
    -35    def wrap_answer(
    -36        query: str,
    -37        answer: str,
    -38        model_result: ModelResult = ModelResult.default(),
    -39        rag: AccResponse | None = None
    -40    ) -> str:
    -41        """Provide a final answer to the user.
    -42        :param query: The user question.
    -43        :param answer: The AI response.
    -44        :param model_result: The selected routing model.
    -45        :param rag: The final accuracy check (RAG) response.
    -46        """
    -47        output: str = answer
    -48        model: RoutingModel = RoutingModel.of_model(model_result.mid)
    -49        events.reply.emit(message=msg.model_select(str(model)), verbosity="debug")
    -50        args = {'user': shared.username, 'idiom': shared.idiom, 'context': answer, 'question': query}
    -51
    -52        match model, configs.is_speak:
    -53            case RoutingModel.TERMINAL_COMMAND, True:
    -54                output = final_answer("taius-stt", [k for k in args.keys()], **args)
    -55            case RoutingModel.ASSISTIVE_TECH_HELPER, _:
    -56                output = final_answer("taius-stt", [k for k in args.keys()], **args)
    -57            case RoutingModel.CHAT_MASTER, _:
    -58                output = final_answer("taius-jarvis", [k for k in args.keys()], **args)
    -59            case RoutingModel.REFINER, _:
    -60                if rag:
    -61                    ctx: str = str(shared.context.flat("HISTORY"))
    -62                    args = {'improvements': rag.reasoning, 'context': ctx, 'response': answer, 'question': query}
    -63                    output = final_answer("taius-refiner", [k for k in args.keys()], **args)
    -64            case _:
    -65                # Default is to leave the last AI response intact.
    -66                pass
    -67
    -68        shared.context.push("HISTORY", query)
    -69        shared.context.push("HISTORY", output, "assistant")
    -70        shared.memory.save_context({"input": query}, {"output": output})
    -71
    -72        return output
    -
    - - -

    Provide a final answer to the user.

    - -
    Parameters
    - -
      -
    • query: The user question.
    • -
    • answer: The AI response.
    • -
    • model_result: The selected routing model.
    • -
    • rag: The final accuracy check (RAG) response.
    • -
    -
    - -
    @@ -510,46 +324,37 @@
    Parameters
    -
    77    @property
    -78    def agent_template(self) -> ChatPromptTemplate:
    -79        """Retrieve the Structured Agent Template.
    -80        Ref: https://smith.langchain.com/hub/hwchase17/structured-chat-agent
    -81        """
    -82        prompt_file: PathObject = PathObject.of(prompt.append_path(f"langchain/structured-chat-agent"))
    -83        final_prompt: str = prompt.read_prompt(prompt_file.filename, prompt_file.abs_dir)
    -84        return ChatPromptTemplate.from_messages(
    -85            [
    -86                ("system", final_prompt),
    -87                MessagesPlaceholder(variable_name="chat_history", optional=True),
    -88                ("user", "{input}"),
    -89                ("assistant", "{agent_scratchpad}"),
    -90            ]
    -91        )
    +            
    36    @property
    +37    def agent_template(self) -> ChatPromptTemplate:
    +38        """Retrieve the Structured Agent Template for use in the chat agent. This template is used to structure the
    +39        interactions of the chat agent.
    +40        Reference: https://smith.langchain.com/hub/hwchase17/structured-chat-agent
    +41        :return: An instance of ChatPromptTemplate representing the structured agent template.
    +42        """
    +43        prompt_file: PathObject = PathObject.of(prompt.append_path(f"langchain/structured-chat-agent"))
    +44        final_prompt: str = prompt.read_prompt(prompt_file.filename, prompt_file.abs_dir)
    +45        return ChatPromptTemplate.from_messages(
    +46            [
    +47                ("system", final_prompt),
    +48                MessagesPlaceholder(variable_name="chat_history", optional=True),
    +49                ("user", "{input}"),
    +50                ("assistant", "{agent_scratchpad}"),
    +51            ]
    +52        )
     
    -

    Retrieve the Structured Agent Template. -Ref: https://smith.langchain.com/hub/hwchase17/structured-chat-agent

    -
    - +

    Retrieve the Structured Agent Template for use in the chat agent. This template is used to structure the +interactions of the chat agent. +Reference: https://smith.langchain.com/hub/hwchase17/structured-chat-agent

    -
    -
    - -
    - lc_agent: langchain_core.runnables.base.Runnable - - - -
    - -
    93    @property
    -94    def lc_agent(self) -> Runnable:
    -95        return self._create_lc_agent()
    -
    +
    Returns
    +
    +

    An instance of ChatPromptTemplate representing the structured agent template.

    +
    +
    -
    @@ -557,63 +362,44 @@
    Parameters
    def - invoke(self, query: str, plan: askai.core.model.action_plan.ActionPlan) -> str: + invoke(self, task: str) -> str:
    -
     97    def invoke(self, query: str, plan: ActionPlan) -> str:
    - 98        """Invoke the agent to respond the given query, using the specified action plan.
    - 99        :param query: The user question.
    -100        :param plan: The AI action plan.
    -101        """
    -102        shared.context.push("HISTORY", query)
    -103        output: str = ""
    -104        accumulated: list[str] = []
    -105        if tasks := plan.tasks:
    -106            if plan.speak:
    -107                events.reply.emit(message=plan.speak)
    -108            for idx, action in enumerate(tasks, start=1):
    -109                path_str: str = 'Path: ' + action.path \
    -110                    if hasattr(action, 'path') and action.path.upper() not in ['N/A', 'NONE'] \
    -111                    else ''
    -112                task: str = f"{action.task}  {path_str}"
    -113                events.reply.emit(message=msg.task(task), verbosity="debug")
    -114                if (response := self._exec_action(task)) and (output := response["output"]):
    -115                    log.info("Router::[RESPONSE] Received from AI: \n%s.", output)
    -116                    if len(tasks) > 1:
    -117                        assert_accuracy(task, output, AccResponse.MODERATE)
    -118                        # Push intermediary steps to the chat history.
    -119                        shared.context.push("HISTORY", task, "assistant")
    -120                        shared.context.push("HISTORY", output, "assistant")
    -121                        shared.memory.save_context({"input": task}, {"output": output})
    -122                        if len(tasks) > idx:
    -123                            # Print intermediary steps.
    -124                            events.reply.emit(message=output)
    -125                    accumulated.append(output)
    -126                else:
    -127                    output = msg.no_output("AI")
    -128                    accumulated.append(output)
    -129        else:
    -130            output = plan.speak
    -131            accumulated.append(output)
    -132
    -133        if (rag := assert_accuracy(query, os.linesep.join(accumulated), AccResponse.MODERATE)).is_moderate:
    -134            plan.model.mid = RoutingModel.REFINER.model
    -135
    -136        return self.wrap_answer(plan.primary_goal, output, plan.model, rag)
    +            
    54    def invoke(self, task: str) -> str:
    +55        """Invoke the agent to respond to the given query using the specified action plan.
    +56        :param task: The AI task that outlines the steps to generate the response.
    +57        :return: The agent's response as a string.
    +58        """
    +59        events.reply.emit(reply=AIReply.debug(msg.task(task)))
    +60        if (response := self._exec_task(task)) and (output := response["output"]):
    +61            log.info("Router::[RESPONSE] Received from AI: \n%s.", output)
    +62            shared.context.push("HISTORY", task, "assistant")
    +63            shared.context.push("HISTORY", output, "assistant")
    +64            shared.memory.save_context({"input": task}, {"output": output})
    +65            assert_accuracy(task, output, AccResponse.MODERATE)
    +66        else:
    +67            output = msg.no_output("AI")
    +68
    +69        return output
     
    -

    Invoke the agent to respond the given query, using the specified action plan.

    +

    Invoke the agent to respond to the given query using the specified action plan.

    Parameters
      -
    • query: The user question.
    • -
    • plan: The AI action plan.
    • +
    • task: The AI task that outlines the steps to generate the response.
    + +
    Returns
    + +
    +

    The agent's response as a string.

    +
    diff --git a/docs/api/askai/main/askai/core/features/router/task_splitter.html b/docs/api/askai/main/askai/core/features/router/task_splitter.html deleted file mode 100644 index c38af3eb..00000000 --- a/docs/api/askai/main/askai/core/features/router/task_splitter.html +++ /dev/null @@ -1,658 +0,0 @@ - - - - - - - main.askai.core.features.router.task_splitter API documentation - - - - - - - - - -
    -
    -

    -main.askai.core.features.router.task_splitter

    - -

    @project: HsPyLib-AskAI -@package: askai.core.features.router - @file: task_splitter.py -@created: Mon, 01 Apr 2024 - @author: Hugo Saporetti Junior" - @site: https://github.com/yorevs/askai -@license: MIT - Please refer to https://opensource.org/licenses/MIT

    - -

    Copyright (c) 2024, HomeSetup

    -
    - - - - - -
      1#!/usr/bin/env python3
    -  2# -*- coding: utf-8 -*-
    -  3
    -  4"""
    -  5   @project: HsPyLib-AskAI
    -  6   @package: askai.core.features.router
    -  7      @file: task_splitter.py
    -  8   @created: Mon, 01 Apr 2024
    -  9    @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior"
    - 10      @site: https://github.com/yorevs/askai
    - 11   @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
    - 12
    - 13   Copyright (c) 2024, HomeSetup
    - 14"""
    - 15import logging as log
    - 16from pathlib import Path
    - 17from textwrap import dedent
    - 18from typing import Any, Optional, Type, TypeAlias
    - 19
    - 20import PIL
    - 21from hspylib.core.exception.exceptions import InvalidArgumentError
    - 22from hspylib.core.metaclass.singleton import Singleton
    - 23from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, PromptTemplate
    - 24from langchain_core.runnables.history import RunnableWithMessageHistory
    - 25from retry import retry
    - 26
    - 27from askai.core.askai_configs import configs
    - 28from askai.core.askai_events import AskAiEvents
    - 29from askai.core.askai_prompt import prompt
    - 30from askai.core.component.geo_location import geo_location
    - 31from askai.core.engine.openai.temperature import Temperature
    - 32from askai.core.features.router.model_selector import selector
    - 33from askai.core.features.router.task_agent import agent
    - 34from askai.core.model.action_plan import ActionPlan
    - 35from askai.core.model.model_result import ModelResult
    - 36from askai.core.support.langchain_support import lc_llm
    - 37from askai.core.support.shared_instances import shared
    - 38from askai.exception.exceptions import InaccurateResponse
    - 39
    - 40AgentResponse: TypeAlias = dict[str, Any]
    - 41
    - 42
    - 43class TaskSplitter(metaclass=Singleton):
    - 44    """Processor to provide a divide and conquer set of tasks to fulfill an objective. This is responsible for the
    - 45    orchestration and execution of the smaller tasks."""
    - 46
    - 47    INSTANCE: "TaskSplitter"
    - 48
    - 49    HUMAN_PROMPT: str = dedent(
    - 50        """
    - 51        My Question: "{input}"
    - 52        """
    - 53    )
    - 54
    - 55    # Allow the router to retry on the errors bellow.
    - 56    RETRIABLE_ERRORS: tuple[Type[Exception], ...] = (
    - 57        InaccurateResponse,
    - 58        InvalidArgumentError,
    - 59        ValueError,
    - 60        AttributeError,
    - 61        PIL.UnidentifiedImageError,
    - 62    )
    - 63
    - 64    def __init__(self):
    - 65        self._approved: bool = False
    - 66
    - 67    @property
    - 68    def template(self) -> ChatPromptTemplate:
    - 69        """Retrieve the Router Template."""
    - 70
    - 71        rag: str = str(shared.context.flat("RAG"))
    - 72        template = PromptTemplate(
    - 73            input_variables=["os_type", "shell", "datetime", "home"],
    - 74            template=prompt.read_prompt("task-split.txt"))
    - 75        return ChatPromptTemplate.from_messages(
    - 76            [
    - 77                (
    - 78                    "system",
    - 79                    template.format(
    - 80                        os_type=prompt.os_type, shell=prompt.shell, datetime=geo_location.datetime, home=Path.home()
    - 81                    ),
    - 82                ),
    - 83                MessagesPlaceholder("chat_history", optional=True),
    - 84                ("assistant", rag),
    - 85                ("human", self.HUMAN_PROMPT),
    - 86            ]
    - 87        )
    - 88
    - 89    def process(self, query: str) -> Optional[str]:
    - 90        """Process the user query and retrieve the final response.
    - 91        :param query: The user query to complete.
    - 92        """
    - 93
    - 94        shared.context.forget("RAG")
    - 95        model: ModelResult = selector.select_model(query)
    - 96
    - 97        @retry(exceptions=self.RETRIABLE_ERRORS, tries=configs.max_router_retries, backoff=0)
    - 98        def _process_wrapper() -> Optional[str]:
    - 99            """Wrapper to allow RAG retries."""
    -100            log.info("Router::[QUESTION] '%s'", query)
    -101            runnable = self.template | lc_llm.create_chat_model(Temperature.CODE_GENERATION.temp)
    -102            runnable = RunnableWithMessageHistory(
    -103                runnable, shared.context.flat, input_messages_key="input", history_messages_key="chat_history"
    -104            )
    -105            if response := runnable.invoke({"input": query}, config={"configurable": {"session_id": "HISTORY"}}):
    -106                log.info("Router::[RESPONSE] Received from AI: \n%s.", str(response.content))
    -107                plan: ActionPlan = ActionPlan.create(query, response, model)
    -108                AskAiEvents.ASKAI_BUS.events.reply.emit(message=f"> {plan}", verbosity="debug")
    -109                output = agent.invoke(query, plan)
    -110            else:
    -111                # Most of the times, this indicates a failure.
    -112                output = response
    -113            return output
    -114
    -115        return _process_wrapper()
    -116
    -117
    -118assert (splitter := TaskSplitter().INSTANCE) is not None
    -
    - - -
    -
    -
    - AgentResponse: TypeAlias = -dict[str, typing.Any] - - -
    - - - - -
    -
    - -
    - - class - TaskSplitter(typing.Type): - - - -
    - -
     44class TaskSplitter(metaclass=Singleton):
    - 45    """Processor to provide a divide and conquer set of tasks to fulfill an objective. This is responsible for the
    - 46    orchestration and execution of the smaller tasks."""
    - 47
    - 48    INSTANCE: "TaskSplitter"
    - 49
    - 50    HUMAN_PROMPT: str = dedent(
    - 51        """
    - 52        My Question: "{input}"
    - 53        """
    - 54    )
    - 55
    - 56    # Allow the router to retry on the errors bellow.
    - 57    RETRIABLE_ERRORS: tuple[Type[Exception], ...] = (
    - 58        InaccurateResponse,
    - 59        InvalidArgumentError,
    - 60        ValueError,
    - 61        AttributeError,
    - 62        PIL.UnidentifiedImageError,
    - 63    )
    - 64
    - 65    def __init__(self):
    - 66        self._approved: bool = False
    - 67
    - 68    @property
    - 69    def template(self) -> ChatPromptTemplate:
    - 70        """Retrieve the Router Template."""
    - 71
    - 72        rag: str = str(shared.context.flat("RAG"))
    - 73        template = PromptTemplate(
    - 74            input_variables=["os_type", "shell", "datetime", "home"],
    - 75            template=prompt.read_prompt("task-split.txt"))
    - 76        return ChatPromptTemplate.from_messages(
    - 77            [
    - 78                (
    - 79                    "system",
    - 80                    template.format(
    - 81                        os_type=prompt.os_type, shell=prompt.shell, datetime=geo_location.datetime, home=Path.home()
    - 82                    ),
    - 83                ),
    - 84                MessagesPlaceholder("chat_history", optional=True),
    - 85                ("assistant", rag),
    - 86                ("human", self.HUMAN_PROMPT),
    - 87            ]
    - 88        )
    - 89
    - 90    def process(self, query: str) -> Optional[str]:
    - 91        """Process the user query and retrieve the final response.
    - 92        :param query: The user query to complete.
    - 93        """
    - 94
    - 95        shared.context.forget("RAG")
    - 96        model: ModelResult = selector.select_model(query)
    - 97
    - 98        @retry(exceptions=self.RETRIABLE_ERRORS, tries=configs.max_router_retries, backoff=0)
    - 99        def _process_wrapper() -> Optional[str]:
    -100            """Wrapper to allow RAG retries."""
    -101            log.info("Router::[QUESTION] '%s'", query)
    -102            runnable = self.template | lc_llm.create_chat_model(Temperature.CODE_GENERATION.temp)
    -103            runnable = RunnableWithMessageHistory(
    -104                runnable, shared.context.flat, input_messages_key="input", history_messages_key="chat_history"
    -105            )
    -106            if response := runnable.invoke({"input": query}, config={"configurable": {"session_id": "HISTORY"}}):
    -107                log.info("Router::[RESPONSE] Received from AI: \n%s.", str(response.content))
    -108                plan: ActionPlan = ActionPlan.create(query, response, model)
    -109                AskAiEvents.ASKAI_BUS.events.reply.emit(message=f"> {plan}", verbosity="debug")
    -110                output = agent.invoke(query, plan)
    -111            else:
    -112                # Most of the times, this indicates a failure.
    -113                output = response
    -114            return output
    -115
    -116        return _process_wrapper()
    -
    - - -

    Processor to provide a divide and conquer set of tasks to fulfill an objective. This is responsible for the -orchestration and execution of the smaller tasks.

    -
    - - -
    - -
    - - TaskSplitter(*args, **kwargs) - - - -
    - -
    35    def __call__(mcs, *args, **kwargs) -> Any:
    -36        """Invoke the class constructor or return the instance if it exists."""
    -37        if not Singleton.has_instance(mcs):
    -38            try:
    -39                instance = super().__call__(*args, **kwargs)
    -40                check_not_none(instance, f"Unable to create Singleton instance: {mcs}")
    -41                setattr(mcs, "INSTANCE", instance)
    -42                Singleton._instances[mcs.__name__] = instance
    -43                log.debug("Created a new Singleton instance: %s.%s", mcs.__module__, mcs.__name__)
    -44                return instance
    -45            except Exception as err:
    -46                raise HSBaseException(f"Failed to create singleton instance: '{mcs.__name__}'", err) from err
    -47        return Singleton._instances[mcs.__name__]
    -
    - - -

    Invoke the class constructor or return the instance if it exists.

    -
    - - -
    -
    -
    - INSTANCE: TaskSplitter - - -
    - - - - -
    -
    -
    - HUMAN_PROMPT: str = -'\nMy Question: "{input}"\n' - - -
    - - - - -
    -
    -
    - RETRIABLE_ERRORS: tuple[typing.Type[Exception], ...] = - - (<class 'askai.exception.exceptions.InaccurateResponse'>, <class 'hspylib.core.exception.exceptions.InvalidArgumentError'>, <class 'ValueError'>, <class 'AttributeError'>, <class 'PIL.UnidentifiedImageError'>) - - -
    - - - - -
    -
    - -
    - template: langchain_core.prompts.chat.ChatPromptTemplate - - - -
    - -
    68    @property
    -69    def template(self) -> ChatPromptTemplate:
    -70        """Retrieve the Router Template."""
    -71
    -72        rag: str = str(shared.context.flat("RAG"))
    -73        template = PromptTemplate(
    -74            input_variables=["os_type", "shell", "datetime", "home"],
    -75            template=prompt.read_prompt("task-split.txt"))
    -76        return ChatPromptTemplate.from_messages(
    -77            [
    -78                (
    -79                    "system",
    -80                    template.format(
    -81                        os_type=prompt.os_type, shell=prompt.shell, datetime=geo_location.datetime, home=Path.home()
    -82                    ),
    -83                ),
    -84                MessagesPlaceholder("chat_history", optional=True),
    -85                ("assistant", rag),
    -86                ("human", self.HUMAN_PROMPT),
    -87            ]
    -88        )
    -
    - - -

    Retrieve the Router Template.

    -
    - - -
    -
    - -
    - - def - process(self, query: str) -> Optional[str]: - - - -
    - -
     90    def process(self, query: str) -> Optional[str]:
    - 91        """Process the user query and retrieve the final response.
    - 92        :param query: The user query to complete.
    - 93        """
    - 94
    - 95        shared.context.forget("RAG")
    - 96        model: ModelResult = selector.select_model(query)
    - 97
    - 98        @retry(exceptions=self.RETRIABLE_ERRORS, tries=configs.max_router_retries, backoff=0)
    - 99        def _process_wrapper() -> Optional[str]:
    -100            """Wrapper to allow RAG retries."""
    -101            log.info("Router::[QUESTION] '%s'", query)
    -102            runnable = self.template | lc_llm.create_chat_model(Temperature.CODE_GENERATION.temp)
    -103            runnable = RunnableWithMessageHistory(
    -104                runnable, shared.context.flat, input_messages_key="input", history_messages_key="chat_history"
    -105            )
    -106            if response := runnable.invoke({"input": query}, config={"configurable": {"session_id": "HISTORY"}}):
    -107                log.info("Router::[RESPONSE] Received from AI: \n%s.", str(response.content))
    -108                plan: ActionPlan = ActionPlan.create(query, response, model)
    -109                AskAiEvents.ASKAI_BUS.events.reply.emit(message=f"> {plan}", verbosity="debug")
    -110                output = agent.invoke(query, plan)
    -111            else:
    -112                # Most of the times, this indicates a failure.
    -113                output = response
    -114            return output
    -115
    -116        return _process_wrapper()
    -
    - - -

    Process the user query and retrieve the final response.

    - -
    Parameters
    - -
      -
    • query: The user query to complete.
    • -
    -
    - - -
    -
    -
    - - \ No newline at end of file diff --git a/docs/api/askai/main/askai/core/features/router/task_toolkit.html b/docs/api/askai/main/askai/core/features/router/task_toolkit.html index ba86f216..bc505528 100644 --- a/docs/api/askai/main/askai/core/features/router/task_toolkit.html +++ b/docs/api/askai/main/askai/core/features/router/task_toolkit.html @@ -3,7 +3,7 @@ - + main.askai.core.features.router.task_toolkit API documentation @@ -55,13 +55,16 @@

    API Documentation

    image_captioner
  • - generate_content + webcam_capturer
  • - save_content + webcam_identifier
  • - display_tool + generate_content +
  • +
  • + save_content
  • direct_answer @@ -130,169 +133,205 @@

    13 Copyright (c) 2024, HomeSetup 14""" 15from askai.core.askai_messages import msg - 16from askai.core.features.router.tools.analysis import query_output - 17from askai.core.features.router.tools.browser import browse - 18from askai.core.features.router.tools.general import display_tool, final_answer - 19from askai.core.features.router.tools.generation import generate_content, save_content - 20from askai.core.features.router.tools.summarization import summarize - 21from askai.core.features.router.tools.terminal import execute_command, list_contents, open_command - 22from askai.core.features.router.tools.vision import image_captioner - 23from askai.core.support.shared_instances import shared + 16from askai.core.features.tools.analysis import query_output + 17from askai.core.features.tools.browser import browse + 18from askai.core.features.tools.general import display_tool + 19from askai.core.features.tools.generation import generate_content, save_content + 20from askai.core.features.tools.summarization import summarize + 21from askai.core.features.tools.terminal import execute_command, list_contents, open_command + 22from askai.core.features.tools.vision import image_captioner, parse_caption + 23from askai.core.features.tools.webcam import webcam_capturer, webcam_identifier 24from askai.exception.exceptions import TerminatingQuery 25from clitt.core.tui.line_input.line_input import line_input 26from functools import lru_cache 27from hspylib.core.metaclass.classpath import AnyPath 28from hspylib.core.metaclass.singleton import Singleton - 29from langchain_core.tools import BaseTool, StructuredTool - 30from textwrap import dedent - 31from typing import Callable - 32 - 33import inspect - 34import logging as log - 35 + 29from hspylib.core.tools.text_tools import ensure_endswith, ensure_startswith + 30from langchain_core.tools import BaseTool, StructuredTool + 31from textwrap import dedent + 32from typing import Callable, Optional + 33 + 34import inspect + 35import logging as log 36 - 37class AgentToolkit(metaclass=Singleton): - 38 """This class provides the AskAI task agent tools.""" - 39 - 40 INSTANCE: "AgentToolkit" - 41 - 42 RESERVED: list[str] = ["tools"] - 43 - 44 def __init__(self): - 45 self._all: dict[str, Callable] = dict( - 46 filter( - 47 lambda pair: pair[0] not in self.RESERVED and not pair[0].startswith("_"), - 48 {n: fn for n, fn in inspect.getmembers(self, predicate=inspect.ismethod)}.items(), - 49 ) - 50 ) - 51 - 52 @lru_cache - 53 def tools(self) -> list[BaseTool]: - 54 """Return a list of Langchain base tools.""" - 55 tools: list[BaseTool] = [self._create_structured_tool(v) for _, v in self._all.items()] - 56 - 57 log.debug("Available tools: are: '%s'", tools) - 58 - 59 return tools - 60 - 61 def _human_approval(self) -> bool: - 62 """Prompt for human approval.""" - 63 confirm_msg = msg.access_grant() - 64 if (resp := line_input(confirm_msg).lower()) not in ("yes", "y"): - 65 raise ValueError(f"Terminal command execution was not approved '{resp}' !") - 66 self._approved = True - 67 - 68 return self._approved - 69 - 70 def _create_structured_tool(self, fn: Callable) -> BaseTool: - 71 """Create the LangChain agent tool. - 72 :param fn: The function that provides the tool implementation. - 73 """ - 74 return StructuredTool.from_function( - 75 func=fn, - 76 name=fn.__name__, - 77 description=f"```{dedent(fn.__doc__)}```\n\n" if fn and fn.__doc__ else "", - 78 return_direct=True, - 79 ) - 80 - 81 def browse(self, search_query: str) -> str: - 82 """Use this tool to browse the internet or to stay informed about the latest news and current events, especially when you require up-to-date information quickly. It is especially effective for accessing the most recent data available online. - 83 Usage: `browse(search_query)` - 84 :param search_query: The web search query in string format. - 85 """ - 86 return browse(search_query) - 87 - 88 def query_output(self, output_query: str) -> str: - 89 """Use this tool to analyze textual content to identify the presence of files, folders, and applications. It is designed to process and analyze content that is already available in textual form, but not to read or extract file contents directly. - 90 Usage: `query_output(query)` - 91 :param output_query: The query regarding the output. Prefer using "Identify <file types or name or textual content>". - 92 """ - 93 return query_output(output_query) - 94 - 95 def image_captioner(self, image_path: str) -> str: - 96 """Use this tool to provide a textual description of a visual content, such as, image files. - 97 Usage: image_captioner(image_path) - 98 :param image_path: The absolute path of the image file to be analyzed. - 99 """ -100 return image_captioner(image_path) -101 -102 def generate_content(self, instructions: str, mime_type: str, filepath: AnyPath) -> str: -103 """Use this tool for tasks that require generating any kind of content, such as, code and text, image, etc. -104 Usage: generate_content(instructions, mime_type) -105 :param instructions: The instructions for generating the content. -106 :param mime_type: The generated content type (use MIME types). -107 :param filepath: Optional file path for saving the content. -108 """ -109 return generate_content(instructions, mime_type, filepath) -110 -111 def save_content(self, filepath: AnyPath) -> str: -112 """Use this tool to save generated content into disk (program, script, text, image, etc). -113 Usage: save_content(filepath) -114 :param filepath: The path where you want to save the content. -115 """ -116 return save_content(filepath) + 37 + 38class AgentToolkit(metaclass=Singleton): + 39 """This class serves as the toolkit for AskAI task agents, providing essential tools and functionalities required + 40 for their tasks. + 41 """ + 42 + 43 INSTANCE: "AgentToolkit" + 44 + 45 RESERVED: list[str] = ["tools"] + 46 + 47 def __init__(self): + 48 self._all: dict[str, Callable] = dict( + 49 filter( + 50 lambda pair: pair[0] not in self.RESERVED and not pair[0].startswith("_"), + 51 {n: fn for n, fn in inspect.getmembers(self, predicate=inspect.ismethod)}.items(), + 52 ) + 53 ) + 54 + 55 @lru_cache + 56 def tools(self) -> list[BaseTool]: + 57 """Return a cached list of LangChain base tools. + 58 :return: A list of BaseTool's instances available for use. + 59 """ + 60 tools: list[BaseTool] = [self._create_structured_tool(v) for _, v in self._all.items()] + 61 + 62 log.debug("Available tools: are: '%s'", tools) + 63 + 64 return tools + 65 + 66 def _human_approval(self) -> bool: + 67 """Prompt for human approval.""" + 68 confirm_msg = msg.access_grant() + 69 if (resp := line_input(confirm_msg).lower()) not in ("yes", "y"): + 70 raise ValueError(f"Terminal command execution was not approved '{resp}' !") + 71 self._approved = True + 72 + 73 return self._approved + 74 + 75 def _create_structured_tool(self, fn: Callable) -> BaseTool: + 76 """Create a LangChain agent tool based on the provided function. + 77 :param fn: The function that implements the tool's behavior. + 78 :return: An instance of BaseTool configured with the provided function. + 79 """ + 80 return StructuredTool.from_function( + 81 func=fn, + 82 name=fn.__name__, + 83 description=f"```{dedent(fn.__doc__)}```\n\n" if fn and fn.__doc__ else "", + 84 return_direct=True, + 85 ) + 86 + 87 def browse(self, search_query: str) -> Optional[str]: + 88 """Use this tool to browse the internet for the latest news and current events, particularly when up-to-date + 89 information is needed quickly. This tool is especially effective for accessing the most recent data available + 90 online. + 91 Usage: `browse(search_query)` + 92 :param search_query: The web search query as a string. + 93 :return: A string containing the results of the web search, or None if no relevant results are found. + 94 """ + 95 return browse(search_query) + 96 + 97 def query_output(self, output_query: str) -> str: + 98 """Use this tool to analyze textual content and identify the presence of files, folders, and applications. This + 99 tool is designed to process and analyze content that is already available in textual form, but it does not +100 directly read or extract file contents. +101 Usage: `query_output(output_query)` +102 :param output_query: The query regarding the output. Use "Identify <file types, names, or textual content>". +103 :return: A string containing the results of the analysis based on the query. +104 """ +105 return query_output(output_query) +106 +107 def image_captioner(self, image_path: str) -> str: +108 """Use this tool to generate a textual description of visual content, such as image files. +109 Usage: `image_captioner(image_path)` +110 :param image_path: The absolute path of the image file to be analyzed. +111 :return: A string containing the generated caption describing the image. +112 """ +113 return ensure_endswith( +114 ensure_startswith(parse_caption(image_captioner(image_path)), f"\n>  Description of '{image_path}':\n"), +115 "\n", +116 ) 117 -118 def display_tool(self, texts: list[str] | str) -> str: -119 """ -120 Name: 'Display Tool' -121 Description: Use this tool to display textual information. -122 Usage: 'display_tool(text, ...repeat N times)' -123 :param texts: The comma separated list of texts to be displayed. -124 """ -125 return display_tool(*(texts if isinstance(texts, list) else [texts])) -126 -127 def direct_answer(self, question: str, answer: str) -> str: -128 """Use this tool to execute terminal commands or process user-provided commands." -129 Usage: 'direct_answer(answer)' -130 :param question: The original user question. -131 :param answer: Your direct answer to the user. -132 """ -133 args = {'user': shared.username, 'idiom': shared.idiom, 'context': answer, 'question': question} -134 return final_answer("taius-jarvis", [k for k in args.keys()], **args) +118 def webcam_capturer(self, photo_name: str | None, detect_faces: bool = False) -> str: +119 """Capture a photo using the webcam, and save it locally. This tool is useful for taking photos, detect people's +120 faces, and, describing what is in front of the webcam. +121 Usage: `webcam_capturer(photo_name, detect_faces)` +122 :param photo_name: The name of the photo file (without the extension). If None, a default name will be used. +123 :param detect_faces: Whether to detect and describe all faces in the photo (default is False). +124 :return: The file path of the saved JPEG image. +125 """ +126 return webcam_capturer(photo_name, detect_faces) +127 +128 def webcam_identifier(self) -> str: +129 """Identify the person in front of the webcam using a pre-stored set of faces and provide a description. This +130 tool is useful for recognizing individuals and generating descriptions based on pre-stored facial data. +131 Usage: `webcam_identifier()` +132 :return: A string containing the identification and description of the person. +133 """ +134 return webcam_identifier() 135 -136 def list_tool(self, folder: str, filters: str | None = None) -> str: -137 """ -138 Name: 'List Folder' -139 Description: Use this tool to access the contents of a specified folder. -140 Usage: 'list_tool(folder, filters)' -141 :param folder: The absolute path of the folder whose contents you wish to list or access. -142 :param filters: Optional Parameter: Specify a comma-separated list of file glob to filter the results (e.g., "*.*, *.txt"). -143 """ -144 return list_contents(folder, filters) -145 -146 def open_tool(self, path_name: str) -> str: -147 """Use this tool to open, read (show) content of files, and also, to playback any media files. -148 Usage: 'open_tool(path_name)' -149 :param path_name: The absolute file, folder, or application path name. -150 """ -151 return open_command(path_name) -152 -153 def summarize(self, folder: str, glob: str) -> str: -154 """Use this tool only when the user explicitly requests a summary of files and folders. -155 Usage: summarize(folder_name, glob) -156 :param folder: The base name of the folder containing the files to be summarized. -157 :param glob: The glob expression to specify files to be included for summarization. -158 """ -159 return summarize(folder, glob) -160 -161 def terminal(self, shell_type: str, command: str) -> str: -162 """Use this tool to execute terminal commands or process user-provided commands." -163 Usage: 'terminal(shell_type, command)' -164 :param shell_type: The type of the shell (e.g. bash, zsh, powershell, etc). -165 :param command: The actual commands you wish to execute in the terminal. -166 """ -167 # TODO Check for permission before executing -168 return execute_command(shell_type, command) -169 -170 def shutdown(self, reason: str) -> None: -171 """Use this tool when the user provides a query that indicates he wants to conclude the interaction (e.g. bye, exit). -172 Usage: 'shutdown(reason)' -173 :param reason: The reason for termination. -174 """ -175 raise TerminatingQuery(reason) -176 -177 -178assert (features := AgentToolkit().INSTANCE) is not None +136 def generate_content(self, instructions: str, mime_type: str, filepath: AnyPath) -> str: +137 """Use this tool to generate various types of content, such as code, text, images, etc. This tool processes +138 descriptive instructions to create the specified content type and can optionally save it to a file. +139 Usage: `generate_content(instructions, mime_type, filepath)` +140 :param instructions: Descriptive instructions on how to create the content (not the content itself). +141 :param mime_type: The MIME type representing the type of content to generate. +142 :param filepath: The optional file path where the generated content will be saved. +143 :return: The generated content as a string. +144 """ +145 return generate_content(instructions, mime_type, filepath) +146 +147 def save_content(self, filepath: AnyPath) -> str: +148 """Save previously generated content to disk, such as programs, scripts, text, images, etc. This tool is used +149 to persist content that was generated using the `generate_content` tool. +150 Usage: `save_content(filepath)` +151 :param filepath: The path where you want to save the content. +152 :return: The absolute path name of the saved file. +153 """ +154 return save_content(filepath) +155 +156 def direct_answer(self, texts: list[str] | str) -> str: +157 """Provide and display text as a direct answer to the user. This tool is used to present one or more pieces of +158 text directly to the user. +159 Usage: `direct_answer(text, ...repeat N times)` +160 :param texts: A list of texts or a single text string to be displayed. +161 :return: A string containing all provided texts concatenated together. +162 """ +163 return display_tool(*(texts if isinstance(texts, list) else [texts])) +164 +165 def list_tool(self, folder: str, filters: str | None = None) -> str: +166 """Access and list the contents of a specified folder. This tool is used to retrieve the contents of a folder, +167 optionally filtering the results based on specified criteria. +168 Usage: `list_tool(folder, filters)` +169 :param folder: The absolute path of the folder whose contents you wish to list or access. +170 :param filters: Optional parameter: A comma-separated list of file globs to filter results (e.g., "*.*, *.txt"). +171 :return: A string listing the contents of the folder, filtered by the provided criteria if applicable. +172 """ +173 return list_contents(folder, filters) +174 +175 def open_tool(self, path_name: str) -> str: +176 """Open and display the content of files, or playback media files, and also execute applications. This tool is +177 used to open a file, folder, or application, read its contents, or play back media files. +178 Usage: `open_tool(path_name)` +179 :param path_name: The absolute path of the file, folder, or application to be opened. +180 :return: The output generated by the open command, such as file content or playback status. +181 """ +182 return open_command(path_name) +183 +184 def summarize(self, folder: str, glob: str) -> str: +185 """Summarize the contents of files and folders based on user requests. This tool should be used only when the +186 user explicitly requests a summary of files and folders, not for summarizing textual content. +187 Usage: `summarize(folder, glob)` +188 :param folder: The base path of the folder containing the files to be summarized. +189 :param glob: The glob expression to specify which files should be included in the summary. +190 :return: A string containing the summary of the specified files and folders. +191 """ +192 return summarize(folder, glob) +193 +194 def terminal(self, shell_type: str, command: str) -> str: +195 """Execute terminal commands or process user-provided commands using the specified shell. This tool is used to +196 run commands in a terminal environment based on the provided shell type. +197 Usage: `terminal(shell_type, command)` +198 :param shell_type: The type of shell to use (e.g., bash, zsh, powershell, etc.). +199 :param command: The command or set of commands to execute in the terminal. +200 :return: The output of the executed terminal command(s) as a string. +201 """ +202 # TODO Check for permission before executing +203 return execute_command(shell_type, command) +204 +205 def shutdown(self, reason: str) -> None: +206 """Conclude the interaction based on the user's intent to end the session (e.g., bye, exit). This tool is used +207 to gracefully shut down the interaction when the user indicates a desire to terminate the session. +208 Usage: `shutdown(reason)` +209 :param reason: The reason for terminating the session. +210 """ +211 raise TerminatingQuery(reason) +212 +213 +214assert (features := AgentToolkit().INSTANCE) is not None

  • @@ -308,149 +347,185 @@

    -
     38class AgentToolkit(metaclass=Singleton):
    - 39    """This class provides the AskAI task agent tools."""
    - 40
    - 41    INSTANCE: "AgentToolkit"
    - 42
    - 43    RESERVED: list[str] = ["tools"]
    - 44
    - 45    def __init__(self):
    - 46        self._all: dict[str, Callable] = dict(
    - 47            filter(
    - 48                lambda pair: pair[0] not in self.RESERVED and not pair[0].startswith("_"),
    - 49                {n: fn for n, fn in inspect.getmembers(self, predicate=inspect.ismethod)}.items(),
    - 50            )
    - 51        )
    - 52
    - 53    @lru_cache
    - 54    def tools(self) -> list[BaseTool]:
    - 55        """Return a list of Langchain base tools."""
    - 56        tools: list[BaseTool] = [self._create_structured_tool(v) for _, v in self._all.items()]
    - 57
    - 58        log.debug("Available tools: are: '%s'", tools)
    - 59
    - 60        return tools
    - 61
    - 62    def _human_approval(self) -> bool:
    - 63        """Prompt for human approval."""
    - 64        confirm_msg = msg.access_grant()
    - 65        if (resp := line_input(confirm_msg).lower()) not in ("yes", "y"):
    - 66            raise ValueError(f"Terminal command execution was not approved '{resp}' !")
    - 67        self._approved = True
    - 68
    - 69        return self._approved
    - 70
    - 71    def _create_structured_tool(self, fn: Callable) -> BaseTool:
    - 72        """Create the LangChain agent tool.
    - 73        :param fn: The function that provides the tool implementation.
    - 74        """
    - 75        return StructuredTool.from_function(
    - 76            func=fn,
    - 77            name=fn.__name__,
    - 78            description=f"```{dedent(fn.__doc__)}```\n\n" if fn and fn.__doc__ else "",
    - 79            return_direct=True,
    - 80        )
    - 81
    - 82    def browse(self, search_query: str) -> str:
    - 83        """Use this tool to browse the internet or to stay informed about the latest news and current events, especially when you require up-to-date information quickly. It is especially effective for accessing the most recent data available online.
    - 84        Usage: `browse(search_query)`
    - 85        :param search_query: The web search query in string format.
    - 86        """
    - 87        return browse(search_query)
    - 88
    - 89    def query_output(self, output_query: str) -> str:
    - 90        """Use this tool to analyze textual content to identify the presence of files, folders, and applications. It is designed to process and analyze content that is already available in textual form, but not to read or extract file contents directly.
    - 91        Usage: `query_output(query)`
    - 92        :param output_query: The query regarding the output. Prefer using "Identify <file types or name or textual content>".
    - 93        """
    - 94        return query_output(output_query)
    - 95
    - 96    def image_captioner(self, image_path: str) -> str:
    - 97        """Use this tool to provide a textual description of a visual content, such as, image files.
    - 98        Usage: image_captioner(image_path)
    - 99        :param image_path: The absolute path of the image file to be analyzed.
    -100        """
    -101        return image_captioner(image_path)
    -102
    -103    def generate_content(self, instructions: str, mime_type: str, filepath: AnyPath) -> str:
    -104        """Use this tool for tasks that require generating any kind of content, such as, code and text, image, etc.
    -105        Usage: generate_content(instructions, mime_type)
    -106        :param instructions: The instructions for generating the content.
    -107        :param mime_type: The generated content type (use MIME types).
    -108        :param filepath: Optional file path for saving the content.
    -109        """
    -110        return generate_content(instructions, mime_type, filepath)
    -111
    -112    def save_content(self, filepath: AnyPath) -> str:
    -113        """Use this tool to save generated content into disk (program, script, text, image, etc).
    -114        Usage: save_content(filepath)
    -115        :param filepath: The path where you want to save the content.
    -116        """
    -117        return save_content(filepath)
    +            
     39class AgentToolkit(metaclass=Singleton):
    + 40    """This class serves as the toolkit for AskAI task agents, providing essential tools and functionalities required
    + 41    for their tasks.
    + 42    """
    + 43
    + 44    INSTANCE: "AgentToolkit"
    + 45
    + 46    RESERVED: list[str] = ["tools"]
    + 47
    + 48    def __init__(self):
    + 49        self._all: dict[str, Callable] = dict(
    + 50            filter(
    + 51                lambda pair: pair[0] not in self.RESERVED and not pair[0].startswith("_"),
    + 52                {n: fn for n, fn in inspect.getmembers(self, predicate=inspect.ismethod)}.items(),
    + 53            )
    + 54        )
    + 55
    + 56    @lru_cache
    + 57    def tools(self) -> list[BaseTool]:
    + 58        """Return a cached list of LangChain base tools.
    + 59        :return: A list of BaseTool's instances available for use.
    + 60        """
    + 61        tools: list[BaseTool] = [self._create_structured_tool(v) for _, v in self._all.items()]
    + 62
    + 63        log.debug("Available tools: are: '%s'", tools)
    + 64
    + 65        return tools
    + 66
    + 67    def _human_approval(self) -> bool:
    + 68        """Prompt for human approval."""
    + 69        confirm_msg = msg.access_grant()
    + 70        if (resp := line_input(confirm_msg).lower()) not in ("yes", "y"):
    + 71            raise ValueError(f"Terminal command execution was not approved '{resp}' !")
    + 72        self._approved = True
    + 73
    + 74        return self._approved
    + 75
    + 76    def _create_structured_tool(self, fn: Callable) -> BaseTool:
    + 77        """Create a LangChain agent tool based on the provided function.
    + 78        :param fn: The function that implements the tool's behavior.
    + 79        :return: An instance of BaseTool configured with the provided function.
    + 80        """
    + 81        return StructuredTool.from_function(
    + 82            func=fn,
    + 83            name=fn.__name__,
    + 84            description=f"```{dedent(fn.__doc__)}```\n\n" if fn and fn.__doc__ else "",
    + 85            return_direct=True,
    + 86        )
    + 87
    + 88    def browse(self, search_query: str) -> Optional[str]:
    + 89        """Use this tool to browse the internet for the latest news and current events, particularly when up-to-date
    + 90        information is needed quickly. This tool is especially effective for accessing the most recent data available
    + 91        online.
    + 92        Usage: `browse(search_query)`
    + 93        :param search_query: The web search query as a string.
    + 94        :return: A string containing the results of the web search, or None if no relevant results are found.
    + 95        """
    + 96        return browse(search_query)
    + 97
    + 98    def query_output(self, output_query: str) -> str:
    + 99        """Use this tool to analyze textual content and identify the presence of files, folders, and applications. This
    +100        tool is designed to process and analyze content that is already available in textual form, but it does not
    +101        directly read or extract file contents.
    +102        Usage: `query_output(output_query)`
    +103        :param output_query: The query regarding the output. Use "Identify <file types, names, or textual content>".
    +104        :return: A string containing the results of the analysis based on the query.
    +105        """
    +106        return query_output(output_query)
    +107
    +108    def image_captioner(self, image_path: str) -> str:
    +109        """Use this tool to generate a textual description of visual content, such as image files.
    +110        Usage: `image_captioner(image_path)`
    +111        :param image_path: The absolute path of the image file to be analyzed.
    +112        :return: A string containing the generated caption describing the image.
    +113        """
    +114        return ensure_endswith(
    +115            ensure_startswith(parse_caption(image_captioner(image_path)), f"\n>   Description of '{image_path}':\n"),
    +116            "\n",
    +117        )
     118
    -119    def display_tool(self, texts: list[str] | str) -> str:
    -120        """
    -121        Name: 'Display Tool'
    -122        Description: Use this tool to display textual information.
    -123        Usage: 'display_tool(text, ...repeat N times)'
    -124        :param texts: The comma separated list of texts to be displayed.
    -125        """
    -126        return display_tool(*(texts if isinstance(texts, list) else [texts]))
    -127
    -128    def direct_answer(self, question: str, answer: str) -> str:
    -129        """Use this tool to execute terminal commands or process user-provided commands."
    -130        Usage: 'direct_answer(answer)'
    -131        :param question: The original user question.
    -132        :param answer: Your direct answer to the user.
    -133        """
    -134        args = {'user': shared.username, 'idiom': shared.idiom, 'context': answer, 'question': question}
    -135        return final_answer("taius-jarvis", [k for k in args.keys()], **args)
    +119    def webcam_capturer(self, photo_name: str | None, detect_faces: bool = False) -> str:
    +120        """Capture a photo using the webcam, and save it locally. This tool is useful for taking photos, detect people's
    +121        faces, and, describing what is in front of the webcam.
    +122        Usage: `webcam_capturer(photo_name, detect_faces)`
    +123        :param photo_name: The name of the photo file (without the extension). If None, a default name will be used.
    +124        :param detect_faces: Whether to detect and describe all faces in the photo (default is False).
    +125        :return: The file path of the saved JPEG image.
    +126        """
    +127        return webcam_capturer(photo_name, detect_faces)
    +128
    +129    def webcam_identifier(self) -> str:
    +130        """Identify the person in front of the webcam using a pre-stored set of faces and provide a description. This
    +131        tool is useful for recognizing individuals and generating descriptions based on pre-stored facial data.
    +132        Usage: `webcam_identifier()`
    +133        :return: A string containing the identification and description of the person.
    +134        """
    +135        return webcam_identifier()
     136
    -137    def list_tool(self, folder: str, filters: str | None = None) -> str:
    -138        """
    -139        Name: 'List Folder'
    -140        Description: Use this tool to access the contents of a specified folder.
    -141        Usage: 'list_tool(folder, filters)'
    -142        :param folder: The absolute path of the folder whose contents you wish to list or access.
    -143        :param filters: Optional Parameter: Specify a comma-separated list of file glob to filter the results (e.g., "*.*, *.txt").
    -144        """
    -145        return list_contents(folder, filters)
    -146
    -147    def open_tool(self, path_name: str) -> str:
    -148        """Use this tool to open, read (show) content of files, and also, to playback any media files.
    -149        Usage: 'open_tool(path_name)'
    -150        :param path_name: The absolute file, folder, or application path name.
    -151        """
    -152        return open_command(path_name)
    -153
    -154    def summarize(self, folder: str, glob: str) -> str:
    -155        """Use this tool only when the user explicitly requests a summary of files and folders.
    -156        Usage: summarize(folder_name, glob)
    -157        :param folder: The base name of the folder containing the files to be summarized.
    -158        :param glob: The glob expression to specify files to be included for summarization.
    -159        """
    -160        return summarize(folder, glob)
    -161
    -162    def terminal(self, shell_type: str, command: str) -> str:
    -163        """Use this tool to execute terminal commands or process user-provided commands."
    -164        Usage: 'terminal(shell_type, command)'
    -165        :param shell_type: The type of the shell (e.g. bash, zsh, powershell, etc).
    -166        :param command: The actual commands you wish to execute in the terminal.
    -167        """
    -168        # TODO Check for permission before executing
    -169        return execute_command(shell_type, command)
    -170
    -171    def shutdown(self, reason: str) -> None:
    -172        """Use this tool when the user provides a query that indicates he wants to conclude the interaction (e.g. bye, exit).
    -173        Usage: 'shutdown(reason)'
    -174        :param reason: The reason for termination.
    -175        """
    -176        raise TerminatingQuery(reason)
    +137    def generate_content(self, instructions: str, mime_type: str, filepath: AnyPath) -> str:
    +138        """Use this tool to generate various types of content, such as code, text, images, etc. This tool processes
    +139        descriptive instructions to create the specified content type and can optionally save it to a file.
    +140        Usage: `generate_content(instructions, mime_type, filepath)`
    +141        :param instructions: Descriptive instructions on how to create the content (not the content itself).
    +142        :param mime_type: The MIME type representing the type of content to generate.
    +143        :param filepath: The optional file path where the generated content will be saved.
    +144        :return: The generated content as a string.
    +145        """
    +146        return generate_content(instructions, mime_type, filepath)
    +147
    +148    def save_content(self, filepath: AnyPath) -> str:
    +149        """Save previously generated content to disk, such as programs, scripts, text, images, etc. This tool is used
    +150        to persist content that was generated using the `generate_content` tool.
    +151        Usage: `save_content(filepath)`
    +152        :param filepath: The path where you want to save the content.
    +153        :return: The absolute path name of the saved file.
    +154        """
    +155        return save_content(filepath)
    +156
    +157    def direct_answer(self, texts: list[str] | str) -> str:
    +158        """Provide and display text as a direct answer to the user. This tool is used to present one or more pieces of
    +159        text directly to the user.
    +160        Usage: `direct_answer(text, ...repeat N times)`
    +161        :param texts: A list of texts or a single text string to be displayed.
    +162        :return: A string containing all provided texts concatenated together.
    +163        """
    +164        return display_tool(*(texts if isinstance(texts, list) else [texts]))
    +165
    +166    def list_tool(self, folder: str, filters: str | None = None) -> str:
    +167        """Access and list the contents of a specified folder. This tool is used to retrieve the contents of a folder,
    +168        optionally filtering the results based on specified criteria.
    +169        Usage: `list_tool(folder, filters)`
    +170        :param folder: The absolute path of the folder whose contents you wish to list or access.
    +171        :param filters: Optional parameter: A comma-separated list of file globs to filter results (e.g., "*.*, *.txt").
    +172        :return: A string listing the contents of the folder, filtered by the provided criteria if applicable.
    +173        """
    +174        return list_contents(folder, filters)
    +175
    +176    def open_tool(self, path_name: str) -> str:
    +177        """Open and display the content of files, or playback media files, and also execute applications. This tool is
    +178        used to open a file, folder, or application, read its contents, or play back media files.
    +179        Usage: `open_tool(path_name)`
    +180        :param path_name: The absolute path of the file, folder, or application to be opened.
    +181        :return: The output generated by the open command, such as file content or playback status.
    +182        """
    +183        return open_command(path_name)
    +184
    +185    def summarize(self, folder: str, glob: str) -> str:
    +186        """Summarize the contents of files and folders based on user requests. This tool should be used only when the
    +187        user explicitly requests a summary of files and folders, not for summarizing textual content.
    +188        Usage: `summarize(folder, glob)`
    +189        :param folder: The base path of the folder containing the files to be summarized.
    +190        :param glob: The glob expression to specify which files should be included in the summary.
    +191        :return: A string containing the summary of the specified files and folders.
    +192        """
    +193        return summarize(folder, glob)
    +194
    +195    def terminal(self, shell_type: str, command: str) -> str:
    +196        """Execute terminal commands or process user-provided commands using the specified shell. This tool is used to
    +197        run commands in a terminal environment based on the provided shell type.
    +198        Usage: `terminal(shell_type, command)`
    +199        :param shell_type: The type of shell to use (e.g., bash, zsh, powershell, etc.).
    +200        :param command: The command or set of commands to execute in the terminal.
    +201        :return: The output of the executed terminal command(s) as a string.
    +202        """
    +203        # TODO Check for permission before executing
    +204        return execute_command(shell_type, command)
    +205
    +206    def shutdown(self, reason: str) -> None:
    +207        """Conclude the interaction based on the user's intent to end the session (e.g., bye, exit). This tool is used
    +208        to gracefully shut down the interaction when the user indicates a desire to terminate the session.
    +209        Usage: `shutdown(reason)`
    +210        :param reason: The reason for terminating the session.
    +211        """
    +212        raise TerminatingQuery(reason)
     
    -

    This class provides the AskAI task agent tools.

    +

    This class serves as the toolkit for AskAI task agents, providing essential tools and functionalities required +for their tasks.

    @@ -514,24 +589,32 @@

    @lru_cache
    def - tools(self) -> list[langchain_core.tools.BaseTool]: + tools(self) -> list[langchain_core.tools.base.BaseTool]:

    -
    53    @lru_cache
    -54    def tools(self) -> list[BaseTool]:
    -55        """Return a list of Langchain base tools."""
    -56        tools: list[BaseTool] = [self._create_structured_tool(v) for _, v in self._all.items()]
    -57
    -58        log.debug("Available tools: are: '%s'", tools)
    -59
    -60        return tools
    +            
    56    @lru_cache
    +57    def tools(self) -> list[BaseTool]:
    +58        """Return a cached list of LangChain base tools.
    +59        :return: A list of BaseTool's instances available for use.
    +60        """
    +61        tools: list[BaseTool] = [self._create_structured_tool(v) for _, v in self._all.items()]
    +62
    +63        log.debug("Available tools: are: '%s'", tools)
    +64
    +65        return tools
     
    -

    Return a list of Langchain base tools.

    +

    Return a cached list of LangChain base tools.

    + +
    Returns
    + +
    +

    A list of BaseTool's instances available for use.

    +
    @@ -541,29 +624,40 @@

    def - browse(self, search_query: str) -> str: + browse(self, search_query: str) -> Optional[str]:
    -
    82    def browse(self, search_query: str) -> str:
    -83        """Use this tool to browse the internet or to stay informed about the latest news and current events, especially when you require up-to-date information quickly. It is especially effective for accessing the most recent data available online.
    -84        Usage: `browse(search_query)`
    -85        :param search_query: The web search query in string format.
    -86        """
    -87        return browse(search_query)
    +            
    88    def browse(self, search_query: str) -> Optional[str]:
    +89        """Use this tool to browse the internet for the latest news and current events, particularly when up-to-date
    +90        information is needed quickly. This tool is especially effective for accessing the most recent data available
    +91        online.
    +92        Usage: `browse(search_query)`
    +93        :param search_query: The web search query as a string.
    +94        :return: A string containing the results of the web search, or None if no relevant results are found.
    +95        """
    +96        return browse(search_query)
     
    -

    Use this tool to browse the internet or to stay informed about the latest news and current events, especially when you require up-to-date information quickly. It is especially effective for accessing the most recent data available online. +

    Use this tool to browse the internet for the latest news and current events, particularly when up-to-date +information is needed quickly. This tool is especially effective for accessing the most recent data available +online. Usage: browse(search_query)

    Parameters
      -
    • search_query: The web search query in string format.
    • +
    • search_query: The web search query as a string.
    + +
    Returns
    + +
    +

    A string containing the results of the web search, or None if no relevant results are found.

    +
    @@ -579,23 +673,34 @@
    Parameters
    -
    89    def query_output(self, output_query: str) -> str:
    -90        """Use this tool to analyze textual content to identify the presence of files, folders, and applications. It is designed to process and analyze content that is already available in textual form, but not to read or extract file contents directly.
    -91        Usage: `query_output(query)`
    -92        :param output_query: The query regarding the output. Prefer using "Identify <file types or name or textual content>".
    -93        """
    -94        return query_output(output_query)
    +            
     98    def query_output(self, output_query: str) -> str:
    + 99        """Use this tool to analyze textual content and identify the presence of files, folders, and applications. This
    +100        tool is designed to process and analyze content that is already available in textual form, but it does not
    +101        directly read or extract file contents.
    +102        Usage: `query_output(output_query)`
    +103        :param output_query: The query regarding the output. Use "Identify <file types, names, or textual content>".
    +104        :return: A string containing the results of the analysis based on the query.
    +105        """
    +106        return query_output(output_query)
     
    -

    Use this tool to analyze textual content to identify the presence of files, folders, and applications. It is designed to process and analyze content that is already available in textual form, but not to read or extract file contents directly. -Usage: query_output(query)

    +

    Use this tool to analyze textual content and identify the presence of files, folders, and applications. This +tool is designed to process and analyze content that is already available in textual form, but it does not +directly read or extract file contents. +Usage: query_output(output_query)

    Parameters
      -
    • output_query: The query regarding the output. Prefer using "Identify ".
    • +
    • output_query: The query regarding the output. Use "Identify ".
    + +
    Returns
    + +
    +

    A string containing the results of the analysis based on the query.

    +
    @@ -611,126 +716,196 @@
    Parameters
    -
     96    def image_captioner(self, image_path: str) -> str:
    - 97        """Use this tool to provide a textual description of a visual content, such as, image files.
    - 98        Usage: image_captioner(image_path)
    - 99        :param image_path: The absolute path of the image file to be analyzed.
    -100        """
    -101        return image_captioner(image_path)
    +            
    108    def image_captioner(self, image_path: str) -> str:
    +109        """Use this tool to generate a textual description of visual content, such as image files.
    +110        Usage: `image_captioner(image_path)`
    +111        :param image_path: The absolute path of the image file to be analyzed.
    +112        :return: A string containing the generated caption describing the image.
    +113        """
    +114        return ensure_endswith(
    +115            ensure_startswith(parse_caption(image_captioner(image_path)), f"\n>   Description of '{image_path}':\n"),
    +116            "\n",
    +117        )
     
    -

    Use this tool to provide a textual description of a visual content, such as, image files. -Usage: image_captioner(image_path)

    +

    Use this tool to generate a textual description of visual content, such as image files. +Usage: image_captioner(image_path)

    Parameters
    • image_path: The absolute path of the image file to be analyzed.
    + +
    Returns
    + +
    +

    A string containing the generated caption describing the image.

    +
    -
    - +
    +
    def - generate_content( self, instructions: str, mime_type: str, filepath: Union[pathlib.Path, str, NoneType]) -> str: + webcam_capturer(self, photo_name: str | None, detect_faces: bool = False) -> str: - +
    - -
    103    def generate_content(self, instructions: str, mime_type: str, filepath: AnyPath) -> str:
    -104        """Use this tool for tasks that require generating any kind of content, such as, code and text, image, etc.
    -105        Usage: generate_content(instructions, mime_type)
    -106        :param instructions: The instructions for generating the content.
    -107        :param mime_type: The generated content type (use MIME types).
    -108        :param filepath: Optional file path for saving the content.
    -109        """
    -110        return generate_content(instructions, mime_type, filepath)
    +    
    +            
    119    def webcam_capturer(self, photo_name: str | None, detect_faces: bool = False) -> str:
    +120        """Capture a photo using the webcam, and save it locally. This tool is useful for taking photos, detect people's
    +121        faces, and, describing what is in front of the webcam.
    +122        Usage: `webcam_capturer(photo_name, detect_faces)`
    +123        :param photo_name: The name of the photo file (without the extension). If None, a default name will be used.
    +124        :param detect_faces: Whether to detect and describe all faces in the photo (default is False).
    +125        :return: The file path of the saved JPEG image.
    +126        """
    +127        return webcam_capturer(photo_name, detect_faces)
     
    -

    Use this tool for tasks that require generating any kind of content, such as, code and text, image, etc. -Usage: generate_content(instructions, mime_type)

    +

    Capture a photo using the webcam, and save it locally. This tool is useful for taking photos, detect people's +faces, and, describing what is in front of the webcam. +Usage: webcam_capturer(photo_name, detect_faces)

    Parameters
      -
    • instructions: The instructions for generating the content.
    • -
    • mime_type: The generated content type (use MIME types).
    • -
    • filepath: Optional file path for saving the content.
    • +
    • photo_name: The name of the photo file (without the extension). If None, a default name will be used.
    • +
    • detect_faces: Whether to detect and describe all faces in the photo (default is False).
    + +
    Returns
    + +
    +

    The file path of the saved JPEG image.

    +
    -
    - +
    +
    def - save_content(self, filepath: Union[pathlib.Path, str, NoneType]) -> str: + webcam_identifier(self) -> str: - +
    - -
    112    def save_content(self, filepath: AnyPath) -> str:
    -113        """Use this tool to save generated content into disk (program, script, text, image, etc).
    -114        Usage: save_content(filepath)
    -115        :param filepath: The path where you want to save the content.
    -116        """
    -117        return save_content(filepath)
    +    
    +            
    129    def webcam_identifier(self) -> str:
    +130        """Identify the person in front of the webcam using a pre-stored set of faces and provide a description. This
    +131        tool is useful for recognizing individuals and generating descriptions based on pre-stored facial data.
    +132        Usage: `webcam_identifier()`
    +133        :return: A string containing the identification and description of the person.
    +134        """
    +135        return webcam_identifier()
     
    -

    Use this tool to save generated content into disk (program, script, text, image, etc). -Usage: save_content(filepath)

    +

    Identify the person in front of the webcam using a pre-stored set of faces and provide a description. This +tool is useful for recognizing individuals and generating descriptions based on pre-stored facial data. +Usage: webcam_identifier()

    + +
    Returns
    + +
    +

    A string containing the identification and description of the person.

    +
    +
    + + +
    +
    + +
    + + def + generate_content( self, instructions: str, mime_type: str, filepath: Union[pathlib.Path, str, NoneType]) -> str: + + + +
    + +
    137    def generate_content(self, instructions: str, mime_type: str, filepath: AnyPath) -> str:
    +138        """Use this tool to generate various types of content, such as code, text, images, etc. This tool processes
    +139        descriptive instructions to create the specified content type and can optionally save it to a file.
    +140        Usage: `generate_content(instructions, mime_type, filepath)`
    +141        :param instructions: Descriptive instructions on how to create the content (not the content itself).
    +142        :param mime_type: The MIME type representing the type of content to generate.
    +143        :param filepath: The optional file path where the generated content will be saved.
    +144        :return: The generated content as a string.
    +145        """
    +146        return generate_content(instructions, mime_type, filepath)
    +
    + + +

    Use this tool to generate various types of content, such as code, text, images, etc. This tool processes +descriptive instructions to create the specified content type and can optionally save it to a file. +Usage: generate_content(instructions, mime_type, filepath)

    Parameters
      -
    • filepath: The path where you want to save the content.
    • +
    • instructions: Descriptive instructions on how to create the content (not the content itself).
    • +
    • mime_type: The MIME type representing the type of content to generate.
    • +
    • filepath: The optional file path where the generated content will be saved.
    + +
    Returns
    + +
    +

    The generated content as a string.

    +
    -
    - +
    +
    def - display_tool(self, texts: list[str] | str) -> str: + save_content(self, filepath: Union[pathlib.Path, str, NoneType]) -> str: - +
    - -
    119    def display_tool(self, texts: list[str] | str) -> str:
    -120        """
    -121        Name: 'Display Tool'
    -122        Description: Use this tool to display textual information.
    -123        Usage: 'display_tool(text, ...repeat N times)'
    -124        :param texts: The comma separated list of texts to be displayed.
    -125        """
    -126        return display_tool(*(texts if isinstance(texts, list) else [texts]))
    +    
    +            
    148    def save_content(self, filepath: AnyPath) -> str:
    +149        """Save previously generated content to disk, such as programs, scripts, text, images, etc. This tool is used
    +150        to persist content that was generated using the `generate_content` tool.
    +151        Usage: `save_content(filepath)`
    +152        :param filepath: The path where you want to save the content.
    +153        :return: The absolute path name of the saved file.
    +154        """
    +155        return save_content(filepath)
     
    -

    Name: 'Display Tool' -Description: Use this tool to display textual information. -Usage: 'display_tool(text, ...repeat N times)'

    +

    Save previously generated content to disk, such as programs, scripts, text, images, etc. This tool is used +to persist content that was generated using the generate_content tool. +Usage: save_content(filepath)

    Parameters
      -
    • texts: The comma separated list of texts to be displayed.
    • +
    • filepath: The path where you want to save the content.
    + +
    Returns
    + +
    +

    The absolute path name of the saved file.

    +
    @@ -740,32 +915,38 @@
    Parameters
    def - direct_answer(self, question: str, answer: str) -> str: + direct_answer(self, texts: list[str] | str) -> str:
    -
    128    def direct_answer(self, question: str, answer: str) -> str:
    -129        """Use this tool to execute terminal commands or process user-provided commands."
    -130        Usage: 'direct_answer(answer)'
    -131        :param question: The original user question.
    -132        :param answer: Your direct answer to the user.
    -133        """
    -134        args = {'user': shared.username, 'idiom': shared.idiom, 'context': answer, 'question': question}
    -135        return final_answer("taius-jarvis", [k for k in args.keys()], **args)
    +            
    157    def direct_answer(self, texts: list[str] | str) -> str:
    +158        """Provide and display text as a direct answer to the user. This tool is used to present one or more pieces of
    +159        text directly to the user.
    +160        Usage: `direct_answer(text, ...repeat N times)`
    +161        :param texts: A list of texts or a single text string to be displayed.
    +162        :return: A string containing all provided texts concatenated together.
    +163        """
    +164        return display_tool(*(texts if isinstance(texts, list) else [texts]))
     
    -

    Use this tool to execute terminal commands or process user-provided commands." -Usage: 'direct_answer(answer)'

    +

    Provide and display text as a direct answer to the user. This tool is used to present one or more pieces of +text directly to the user. +Usage: direct_answer(text, ...repeat N times)

    Parameters
      -
    • question: The original user question.
    • -
    • answer: Your direct answer to the user.
    • +
    • texts: A list of texts or a single text string to be displayed.
    + +
    Returns
    + +
    +

    A string containing all provided texts concatenated together.

    +
    @@ -781,28 +962,34 @@
    Parameters
    -
    137    def list_tool(self, folder: str, filters: str | None = None) -> str:
    -138        """
    -139        Name: 'List Folder'
    -140        Description: Use this tool to access the contents of a specified folder.
    -141        Usage: 'list_tool(folder, filters)'
    -142        :param folder: The absolute path of the folder whose contents you wish to list or access.
    -143        :param filters: Optional Parameter: Specify a comma-separated list of file glob to filter the results (e.g., "*.*, *.txt").
    -144        """
    -145        return list_contents(folder, filters)
    +            
    166    def list_tool(self, folder: str, filters: str | None = None) -> str:
    +167        """Access and list the contents of a specified folder. This tool is used to retrieve the contents of a folder,
    +168        optionally filtering the results based on specified criteria.
    +169        Usage: `list_tool(folder, filters)`
    +170        :param folder: The absolute path of the folder whose contents you wish to list or access.
    +171        :param filters: Optional parameter: A comma-separated list of file globs to filter results (e.g., "*.*, *.txt").
    +172        :return: A string listing the contents of the folder, filtered by the provided criteria if applicable.
    +173        """
    +174        return list_contents(folder, filters)
     
    -

    Name: 'List Folder' -Description: Use this tool to access the contents of a specified folder. -Usage: 'list_tool(folder, filters)'

    +

    Access and list the contents of a specified folder. This tool is used to retrieve the contents of a folder, +optionally filtering the results based on specified criteria. +Usage: list_tool(folder, filters)

    Parameters
    • folder: The absolute path of the folder whose contents you wish to list or access.
    • -
    • filters: Optional Parameter: Specify a comma-separated list of file glob to filter the results (e.g., "., *.txt").
    • +
    • filters: Optional parameter: A comma-separated list of file globs to filter results (e.g., "., *.txt").
    + +
    Returns
    + +
    +

    A string listing the contents of the folder, filtered by the provided criteria if applicable.

    +
    @@ -818,23 +1005,32 @@
    Parameters
    -
    147    def open_tool(self, path_name: str) -> str:
    -148        """Use this tool to open, read (show) content of files, and also, to playback any media files.
    -149        Usage: 'open_tool(path_name)'
    -150        :param path_name: The absolute file, folder, or application path name.
    -151        """
    -152        return open_command(path_name)
    +            
    176    def open_tool(self, path_name: str) -> str:
    +177        """Open and display the content of files, or playback media files, and also execute applications. This tool is
    +178        used to open a file, folder, or application, read its contents, or play back media files.
    +179        Usage: `open_tool(path_name)`
    +180        :param path_name: The absolute path of the file, folder, or application to be opened.
    +181        :return: The output generated by the open command, such as file content or playback status.
    +182        """
    +183        return open_command(path_name)
     
    -

    Use this tool to open, read (show) content of files, and also, to playback any media files. -Usage: 'open_tool(path_name)'

    +

    Open and display the content of files, or playback media files, and also execute applications. This tool is +used to open a file, folder, or application, read its contents, or play back media files. +Usage: open_tool(path_name)

    Parameters
      -
    • path_name: The absolute file, folder, or application path name.
    • +
    • path_name: The absolute path of the file, folder, or application to be opened.
    + +
    Returns
    + +
    +

    The output generated by the open command, such as file content or playback status.

    +
    @@ -850,25 +1046,34 @@
    Parameters
    -
    154    def summarize(self, folder: str, glob: str) -> str:
    -155        """Use this tool only when the user explicitly requests a summary of files and folders.
    -156        Usage: summarize(folder_name, glob)
    -157        :param folder: The base name of the folder containing the files to be summarized.
    -158        :param glob: The glob expression to specify files to be included for summarization.
    -159        """
    -160        return summarize(folder, glob)
    +            
    185    def summarize(self, folder: str, glob: str) -> str:
    +186        """Summarize the contents of files and folders based on user requests. This tool should be used only when the
    +187        user explicitly requests a summary of files and folders, not for summarizing textual content.
    +188        Usage: `summarize(folder, glob)`
    +189        :param folder: The base path of the folder containing the files to be summarized.
    +190        :param glob: The glob expression to specify which files should be included in the summary.
    +191        :return: A string containing the summary of the specified files and folders.
    +192        """
    +193        return summarize(folder, glob)
     
    -

    Use this tool only when the user explicitly requests a summary of files and folders. -Usage: summarize(folder_name, glob)

    +

    Summarize the contents of files and folders based on user requests. This tool should be used only when the +user explicitly requests a summary of files and folders, not for summarizing textual content. +Usage: summarize(folder, glob)

    Parameters
      -
    • folder: The base name of the folder containing the files to be summarized.
    • -
    • glob: The glob expression to specify files to be included for summarization.
    • +
    • folder: The base path of the folder containing the files to be summarized.
    • +
    • glob: The glob expression to specify which files should be included in the summary.
    + +
    Returns
    + +
    +

    A string containing the summary of the specified files and folders.

    +
    @@ -884,26 +1089,35 @@
    Parameters
    -
    162    def terminal(self, shell_type: str, command: str) -> str:
    -163        """Use this tool to execute terminal commands or process user-provided commands."
    -164        Usage: 'terminal(shell_type, command)'
    -165        :param shell_type: The type of the shell (e.g. bash, zsh, powershell, etc).
    -166        :param command: The actual commands you wish to execute in the terminal.
    -167        """
    -168        # TODO Check for permission before executing
    -169        return execute_command(shell_type, command)
    +            
    195    def terminal(self, shell_type: str, command: str) -> str:
    +196        """Execute terminal commands or process user-provided commands using the specified shell. This tool is used to
    +197        run commands in a terminal environment based on the provided shell type.
    +198        Usage: `terminal(shell_type, command)`
    +199        :param shell_type: The type of shell to use (e.g., bash, zsh, powershell, etc.).
    +200        :param command: The command or set of commands to execute in the terminal.
    +201        :return: The output of the executed terminal command(s) as a string.
    +202        """
    +203        # TODO Check for permission before executing
    +204        return execute_command(shell_type, command)
     
    -

    Use this tool to execute terminal commands or process user-provided commands." -Usage: 'terminal(shell_type, command)'

    +

    Execute terminal commands or process user-provided commands using the specified shell. This tool is used to +run commands in a terminal environment based on the provided shell type. +Usage: terminal(shell_type, command)

    Parameters
      -
    • shell_type: The type of the shell (e.g. bash, zsh, powershell, etc).
    • -
    • command: The actual commands you wish to execute in the terminal.
    • +
    • shell_type: The type of shell to use (e.g., bash, zsh, powershell, etc.).
    • +
    • command: The command or set of commands to execute in the terminal.
    + +
    Returns
    + +
    +

    The output of the executed terminal command(s) as a string.

    +
    @@ -919,22 +1133,24 @@
    Parameters
    -
    171    def shutdown(self, reason: str) -> None:
    -172        """Use this tool when the user provides a query that indicates he wants to conclude the interaction (e.g. bye, exit).
    -173        Usage: 'shutdown(reason)'
    -174        :param reason: The reason for termination.
    -175        """
    -176        raise TerminatingQuery(reason)
    +            
    206    def shutdown(self, reason: str) -> None:
    +207        """Conclude the interaction based on the user's intent to end the session (e.g., bye, exit). This tool is used
    +208        to gracefully shut down the interaction when the user indicates a desire to terminate the session.
    +209        Usage: `shutdown(reason)`
    +210        :param reason: The reason for terminating the session.
    +211        """
    +212        raise TerminatingQuery(reason)
     
    -

    Use this tool when the user provides a query that indicates he wants to conclude the interaction (e.g. bye, exit). -Usage: 'shutdown(reason)'

    +

    Conclude the interaction based on the user's intent to end the session (e.g., bye, exit). This tool is used +to gracefully shut down the interaction when the user indicates a desire to terminate the session. +Usage: shutdown(reason)

    Parameters
      -
    • reason: The reason for termination.
    • +
    • reason: The reason for terminating the session.
    diff --git a/docs/api/askai/main/askai/core/features/router/tools.html b/docs/api/askai/main/askai/core/features/tools.html similarity index 97% rename from docs/api/askai/main/askai/core/features/router/tools.html rename to docs/api/askai/main/askai/core/features/tools.html index a83c7748..7f802ba7 100644 --- a/docs/api/askai/main/askai/core/features/router/tools.html +++ b/docs/api/askai/main/askai/core/features/tools.html @@ -3,8 +3,8 @@ - - main.askai.core.features.router.tools API documentation + + main.askai.core.features.tools API documentation @@ -16,11 +16,11 @@

  • summarization
  • terminal
  • vision
  • +
  • webcam
  • @@ -50,7 +51,7 @@

    Submodules

    -main.askai.core.features.router.tools

    +main.askai.core.features.tools

    Package initialization.

    @@ -61,9 +62,9 @@

     1# _*_ coding: utf-8 _*_
      2#
    - 3# hspylib-askai v1.0.11
    + 3# hspylib-askai v1.0.13
      4#
    - 5# Package: main.askai.core.features.router.tools
    + 5# Package: main.askai.core.features.tools
      6"""Package initialization."""
      7
      8__all__ = [
    @@ -73,9 +74,10 @@ 

    12 'generation', 13 'summarization', 14 'terminal', -15 'vision' -16] -17__version__ = '1.0.11' +15 'vision', +16 'webcam' +17] +18__version__ = '1.0.13'

    @@ -133,7 +135,7 @@

    script.async = true; script.onload = () => resolve(window.pdocSearch); script.onerror = (e) => reject(e); - script.src = "../../../../../search.js"; + script.src = "../../../../search.js"; document.getElementsByTagName("head")[0].appendChild(script); }); } catch (e) { @@ -175,7 +177,7 @@

    } for (let result of results.slice(0, 10)) { let doc = result.doc; - let url = `../../../../../${doc.modulename.replaceAll(".", "/")}.html`; + let url = `../../../../${doc.modulename.replaceAll(".", "/")}.html`; if (doc.qualname) { url += `#${doc.qualname}`; } diff --git a/docs/api/askai/main/askai/core/features/router/tools/analysis.html b/docs/api/askai/main/askai/core/features/tools/analysis.html similarity index 93% rename from docs/api/askai/main/askai/core/features/router/tools/analysis.html rename to docs/api/askai/main/askai/core/features/tools/analysis.html index 8b9a5f69..3abb0f67 100644 --- a/docs/api/askai/main/askai/core/features/router/tools/analysis.html +++ b/docs/api/askai/main/askai/core/features/tools/analysis.html @@ -3,8 +3,8 @@ - - main.askai.core.features.router.tools.analysis API documentation + + main.askai.core.features.tools.analysis API documentation @@ -20,7 +20,7 @@ -  main.askai.core.features.router.tools +  main.askai.core.features.tools API Documentation

    -main.askai.core.features.router.tools.analysis

    +main.askai.core.features.tools.analysis

    @project: HsPyLib-AskAI @package: askai.core.features.tools.analysis @@ -82,39 +82,40 @@

    16from askai.core.askai_messages import msg 17from askai.core.askai_prompt import prompt 18from askai.core.engine.openai.temperature import Temperature -19from askai.core.support.langchain_support import lc_llm -20from askai.core.support.shared_instances import shared -21from askai.core.support.utilities import ensure_ln -22from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder -23from langchain_core.runnables.history import RunnableWithMessageHistory -24 -25import logging as log -26 +19from askai.core.model.ai_reply import AIReply +20from askai.core.support.langchain_support import lc_llm +21from askai.core.support.shared_instances import shared +22from askai.core.support.text_formatter import TextFormatter +23from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder +24from langchain_core.runnables.history import RunnableWithMessageHistory +25 +26import logging as log 27 -28def query_output(query: str, context: str = None) -> str: -29 """Handle 'Text analysis', invoking: analyze(question: str). Analyze the context and answer the question. -30 :param query: The question about the content to be analyzed. -31 :param context: The context of the question. -32 """ -33 output = None -34 template = ChatPromptTemplate.from_messages( -35 [ -36 ("system", prompt.read_prompt("analysis")), -37 MessagesPlaceholder("chat_history", optional=True), -38 ("human", "{input}"), -39 ] -40 ) -41 if context or (context := str(shared.context.flat("HISTORY"))): -42 runnable = template | lc_llm.create_chat_model(Temperature.DATA_ANALYSIS.temp) -43 runnable = RunnableWithMessageHistory( -44 runnable, shared.context.flat, input_messages_key="input", history_messages_key="chat_history" -45 ) -46 log.info("Analysis::[QUERY] '%s' context=%s", query, context) -47 if response := runnable.invoke({"input": query}, config={"configurable": {"session_id": "HISTORY"}}): -48 output = response.content -49 events.reply.emit(message=msg.analysis(output), verbosity="debug") -50 -51 return ensure_ln(output or "Sorry, I don't know.") +28 +29def query_output(query: str, context: str = None) -> str: +30 """Handle 'Text analysis', invoking: analyze(question: str). Analyze the context and answer the question. +31 :param query: The question about the content to be analyzed. +32 :param context: The context of the question. +33 """ +34 output = None +35 template = ChatPromptTemplate.from_messages( +36 [ +37 ("system", prompt.read_prompt("analysis")), +38 MessagesPlaceholder("chat_history", optional=True), +39 ("human", "{input}"), +40 ] +41 ) +42 if context or (context := str(shared.context.flat("HISTORY"))): +43 runnable = template | lc_llm.create_chat_model(Temperature.DATA_ANALYSIS.temp) +44 runnable = RunnableWithMessageHistory( +45 runnable, shared.context.flat, input_messages_key="input", history_messages_key="chat_history" +46 ) +47 log.info("Analysis::[QUERY] '%s' context=%s", query, context) +48 if response := runnable.invoke({"input": query}, config={"configurable": {"session_id": "HISTORY"}}): +49 output = response.content +50 events.reply.emit(reply=AIReply.detailed(msg.analysis(output))) +51 +52 return TextFormatter.ensure_ln(output or "Sorry, I don't know.")

    @@ -130,30 +131,30 @@

    -
    29def query_output(query: str, context: str = None) -> str:
    -30    """Handle 'Text analysis', invoking: analyze(question: str). Analyze the context and answer the question.
    -31    :param query: The question about the content to be analyzed.
    -32    :param context: The context of the question.
    -33    """
    -34    output = None
    -35    template = ChatPromptTemplate.from_messages(
    -36        [
    -37            ("system", prompt.read_prompt("analysis")),
    -38            MessagesPlaceholder("chat_history", optional=True),
    -39            ("human", "{input}"),
    -40        ]
    -41    )
    -42    if context or (context := str(shared.context.flat("HISTORY"))):
    -43        runnable = template | lc_llm.create_chat_model(Temperature.DATA_ANALYSIS.temp)
    -44        runnable = RunnableWithMessageHistory(
    -45            runnable, shared.context.flat, input_messages_key="input", history_messages_key="chat_history"
    -46        )
    -47        log.info("Analysis::[QUERY] '%s'  context=%s", query, context)
    -48        if response := runnable.invoke({"input": query}, config={"configurable": {"session_id": "HISTORY"}}):
    -49            output = response.content
    -50            events.reply.emit(message=msg.analysis(output), verbosity="debug")
    -51
    -52    return ensure_ln(output or "Sorry, I don't know.")
    +            
    30def query_output(query: str, context: str = None) -> str:
    +31    """Handle 'Text analysis', invoking: analyze(question: str). Analyze the context and answer the question.
    +32    :param query: The question about the content to be analyzed.
    +33    :param context: The context of the question.
    +34    """
    +35    output = None
    +36    template = ChatPromptTemplate.from_messages(
    +37        [
    +38            ("system", prompt.read_prompt("analysis")),
    +39            MessagesPlaceholder("chat_history", optional=True),
    +40            ("human", "{input}"),
    +41        ]
    +42    )
    +43    if context or (context := str(shared.context.flat("HISTORY"))):
    +44        runnable = template | lc_llm.create_chat_model(Temperature.DATA_ANALYSIS.temp)
    +45        runnable = RunnableWithMessageHistory(
    +46            runnable, shared.context.flat, input_messages_key="input", history_messages_key="chat_history"
    +47        )
    +48        log.info("Analysis::[QUERY] '%s'  context=%s", query, context)
    +49        if response := runnable.invoke({"input": query}, config={"configurable": {"session_id": "HISTORY"}}):
    +50            output = response.content
    +51            events.reply.emit(reply=AIReply.detailed(msg.analysis(output)))
    +52
    +53    return TextFormatter.ensure_ln(output or "Sorry, I don't know.")
     
    @@ -222,7 +223,7 @@
    Parameters
    script.async = true; script.onload = () => resolve(window.pdocSearch); script.onerror = (e) => reject(e); - script.src = "../../../../../../search.js"; + script.src = "../../../../../search.js"; document.getElementsByTagName("head")[0].appendChild(script); }); } catch (e) { @@ -264,7 +265,7 @@
    Parameters
    } for (let result of results.slice(0, 10)) { let doc = result.doc; - let url = `../../../../../../${doc.modulename.replaceAll(".", "/")}.html`; + let url = `../../../../../${doc.modulename.replaceAll(".", "/")}.html`; if (doc.qualname) { url += `#${doc.qualname}`; } diff --git a/docs/api/askai/main/askai/core/features/router/tools/browser.html b/docs/api/askai/main/askai/core/features/tools/browser.html similarity index 98% rename from docs/api/askai/main/askai/core/features/router/tools/browser.html rename to docs/api/askai/main/askai/core/features/tools/browser.html index 3a1afe99..3a38a9b8 100644 --- a/docs/api/askai/main/askai/core/features/router/tools/browser.html +++ b/docs/api/askai/main/askai/core/features/tools/browser.html @@ -3,8 +3,8 @@ - - main.askai.core.features.router.tools.browser API documentation + + main.askai.core.features.tools.browser API documentation @@ -20,7 +20,7 @@ -  main.askai.core.features.router.tools +  main.askai.core.features.tools API Documentation

    -main.askai.core.features.router.tools.browser

    +main.askai.core.features.tools.browser

    @project: HsPyLib-AskAI @package: askai.core.features.tools.browser @@ -113,7 +113,7 @@

    47 log.error(msg.invalid_response(search)) 48 output = response.strip() 49 else: -50 output = internet.search_google(search) +50 output = internet.google_search(search) 51 if not output: 52 output = msg.search_empty() 53 else: @@ -155,7 +155,7 @@

    48 log.error(msg.invalid_response(search)) 49 output = response.strip() 50 else: -51 output = internet.search_google(search) +51 output = internet.google_search(search) 52 if not output: 53 output = msg.search_empty() 54 else: @@ -229,7 +229,7 @@
    Parameters
    script.async = true; script.onload = () => resolve(window.pdocSearch); script.onerror = (e) => reject(e); - script.src = "../../../../../../search.js"; + script.src = "../../../../../search.js"; document.getElementsByTagName("head")[0].appendChild(script); }); } catch (e) { @@ -271,7 +271,7 @@
    Parameters
    } for (let result of results.slice(0, 10)) { let doc = result.doc; - let url = `../../../../../../${doc.modulename.replaceAll(".", "/")}.html`; + let url = `../../../../../${doc.modulename.replaceAll(".", "/")}.html`; if (doc.qualname) { url += `#${doc.qualname}`; } diff --git a/docs/api/askai/main/askai/core/features/router/tools/general.html b/docs/api/askai/main/askai/core/features/tools/general.html similarity index 92% rename from docs/api/askai/main/askai/core/features/router/tools/general.html rename to docs/api/askai/main/askai/core/features/tools/general.html index 05f4a80d..aea75225 100644 --- a/docs/api/askai/main/askai/core/features/router/tools/general.html +++ b/docs/api/askai/main/askai/core/features/tools/general.html @@ -3,8 +3,8 @@ - - main.askai.core.features.router.tools.general API documentation + + main.askai.core.features.tools.general API documentation @@ -20,7 +20,7 @@ -  main.askai.core.features.router.tools +  main.askai.core.features.tools API Documentation

    -main.askai.core.features.router.tools.general

    +main.askai.core.features.tools.general

    @project: HsPyLib-AskAI @package: askai.core.features.tools.general @@ -101,30 +101,25 @@

    32 return output or "Sorry, there is nothing to display" 33 34 -35def final_answer( -36 persona_prompt: str | None = None, -37 input_variables: list[str] | None = None, -38 **prompt_args -39) -> str: -40 """Provide the final response to the user. -41 :param persona_prompt: The persona prompt to be used. -42 :param input_variables: The prompt input variables. -43 :param prompt_args: The prompt input arguments. -44 """ -45 prompt_file: PathObject = PathObject.of(prompt.append_path(f"taius/{persona_prompt}")) -46 template = PromptTemplate( -47 input_variables=input_variables or [], -48 template=prompt.read_prompt(prompt_file.filename, prompt_file.abs_dir), -49 ) -50 final_prompt = template.format(**prompt_args) -51 llm = lc_llm.create_chat_model(temperature=Temperature.COLDEST.temp) -52 response: AIMessage = llm.invoke(final_prompt) -53 output: str | None = None -54 -55 if not response or not (output := response.content) or shared.UNCERTAIN_ID in response.content: -56 output = "Sorry, I was not able to provide a helpful response." -57 -58 return output or "Sorry, the query produced no response!" +35def final_answer(persona_prompt: str | None = None, input_variables: list[str] | None = None, **prompt_args) -> str: +36 """Provide the final response to the user. +37 :param persona_prompt: The persona prompt to be used. +38 :param input_variables: The prompt input variables. +39 :param prompt_args: The prompt input arguments. +40 """ +41 prompt_file: PathObject = PathObject.of(prompt.append_path(f"taius/{persona_prompt}")) +42 template = PromptTemplate( +43 input_variables=input_variables or [], template=prompt.read_prompt(prompt_file.filename, prompt_file.abs_dir) +44 ) +45 final_prompt = template.format(**prompt_args) +46 llm = lc_llm.create_chat_model(temperature=Temperature.COLDEST.temp) +47 response: AIMessage = llm.invoke(final_prompt) +48 output: str | None = None +49 +50 if not response or not (output := response.content) or shared.UNCERTAIN_ID in response.content: +51 output = "Sorry, I was not able to provide a helpful response." +52 +53 return output or "Sorry, the query produced no response!"

    @@ -172,30 +167,25 @@
    Parameters
    -
    36def final_answer(
    -37    persona_prompt: str | None = None,
    -38    input_variables: list[str] | None = None,
    -39    **prompt_args
    -40) -> str:
    -41    """Provide the final response to the user.
    -42    :param persona_prompt: The persona prompt to be used.
    -43    :param input_variables: The prompt input variables.
    -44    :param prompt_args: The prompt input arguments.
    -45    """
    -46    prompt_file: PathObject = PathObject.of(prompt.append_path(f"taius/{persona_prompt}"))
    -47    template = PromptTemplate(
    -48        input_variables=input_variables or [],
    -49        template=prompt.read_prompt(prompt_file.filename, prompt_file.abs_dir),
    -50    )
    -51    final_prompt = template.format(**prompt_args)
    -52    llm = lc_llm.create_chat_model(temperature=Temperature.COLDEST.temp)
    -53    response: AIMessage = llm.invoke(final_prompt)
    -54    output: str | None = None
    -55
    -56    if not response or not (output := response.content) or shared.UNCERTAIN_ID in response.content:
    -57        output = "Sorry, I was not able to provide a helpful response."
    -58
    -59    return output or "Sorry, the query produced no response!"
    +            
    36def final_answer(persona_prompt: str | None = None, input_variables: list[str] | None = None, **prompt_args) -> str:
    +37    """Provide the final response to the user.
    +38    :param persona_prompt: The persona prompt to be used.
    +39    :param input_variables: The prompt input variables.
    +40    :param prompt_args: The prompt input arguments.
    +41    """
    +42    prompt_file: PathObject = PathObject.of(prompt.append_path(f"taius/{persona_prompt}"))
    +43    template = PromptTemplate(
    +44        input_variables=input_variables or [], template=prompt.read_prompt(prompt_file.filename, prompt_file.abs_dir)
    +45    )
    +46    final_prompt = template.format(**prompt_args)
    +47    llm = lc_llm.create_chat_model(temperature=Temperature.COLDEST.temp)
    +48    response: AIMessage = llm.invoke(final_prompt)
    +49    output: str | None = None
    +50
    +51    if not response or not (output := response.content) or shared.UNCERTAIN_ID in response.content:
    +52        output = "Sorry, I was not able to provide a helpful response."
    +53
    +54    return output or "Sorry, the query produced no response!"
     
    @@ -265,7 +255,7 @@
    Parameters
    script.async = true; script.onload = () => resolve(window.pdocSearch); script.onerror = (e) => reject(e); - script.src = "../../../../../../search.js"; + script.src = "../../../../../search.js"; document.getElementsByTagName("head")[0].appendChild(script); }); } catch (e) { @@ -307,7 +297,7 @@
    Parameters
    } for (let result of results.slice(0, 10)) { let doc = result.doc; - let url = `../../../../../../${doc.modulename.replaceAll(".", "/")}.html`; + let url = `../../../../../${doc.modulename.replaceAll(".", "/")}.html`; if (doc.qualname) { url += `#${doc.qualname}`; } diff --git a/docs/api/askai/main/askai/core/features/router/tools/generation.html b/docs/api/askai/main/askai/core/features/tools/generation.html similarity index 97% rename from docs/api/askai/main/askai/core/features/router/tools/generation.html rename to docs/api/askai/main/askai/core/features/tools/generation.html index 94b81c70..984b08e0 100644 --- a/docs/api/askai/main/askai/core/features/router/tools/generation.html +++ b/docs/api/askai/main/askai/core/features/tools/generation.html @@ -3,8 +3,8 @@ - - main.askai.core.features.router.tools.generation API documentation + + main.askai.core.features.tools.generation API documentation @@ -20,7 +20,7 @@ -  main.askai.core.features.router.tools +  main.askai.core.features.tools API Documentation

    -main.askai.core.features.router.tools.generation

    +main.askai.core.features.tools.generation

    @project: HsPyLib-AskAI @package: askai.core.features.tools.generation @@ -141,7 +141,7 @@

    72 with open(filename, "w") as f_path_name: 73 lang, content = extract_codeblock(output) 74 f_path_name.write(f"\n{content}\n") -75 output = f"{lang.title()} content successfully saved as: '{filename}'\n" +75 output = f"{lang.title() if lang else 'The '} content was successfully saved as: '{filename}'\n" 76 if not os.path.exists(filepath): 77 output = f"Sorry, the AI was unable save the content at: '{filepath}' !" 78 else: @@ -227,7 +227,7 @@
    Parameters
    73 with open(filename, "w") as f_path_name: 74 lang, content = extract_codeblock(output) 75 f_path_name.write(f"\n{content}\n") -76 output = f"{lang.title()} content successfully saved as: '{filename}'\n" +76 output = f"{lang.title() if lang else 'The '} content was successfully saved as: '{filename}'\n" 77 if not os.path.exists(filepath): 78 output = f"Sorry, the AI was unable save the content at: '{filepath}' !" 79 else: @@ -302,7 +302,7 @@
    Parameters
    script.async = true; script.onload = () => resolve(window.pdocSearch); script.onerror = (e) => reject(e); - script.src = "../../../../../../search.js"; + script.src = "../../../../../search.js"; document.getElementsByTagName("head")[0].appendChild(script); }); } catch (e) { @@ -344,7 +344,7 @@
    Parameters
    } for (let result of results.slice(0, 10)) { let doc = result.doc; - let url = `../../../../../../${doc.modulename.replaceAll(".", "/")}.html`; + let url = `../../../../../${doc.modulename.replaceAll(".", "/")}.html`; if (doc.qualname) { url += `#${doc.qualname}`; } diff --git a/docs/api/askai/main/askai/core/features/router/tools/summarization.html b/docs/api/askai/main/askai/core/features/tools/summarization.html similarity index 98% rename from docs/api/askai/main/askai/core/features/router/tools/summarization.html rename to docs/api/askai/main/askai/core/features/tools/summarization.html index c38629bd..abd43a6f 100644 --- a/docs/api/askai/main/askai/core/features/router/tools/summarization.html +++ b/docs/api/askai/main/askai/core/features/tools/summarization.html @@ -3,8 +3,8 @@ - - main.askai.core.features.router.tools.summarization API documentation + + main.askai.core.features.tools.summarization API documentation @@ -20,7 +20,7 @@ -  main.askai.core.features.router.tools +  main.askai.core.features.tools API Documentation

    -main.askai.core.features.router.tools.summarization

    +main.askai.core.features.tools.summarization

    @project: HsPyLib-AskAI @package: askai.core.features.tools.summarization @@ -211,7 +211,7 @@

    Parameters
    script.async = true; script.onload = () => resolve(window.pdocSearch); script.onerror = (e) => reject(e); - script.src = "../../../../../../search.js"; + script.src = "../../../../../search.js"; document.getElementsByTagName("head")[0].appendChild(script); }); } catch (e) { @@ -253,7 +253,7 @@
    Parameters
    } for (let result of results.slice(0, 10)) { let doc = result.doc; - let url = `../../../../../../${doc.modulename.replaceAll(".", "/")}.html`; + let url = `../../../../../${doc.modulename.replaceAll(".", "/")}.html`; if (doc.qualname) { url += `#${doc.qualname}`; } diff --git a/docs/api/askai/main/askai/core/features/router/tools/terminal.html b/docs/api/askai/main/askai/core/features/tools/terminal.html similarity index 90% rename from docs/api/askai/main/askai/core/features/router/tools/terminal.html rename to docs/api/askai/main/askai/core/features/tools/terminal.html index 69e3c4c5..ad43686d 100644 --- a/docs/api/askai/main/askai/core/features/router/tools/terminal.html +++ b/docs/api/askai/main/askai/core/features/tools/terminal.html @@ -3,8 +3,8 @@ - - main.askai.core.features.router.tools.terminal API documentation + + main.askai.core.features.tools.terminal API documentation @@ -20,7 +20,7 @@ -  main.askai.core.features.router.tools +  main.askai.core.features.tools API Documentation

    -main.askai.core.features.router.tools.terminal

    +main.askai.core.features.tools.terminal

    @project: HsPyLib-AskAI @package: askai.core.features.tools.terminal @@ -86,125 +86,126 @@

    14""" 15from askai.core.askai_events import events 16from askai.core.askai_messages import msg - 17from askai.core.features.validation.accuracy import resolve_x_refs - 18from askai.core.support.shared_instances import shared - 19from askai.core.support.utilities import extract_path, media_type_of - 20from clitt.core.term.terminal import Terminal - 21from functools import partial - 22from hspylib.core.config.path_object import PathObject - 23from hspylib.modules.application.exit_status import ExitStatus - 24from os.path import expandvars - 25from shutil import which - 26from typing import Tuple - 27 - 28import logging as log - 29import os - 30import re - 31 + 17from askai.core.features.router.task_accuracy import resolve_x_refs + 18from askai.core.model.ai_reply import AIReply + 19from askai.core.support.shared_instances import shared + 20from askai.core.support.utilities import extract_path, media_type_of + 21from clitt.core.term.terminal import Terminal + 22from functools import partial + 23from hspylib.core.config.path_object import PathObject + 24from hspylib.modules.application.exit_status import ExitStatus + 25from os.path import expandvars + 26from shutil import which + 27from typing import Tuple + 28 + 29import logging as log + 30import os + 31import re 32 - 33def list_contents(folder: str, filters: str) -> str: - 34 """List the contents of a folder. - 35 :param folder: The folder to list contents from. - 36 :param filters: The optional listing filters (file glob). - 37 """ - 38 - 39 def _build_filters_() -> str: - 40 return "\\( " + "-o".join([f' -name "{f}" ' for f in re.split(r"[,;|]\s?", filters)]).strip() + " \\)" - 41 - 42 path_obj = PathObject.of(folder) - 43 if path_obj.exists and path_obj.is_dir: - 44 cmd_line: str = ( - 45 f'find {folder} -maxdepth 1 -type f {_build_filters_() if filters else ""} ' - 46 f"-exec ls -lLht {{}} + 2>/dev/null | sort -k9,9" - 47 ) - 48 status, output = _execute_bash(cmd_line) - 49 if status: - 50 if output: - 51 return f"Listing the contents of: `{folder}`:\n\n{output}\n" - 52 return "" if filters else f"The folder: '{folder}' is empty." - 53 - 54 return f"Error: Could not list folder: '{folder}'!" - 55 + 33 + 34def list_contents(folder: str, filters: str) -> str: + 35 """List the contents of a folder. + 36 :param folder: The folder to list contents from. + 37 :param filters: The optional listing filters (file glob). + 38 """ + 39 + 40 def _build_filters_() -> str: + 41 return "\\( " + "-o".join([f' -name "{f}" ' for f in re.split(r"[,;|]\s?", filters)]).strip() + " \\)" + 42 + 43 path_obj = PathObject.of(folder) + 44 if path_obj.exists and path_obj.is_dir: + 45 cmd_line: str = ( + 46 f'find {folder} -maxdepth 1 -type f {_build_filters_() if filters else ""} ' + 47 f"-exec ls -lLht {{}} + 2>/dev/null | sort -k9,9" + 48 ) + 49 status, output = _execute_bash(cmd_line) + 50 if status: + 51 if output: + 52 return f"Listing the contents of: `{folder}`:\n\n{output}\n" + 53 return "" if filters else f"The folder: '{folder}' is empty." + 54 + 55 return f"Error: Could not list folder: '{folder}'!" 56 - 57def open_command(path_name: str) -> str: - 58 """Open the specified path, regardless if it's a file, folder or application. - 59 :param path_name: The file path to open. - 60 """ - 61 posix_path: PathObject = PathObject.of(path_name) - 62 if not posix_path.exists: - 63 # Attempt to resolve cross-references - 64 if history := str(shared.context.flat("HISTORY") or ""): - 65 if (x_referenced := resolve_x_refs(path_name, history)) and x_referenced != shared.UNCERTAIN_ID: - 66 x_ref_path: PathObject = PathObject.of(x_referenced) - 67 posix_path: PathObject = x_ref_path if x_ref_path.exists else posix_path - 68 - 69 if posix_path.exists: - 70 # find the best app to open the file. - 71 path_name: str = str(posix_path) - 72 mtype: tuple[str, ...] = media_type_of(path_name) or ('text', 'plain') - 73 match mtype: - 74 case ("audio", _) | ("video", _): - 75 fn_open = partial(_execute_bash, f"ffplay -v 0 -autoexit {path_name} &>/dev/null") - 76 case ("text", _): - 77 fn_open = partial(_execute_bash, f"cat {path_name}") - 78 case ("inode", "directory"): - 79 fn_open = partial(list_contents, path_name) - 80 case _: - 81 fn_open = partial(_execute_bash, f"open {path_name} 2>/dev/null") - 82 status, output = fn_open() - 83 if status: - 84 if not output: - 85 output = f"{mtype[0].title()}: `{path_name}` was successfully opened!" - 86 else: - 87 output = f"Showing the contents of: `{path_name}`:\n\n{output}\n" - 88 return output - 89 else: - 90 return f"Error: File was not found: '{path_name}'!" - 91 - 92 return f"Error: Failed to open: '{path_name}'!" - 93 + 57 + 58def open_command(path_name: str) -> str: + 59 """Open the specified path, regardless if it's a file, folder or application. + 60 :param path_name: The file path to open. + 61 """ + 62 posix_path: PathObject = PathObject.of(path_name) + 63 if not posix_path.exists: + 64 # Attempt to resolve cross-references + 65 if history := str(shared.context.flat("HISTORY") or ""): + 66 if (x_referenced := resolve_x_refs(path_name, history)) and x_referenced != shared.UNCERTAIN_ID: + 67 x_ref_path: PathObject = PathObject.of(x_referenced) + 68 posix_path: PathObject = x_ref_path if x_ref_path.exists else posix_path + 69 + 70 if posix_path.exists: + 71 # find the best app to open the file. + 72 path_name: str = str(posix_path) + 73 mtype: tuple[str, ...] = media_type_of(path_name) or ("text", "plain") + 74 match mtype: + 75 case ("audio", _) | ("video", _): + 76 fn_open = partial(_execute_bash, f"ffplay -v 0 -autoexit {path_name} &>/dev/null") + 77 case ("text", _): + 78 fn_open = partial(_execute_bash, f"cat {path_name}") + 79 case ("inode", "directory"): + 80 fn_open = partial(list_contents, path_name) + 81 case _: + 82 fn_open = partial(_execute_bash, f"open {path_name} 2>/dev/null") + 83 status, output = fn_open() + 84 if status: + 85 if not output: + 86 output = f"{mtype[0].title()}: `{path_name}` was successfully opened!" + 87 else: + 88 output = f"Showing the contents of: `{path_name}`:\n\n{output}\n" + 89 return output + 90 else: + 91 return f"Error: File was not found: '{path_name}'!" + 92 + 93 return f"Error: Failed to open: '{path_name}'!" 94 - 95def execute_command(shell: str, command_line: str) -> str: - 96 """Execute a terminal command using the specified language. - 97 :param shell: The shell type to be used. - 98 :param command_line: The command line to be executed. - 99 """ -100 match shell: -101 case "bash": -102 status, output = _execute_bash(command_line) -103 case _: -104 raise NotImplementedError(f"'{shell}' is not supported!") -105 -106 return output or (msg.cmd_success(command_line) if status else msg.cmd_failed(command_line)) -107 + 95 + 96def execute_command(shell: str, command_line: str) -> str: + 97 """Execute a terminal command using the specified language. + 98 :param shell: The shell type to be used. + 99 :param command_line: The command line to be executed. +100 """ +101 match shell: +102 case "bash": +103 status, output = _execute_bash(command_line) +104 case _: +105 raise NotImplementedError(f"'{shell}' is not supported!") +106 +107 return output or (msg.cmd_success(command_line) if status else msg.cmd_failed(command_line)) 108 -109def _execute_bash(command_line: str) -> Tuple[bool, str]: -110 """Execute the provided command line using bash. -111 :param command_line: The command line to be executed in bash. -112 """ -113 status, output = False, "" -114 if (command := command_line.split(" ")[0].strip()) and which(command): -115 command = expandvars(command_line.replace("~/", f"{os.getenv('HOME')}/").strip()) -116 log.info("Executing command `%s'", command) -117 events.reply.emit(message=msg.executing(command_line), verbosity="debug") -118 output, exit_code = Terminal.INSTANCE.shell_exec(command, shell=True) -119 if exit_code == ExitStatus.SUCCESS: -120 log.info("Command succeeded: \n|-CODE=%s \n|-PATH: %s \n|-CMD: %s ", exit_code, os.getcwd(), command) -121 if _path_ := extract_path(command): -122 os.chdir(_path_) -123 log.info("Current directory changed to '%s'", _path_) -124 else: -125 log.warning("Directory '%s' does not exist. Current dir unchanged!", _path_) -126 if output: -127 output = f"\n```bash\n{output}```\n" -128 status = True -129 else: -130 log.error("Command failed.\nCODE=%s \nPATH=%s \nCMD=%s ", exit_code, os.getcwd(), command) -131 output = msg.cmd_failed(command) -132 else: -133 output = msg.cmd_no_exist(command) -134 -135 return status, output +109 +110def _execute_bash(command_line: str) -> Tuple[bool, str]: +111 """Execute the provided command line using bash. +112 :param command_line: The command line to be executed in bash. +113 """ +114 status, output = False, "" +115 if (command := command_line.split(" ")[0].strip()) and which(command): +116 command = expandvars(command_line.replace("~/", f"{os.getenv('HOME')}/").strip()) +117 log.info("Executing command `%s'", command) +118 events.reply.emit(reply=AIReply.full(msg.executing(command_line))) +119 output, err_out, exit_code = Terminal.INSTANCE.shell_exec(command, shell=True) +120 if exit_code == ExitStatus.SUCCESS: +121 log.info("Command succeeded: \n|-CODE=%s \n|-PATH: %s \n|-CMD: %s ", exit_code, os.getcwd(), command) +122 if _path_ := extract_path(command): +123 os.chdir(_path_) +124 log.info("Current directory changed to '%s'", _path_) +125 else: +126 log.warning("Directory '%s' does not exist. Current dir unchanged!", _path_) +127 if output: +128 output = f"\n```bash\n{output}```\n" +129 status = True +130 else: +131 log.error("Command failed.\nCODE=%s \nPATH=%s \nCMD=%s ", exit_code, os.getcwd(), command) +132 output = msg.cmd_failed(command, err_out) +133 else: +134 output = msg.cmd_no_exist(command) +135 +136 return status, output

    @@ -220,28 +221,28 @@

    -
    34def list_contents(folder: str, filters: str) -> str:
    -35    """List the contents of a folder.
    -36    :param folder: The folder to list contents from.
    -37    :param filters: The optional listing filters (file glob).
    -38    """
    -39
    -40    def _build_filters_() -> str:
    -41        return "\\( " + "-o".join([f' -name "{f}" ' for f in re.split(r"[,;|]\s?", filters)]).strip() + " \\)"
    -42
    -43    path_obj = PathObject.of(folder)
    -44    if path_obj.exists and path_obj.is_dir:
    -45        cmd_line: str = (
    -46            f'find {folder} -maxdepth 1 -type f {_build_filters_() if filters else ""} '
    -47            f"-exec ls -lLht {{}} + 2>/dev/null | sort -k9,9"
    -48        )
    -49        status, output = _execute_bash(cmd_line)
    -50        if status:
    -51            if output:
    -52                return f"Listing the contents of: `{folder}`:\n\n{output}\n"
    -53            return "" if filters else f"The folder: '{folder}' is empty."
    -54
    -55    return f"Error: Could not list folder: '{folder}'!"
    +            
    35def list_contents(folder: str, filters: str) -> str:
    +36    """List the contents of a folder.
    +37    :param folder: The folder to list contents from.
    +38    :param filters: The optional listing filters (file glob).
    +39    """
    +40
    +41    def _build_filters_() -> str:
    +42        return "\\( " + "-o".join([f' -name "{f}" ' for f in re.split(r"[,;|]\s?", filters)]).strip() + " \\)"
    +43
    +44    path_obj = PathObject.of(folder)
    +45    if path_obj.exists and path_obj.is_dir:
    +46        cmd_line: str = (
    +47            f'find {folder} -maxdepth 1 -type f {_build_filters_() if filters else ""} '
    +48            f"-exec ls -lLht {{}} + 2>/dev/null | sort -k9,9"
    +49        )
    +50        status, output = _execute_bash(cmd_line)
    +51        if status:
    +52            if output:
    +53                return f"Listing the contents of: `{folder}`:\n\n{output}\n"
    +54            return "" if filters else f"The folder: '{folder}' is empty."
    +55
    +56    return f"Error: Could not list folder: '{folder}'!"
     
    @@ -268,42 +269,42 @@
    Parameters
    -
    58def open_command(path_name: str) -> str:
    -59    """Open the specified path, regardless if it's a file, folder or application.
    -60    :param path_name: The file path to open.
    -61    """
    -62    posix_path: PathObject = PathObject.of(path_name)
    -63    if not posix_path.exists:
    -64        # Attempt to resolve cross-references
    -65        if history := str(shared.context.flat("HISTORY") or ""):
    -66            if (x_referenced := resolve_x_refs(path_name, history)) and x_referenced != shared.UNCERTAIN_ID:
    -67                x_ref_path: PathObject = PathObject.of(x_referenced)
    -68                posix_path: PathObject = x_ref_path if x_ref_path.exists else posix_path
    -69
    -70    if posix_path.exists:
    -71        # find the best app to open the file.
    -72        path_name: str = str(posix_path)
    -73        mtype: tuple[str, ...] = media_type_of(path_name) or ('text', 'plain')
    -74        match mtype:
    -75            case ("audio", _) | ("video", _):
    -76                fn_open = partial(_execute_bash, f"ffplay -v 0 -autoexit {path_name} &>/dev/null")
    -77            case ("text", _):
    -78                fn_open = partial(_execute_bash, f"cat {path_name}")
    -79            case ("inode", "directory"):
    -80                fn_open = partial(list_contents, path_name)
    -81            case _:
    -82                fn_open = partial(_execute_bash, f"open {path_name} 2>/dev/null")
    -83        status, output = fn_open()
    -84        if status:
    -85            if not output:
    -86                output = f"{mtype[0].title()}: `{path_name}` was successfully opened!"
    -87            else:
    -88                output = f"Showing the contents of: `{path_name}`:\n\n{output}\n"
    -89            return output
    -90    else:
    -91        return f"Error: File was not found: '{path_name}'!"
    -92
    -93    return f"Error: Failed to open: '{path_name}'!"
    +            
    59def open_command(path_name: str) -> str:
    +60    """Open the specified path, regardless if it's a file, folder or application.
    +61    :param path_name: The file path to open.
    +62    """
    +63    posix_path: PathObject = PathObject.of(path_name)
    +64    if not posix_path.exists:
    +65        # Attempt to resolve cross-references
    +66        if history := str(shared.context.flat("HISTORY") or ""):
    +67            if (x_referenced := resolve_x_refs(path_name, history)) and x_referenced != shared.UNCERTAIN_ID:
    +68                x_ref_path: PathObject = PathObject.of(x_referenced)
    +69                posix_path: PathObject = x_ref_path if x_ref_path.exists else posix_path
    +70
    +71    if posix_path.exists:
    +72        # find the best app to open the file.
    +73        path_name: str = str(posix_path)
    +74        mtype: tuple[str, ...] = media_type_of(path_name) or ("text", "plain")
    +75        match mtype:
    +76            case ("audio", _) | ("video", _):
    +77                fn_open = partial(_execute_bash, f"ffplay -v 0 -autoexit {path_name} &>/dev/null")
    +78            case ("text", _):
    +79                fn_open = partial(_execute_bash, f"cat {path_name}")
    +80            case ("inode", "directory"):
    +81                fn_open = partial(list_contents, path_name)
    +82            case _:
    +83                fn_open = partial(_execute_bash, f"open {path_name} 2>/dev/null")
    +84        status, output = fn_open()
    +85        if status:
    +86            if not output:
    +87                output = f"{mtype[0].title()}: `{path_name}` was successfully opened!"
    +88            else:
    +89                output = f"Showing the contents of: `{path_name}`:\n\n{output}\n"
    +90            return output
    +91    else:
    +92        return f"Error: File was not found: '{path_name}'!"
    +93
    +94    return f"Error: Failed to open: '{path_name}'!"
     
    @@ -329,18 +330,18 @@
    Parameters
    -
     96def execute_command(shell: str, command_line: str) -> str:
    - 97    """Execute a terminal command using the specified language.
    - 98    :param shell: The shell type to be used.
    - 99    :param command_line: The command line to be executed.
    -100    """
    -101    match shell:
    -102        case "bash":
    -103            status, output = _execute_bash(command_line)
    -104        case _:
    -105            raise NotImplementedError(f"'{shell}' is not supported!")
    -106
    -107    return output or (msg.cmd_success(command_line) if status else msg.cmd_failed(command_line))
    +            
     97def execute_command(shell: str, command_line: str) -> str:
    + 98    """Execute a terminal command using the specified language.
    + 99    :param shell: The shell type to be used.
    +100    :param command_line: The command line to be executed.
    +101    """
    +102    match shell:
    +103        case "bash":
    +104            status, output = _execute_bash(command_line)
    +105        case _:
    +106            raise NotImplementedError(f"'{shell}' is not supported!")
    +107
    +108    return output or (msg.cmd_success(command_line) if status else msg.cmd_failed(command_line))
     
    @@ -409,7 +410,7 @@
    Parameters
    script.async = true; script.onload = () => resolve(window.pdocSearch); script.onerror = (e) => reject(e); - script.src = "../../../../../../search.js"; + script.src = "../../../../../search.js"; document.getElementsByTagName("head")[0].appendChild(script); }); } catch (e) { @@ -451,7 +452,7 @@
    Parameters
    } for (let result of results.slice(0, 10)) { let doc = result.doc; - let url = `../../../../../../${doc.modulename.replaceAll(".", "/")}.html`; + let url = `../../../../../${doc.modulename.replaceAll(".", "/")}.html`; if (doc.qualname) { url += `#${doc.qualname}`; } diff --git a/docs/api/askai/main/askai/core/features/tools/vision.html b/docs/api/askai/main/askai/core/features/tools/vision.html new file mode 100644 index 00000000..f16dad18 --- /dev/null +++ b/docs/api/askai/main/askai/core/features/tools/vision.html @@ -0,0 +1,717 @@ + + + + + + + main.askai.core.features.tools.vision API documentation + + + + + + + + + +
    +
    +

    +main.askai.core.features.tools.vision

    + + + + + + +
      1from askai.core.askai_events import events
    +  2from askai.core.askai_messages import msg
    +  3from askai.core.component.cache_service import PICTURE_DIR
    +  4from askai.core.engine.ai_vision import AIVision
    +  5from askai.core.features.router.task_accuracy import resolve_x_refs
    +  6from askai.core.model.ai_reply import AIReply
    +  7from askai.core.model.image_result import ImageResult
    +  8from askai.core.support.shared_instances import shared
    +  9from hspylib.core.config.path_object import PathObject
    + 10from hspylib.core.enums.enumeration import Enumeration
    + 11from hspylib.core.metaclass.classpath import AnyPath
    + 12from hspylib.core.preconditions import check_argument
    + 13from PIL import Image
    + 14from textwrap import indent
    + 15from transformers import BlipForConditionalGeneration, BlipProcessor
    + 16
    + 17import os
    + 18import pyautogui
    + 19import torch
    + 20
    + 21
    + 22class HFModel(Enumeration):
    + 23    """Available Hugging Face models"""
    + 24
    + 25    # fmt: off
    + 26    SF_BLIP_BASE    = "Salesforce/blip-image-captioning-base"
    + 27    SF_BLIP_LARGE   = "Salesforce/blip-image-captioning-large"
    + 28    # fmt: on
    + 29
    + 30    @staticmethod
    + 31    def default() -> "HFModel":
    + 32        """Return the default HF model."""
    + 33        return HFModel.SF_BLIP_BASE
    + 34
    + 35
    + 36def offline_captioner(path_name: AnyPath) -> str:
    + 37    """This tool is used to describe an image.
    + 38    :param path_name: The path of the image to describe.
    + 39    """
    + 40
    + 41    caption: str = "Not available"
    + 42
    + 43    posix_path: PathObject = PathObject.of(path_name)
    + 44    if not posix_path.exists:
    + 45        # Attempt to resolve cross-references
    + 46        if history := str(shared.context.flat("HISTORY") or ""):
    + 47            if (x_referenced := resolve_x_refs(path_name, history)) and x_referenced != shared.UNCERTAIN_ID:
    + 48                x_ref_path: PathObject = PathObject.of(x_referenced)
    + 49                posix_path: PathObject = x_ref_path if x_ref_path.exists else posix_path
    + 50
    + 51    if posix_path.exists:
    + 52        events.reply.emit(reply=AIReply.full(msg.describe_image(posix_path)))
    + 53        # Use GPU if it's available
    + 54        device = "cuda" if torch.cuda.is_available() else "cpu"
    + 55        image = Image.open(str(posix_path)).convert("RGB")
    + 56        model_id: str = HFModel.default().value
    + 57        match model_id.casefold():
    + 58            case model if "blip-" in model:
    + 59                model = BlipForConditionalGeneration.from_pretrained(model_id).to(device)
    + 60                processor = BlipProcessor.from_pretrained(model_id)
    + 61            case _:
    + 62                raise ValueError(f"Unsupported model: '{model_id}'")
    + 63        inputs = processor(images=image, return_tensors="pt").to(device)
    + 64        outputs = model.generate(**inputs)
    + 65        caption = processor.decode(outputs[0], skip_special_tokens=True)
    + 66        caption = caption.title() if caption else "I could not caption the image"
    + 67
    + 68    return caption
    + 69
    + 70
    + 71def image_captioner(path_name: AnyPath, load_dir: AnyPath | None = None) -> str:
    + 72    """This tool is used to describe an image.
    + 73    :param path_name: The path of the image to describe.
    + 74    :param load_dir: Optional directory path for loading related resources.
    + 75    :return: A string containing the description of the image, or None if the description could not be generated.
    + 76    """
    + 77    image_caption: str = "Not available"
    + 78    posix_path: PathObject = PathObject.of(path_name)
    + 79
    + 80    if not posix_path.exists:
    + 81        # Attempt to resolve cross-references
    + 82        if history := str(shared.context.flat("HISTORY") or ""):
    + 83            if (x_referenced := resolve_x_refs(path_name, history)) and x_referenced != shared.UNCERTAIN_ID:
    + 84                x_ref_path: PathObject = PathObject.of(x_referenced)
    + 85                posix_path: PathObject = x_ref_path if x_ref_path.exists else posix_path
    + 86
    + 87    if posix_path.exists:
    + 88        events.reply.emit(reply=AIReply.full(msg.describe_image(posix_path)))
    + 89        vision: AIVision = shared.engine.vision()
    + 90        image_caption = vision.caption(posix_path.filename, load_dir or posix_path.abs_dir or PICTURE_DIR)
    + 91
    + 92    return image_caption
    + 93
    + 94
    + 95def parse_caption(image_caption: str) -> str:
    + 96    """Parse the given image caption.
    + 97    :param image_caption: The caption to parse.
    + 98    :return: The parsed caption as a string.
    + 99    """
    +100    if image_caption:
    +101        result: ImageResult = ImageResult.of(image_caption)
    +102        ln: str = os.linesep
    +103        people_desc: str = ""
    +104        if result.people_description:
    +105            people_desc: str = f"- **People:** `({result.people_count})`\n" + indent(
    +106                f"- {'- '.join([f'`{ppl}{ln}`' + ln for ppl in result.people_description])}", "    "
    +107            )
    +108        # fmt: off
    +109        return (
    +110            f"- **Description:** `{result.env_description}`\n"
    +111            f"- **Objects:** `{', '.join(result.main_objects)}`\n"
    +112            f"{people_desc or ''}"
    +113        )  # fmt: on
    +114
    +115    return msg.no_caption()
    +116
    +117
    +118def take_screenshot(path_name: AnyPath, load_dir: AnyPath | None = None) -> str:
    +119    """Takes a screenshot and saves it to the specified path.
    +120    :param path_name: The path where the screenshot will be saved.
    +121    :param load_dir: Optional directory to save the screenshot.
    +122    :return: The path to the saved screenshot.
    +123    """
    +124
    +125    posix_path: PathObject = PathObject.of(path_name)
    +126    check_argument(os.path.exists(posix_path.abs_dir))
    +127    screenshot = pyautogui.screenshot()
    +128    _, ext = os.path.splitext(posix_path.filename)
    +129    if ext.casefold().endswith((".jpg", ".jpeg")):
    +130        screenshot = screenshot.convert("RGB")
    +131    final_path: str = os.path.join(load_dir or posix_path.abs_dir or PICTURE_DIR, posix_path.filename)
    +132    screenshot.save(final_path)
    +133    events.reply.emit(reply=AIReply.full(msg.screenshot_saved(final_path)))
    +134    desktop_caption = image_captioner(final_path, load_dir)
    +135
    +136    return desktop_caption
    +
    + + +
    +
    + +
    + + class + HFModel(hspylib.core.enums.enumeration.Enumeration): + + + +
    + +
    23class HFModel(Enumeration):
    +24    """Available Hugging Face models"""
    +25
    +26    # fmt: off
    +27    SF_BLIP_BASE    = "Salesforce/blip-image-captioning-base"
    +28    SF_BLIP_LARGE   = "Salesforce/blip-image-captioning-large"
    +29    # fmt: on
    +30
    +31    @staticmethod
    +32    def default() -> "HFModel":
    +33        """Return the default HF model."""
    +34        return HFModel.SF_BLIP_BASE
    +
    + + +

    Available Hugging Face models

    +
    + + +
    +
    + SF_BLIP_BASE = +SF_BLIP_BASE + + +
    + + + + +
    +
    +
    + SF_BLIP_LARGE = +SF_BLIP_LARGE + + +
    + + + + +
    +
    + +
    +
    @staticmethod
    + + def + default() -> HFModel: + + + +
    + +
    31    @staticmethod
    +32    def default() -> "HFModel":
    +33        """Return the default HF model."""
    +34        return HFModel.SF_BLIP_BASE
    +
    + + +

    Return the default HF model.

    +
    + + +
    +
    +
    Inherited Members
    +
    +
    hspylib.core.enums.enumeration.Enumeration
    +
    names
    +
    values
    +
    value_of
    +
    of_value
    +
    compose
    +
    key
    + +
    +
    enum.Enum
    +
    name
    +
    value
    + +
    +
    +
    +
    +
    + +
    + + def + offline_captioner(path_name: Union[pathlib.Path, str, NoneType]) -> str: + + + +
    + +
    37def offline_captioner(path_name: AnyPath) -> str:
    +38    """This tool is used to describe an image.
    +39    :param path_name: The path of the image to describe.
    +40    """
    +41
    +42    caption: str = "Not available"
    +43
    +44    posix_path: PathObject = PathObject.of(path_name)
    +45    if not posix_path.exists:
    +46        # Attempt to resolve cross-references
    +47        if history := str(shared.context.flat("HISTORY") or ""):
    +48            if (x_referenced := resolve_x_refs(path_name, history)) and x_referenced != shared.UNCERTAIN_ID:
    +49                x_ref_path: PathObject = PathObject.of(x_referenced)
    +50                posix_path: PathObject = x_ref_path if x_ref_path.exists else posix_path
    +51
    +52    if posix_path.exists:
    +53        events.reply.emit(reply=AIReply.full(msg.describe_image(posix_path)))
    +54        # Use GPU if it's available
    +55        device = "cuda" if torch.cuda.is_available() else "cpu"
    +56        image = Image.open(str(posix_path)).convert("RGB")
    +57        model_id: str = HFModel.default().value
    +58        match model_id.casefold():
    +59            case model if "blip-" in model:
    +60                model = BlipForConditionalGeneration.from_pretrained(model_id).to(device)
    +61                processor = BlipProcessor.from_pretrained(model_id)
    +62            case _:
    +63                raise ValueError(f"Unsupported model: '{model_id}'")
    +64        inputs = processor(images=image, return_tensors="pt").to(device)
    +65        outputs = model.generate(**inputs)
    +66        caption = processor.decode(outputs[0], skip_special_tokens=True)
    +67        caption = caption.title() if caption else "I could not caption the image"
    +68
    +69    return caption
    +
    + + +

    This tool is used to describe an image.

    + +
    Parameters
    + +
      +
    • path_name: The path of the image to describe.
    • +
    +
    + + +
    +
    + +
    + + def + image_captioner( path_name: Union[pathlib.Path, str, NoneType], load_dir: Union[pathlib.Path, str, NoneType] = None) -> str: + + + +
    + +
    72def image_captioner(path_name: AnyPath, load_dir: AnyPath | None = None) -> str:
    +73    """This tool is used to describe an image.
    +74    :param path_name: The path of the image to describe.
    +75    :param load_dir: Optional directory path for loading related resources.
    +76    :return: A string containing the description of the image, or None if the description could not be generated.
    +77    """
    +78    image_caption: str = "Not available"
    +79    posix_path: PathObject = PathObject.of(path_name)
    +80
    +81    if not posix_path.exists:
    +82        # Attempt to resolve cross-references
    +83        if history := str(shared.context.flat("HISTORY") or ""):
    +84            if (x_referenced := resolve_x_refs(path_name, history)) and x_referenced != shared.UNCERTAIN_ID:
    +85                x_ref_path: PathObject = PathObject.of(x_referenced)
    +86                posix_path: PathObject = x_ref_path if x_ref_path.exists else posix_path
    +87
    +88    if posix_path.exists:
    +89        events.reply.emit(reply=AIReply.full(msg.describe_image(posix_path)))
    +90        vision: AIVision = shared.engine.vision()
    +91        image_caption = vision.caption(posix_path.filename, load_dir or posix_path.abs_dir or PICTURE_DIR)
    +92
    +93    return image_caption
    +
    + + +

    This tool is used to describe an image.

    + +
    Parameters
    + +
      +
    • path_name: The path of the image to describe.
    • +
    • load_dir: Optional directory path for loading related resources.
    • +
    + +
    Returns
    + +
    +

    A string containing the description of the image, or None if the description could not be generated.

    +
    +
    + + +
    +
    + +
    + + def + parse_caption(image_caption: str) -> str: + + + +
    + +
     96def parse_caption(image_caption: str) -> str:
    + 97    """Parse the given image caption.
    + 98    :param image_caption: The caption to parse.
    + 99    :return: The parsed caption as a string.
    +100    """
    +101    if image_caption:
    +102        result: ImageResult = ImageResult.of(image_caption)
    +103        ln: str = os.linesep
    +104        people_desc: str = ""
    +105        if result.people_description:
    +106            people_desc: str = f"- **People:** `({result.people_count})`\n" + indent(
    +107                f"- {'- '.join([f'`{ppl}{ln}`' + ln for ppl in result.people_description])}", "    "
    +108            )
    +109        # fmt: off
    +110        return (
    +111            f"- **Description:** `{result.env_description}`\n"
    +112            f"- **Objects:** `{', '.join(result.main_objects)}`\n"
    +113            f"{people_desc or ''}"
    +114        )  # fmt: on
    +115
    +116    return msg.no_caption()
    +
    + + +

    Parse the given image caption.

    + +
    Parameters
    + +
      +
    • image_caption: The caption to parse.
    • +
    + +
    Returns
    + +
    +

    The parsed caption as a string.

    +
    +
    + + +
    +
    + +
    + + def + take_screenshot( path_name: Union[pathlib.Path, str, NoneType], load_dir: Union[pathlib.Path, str, NoneType] = None) -> str: + + + +
    + +
    119def take_screenshot(path_name: AnyPath, load_dir: AnyPath | None = None) -> str:
    +120    """Takes a screenshot and saves it to the specified path.
    +121    :param path_name: The path where the screenshot will be saved.
    +122    :param load_dir: Optional directory to save the screenshot.
    +123    :return: The path to the saved screenshot.
    +124    """
    +125
    +126    posix_path: PathObject = PathObject.of(path_name)
    +127    check_argument(os.path.exists(posix_path.abs_dir))
    +128    screenshot = pyautogui.screenshot()
    +129    _, ext = os.path.splitext(posix_path.filename)
    +130    if ext.casefold().endswith((".jpg", ".jpeg")):
    +131        screenshot = screenshot.convert("RGB")
    +132    final_path: str = os.path.join(load_dir or posix_path.abs_dir or PICTURE_DIR, posix_path.filename)
    +133    screenshot.save(final_path)
    +134    events.reply.emit(reply=AIReply.full(msg.screenshot_saved(final_path)))
    +135    desktop_caption = image_captioner(final_path, load_dir)
    +136
    +137    return desktop_caption
    +
    + + +

    Takes a screenshot and saves it to the specified path.

    + +
    Parameters
    + +
      +
    • path_name: The path where the screenshot will be saved.
    • +
    • load_dir: Optional directory to save the screenshot.
    • +
    + +
    Returns
    + +
    +

    The path to the saved screenshot.

    +
    +
    + + +
    +
    + + \ No newline at end of file diff --git a/docs/api/askai/main/askai/core/features/tools/webcam.html b/docs/api/askai/main/askai/core/features/tools/webcam.html new file mode 100644 index 00000000..bb05acd4 --- /dev/null +++ b/docs/api/askai/main/askai/core/features/tools/webcam.html @@ -0,0 +1,427 @@ + + + + + + + main.askai.core.features.tools.webcam API documentation + + + + + + + + + +
    +
    +

    +main.askai.core.features.tools.webcam

    + + + + + + +
     1from askai.core.askai_configs import configs
    + 2from askai.core.component.camera import camera
    + 3from askai.core.features.tools.vision import image_captioner, parse_caption
    + 4from askai.core.support.utilities import display_text
    + 5from hspylib.core.tools.text_tools import ensure_endswith, ensure_startswith
    + 6from os.path import basename
    + 7from textwrap import indent
    + 8
    + 9import os
    +10
    +11
    +12def webcam_capturer(photo_name: str | None, detect_faces: bool = False) -> str:
    +13    """Capture a photo using the webcam and save it locally. Optionally detect faces in the photo.
    +14    :param photo_name: The name of the photo file. If None, a default name will be used.
    +15    :param detect_faces: Whether to detect faces in the photo.
    +16    :return: The file path of the saved photo.
    +17    """
    +18
    +19    pic_file, pic_data = camera.capture(photo_name, with_caption=False)
    +20    face_description: str | None = None
    +21    ln: str = os.linesep
    +22
    +23    if detect_faces:
    +24        face_files, face_datas = camera.detect_faces(pic_data, photo_name)
    +25        faces: int = len(face_files)
    +26        face_description = (
    +27            (
    +28                f"- **Faces:** `({faces})`\n"
    +29                + indent(f"- {'- '.join([f'`{ff.img_path}` {ln}' for ff in face_files])}", "    ")
    +30                + f"- **Face-Captions:** `({faces})`\n"
    +31                + indent(
    +32                    f"- {'- '.join([f'*{basename(ff.img_path)}*: `{ff.img_caption}` {ln}' for ff in face_files])}",
    +33                    "    ",
    +34                )
    +35            )
    +36            if faces
    +37            else ""
    +38        )
    +39
    +40    image_description: str = parse_caption(image_captioner(pic_file.img_path))
    +41
    +42    # fmt: off
    +43    return ensure_endswith(ensure_startswith(
    +44        f"\n>   Photo Taken -> {pic_file.img_path}\n\n"
    +45        f"{image_description or ''}\n"
    +46        f"{face_description or ''}", "\n"
    +47    ), "\n")  # fmt: on
    +48
    +49
    +50def webcam_identifier(max_distance: int = configs.max_id_distance) -> str:
    +51    """Identifies the person in front of the webcam and provides a description of them.
    +52    :param max_distance: The maximum distance for identifying the person based on image similarity.
    +53    :return: A description of the identified person.
    +54    """
    +55    identity: str = "%ORANGE%  No identification was possible!%NC%"
    +56    display_text("Look at the camera...")
    +57    if photo := camera.identify(3, max_distance):
    +58        # fmt: off
    +59        identity = ensure_endswith(ensure_startswith(
    +60            f"\n>   Person Identified -> {photo.uri}\n\n"
    +61            f"- **Distance:** `{round(photo.distance, 4):.4f}/{round(max_distance, 4):.4f}`\n"
    +62            f"{photo.caption}", "\n"
    +63        ), "\n")  # fmt: on
    +64
    +65    return identity
    +
    + + +
    +
    + +
    + + def + webcam_capturer(photo_name: str | None, detect_faces: bool = False) -> str: + + + +
    + +
    13def webcam_capturer(photo_name: str | None, detect_faces: bool = False) -> str:
    +14    """Capture a photo using the webcam and save it locally. Optionally detect faces in the photo.
    +15    :param photo_name: The name of the photo file. If None, a default name will be used.
    +16    :param detect_faces: Whether to detect faces in the photo.
    +17    :return: The file path of the saved photo.
    +18    """
    +19
    +20    pic_file, pic_data = camera.capture(photo_name, with_caption=False)
    +21    face_description: str | None = None
    +22    ln: str = os.linesep
    +23
    +24    if detect_faces:
    +25        face_files, face_datas = camera.detect_faces(pic_data, photo_name)
    +26        faces: int = len(face_files)
    +27        face_description = (
    +28            (
    +29                f"- **Faces:** `({faces})`\n"
    +30                + indent(f"- {'- '.join([f'`{ff.img_path}` {ln}' for ff in face_files])}", "    ")
    +31                + f"- **Face-Captions:** `({faces})`\n"
    +32                + indent(
    +33                    f"- {'- '.join([f'*{basename(ff.img_path)}*: `{ff.img_caption}` {ln}' for ff in face_files])}",
    +34                    "    ",
    +35                )
    +36            )
    +37            if faces
    +38            else ""
    +39        )
    +40
    +41    image_description: str = parse_caption(image_captioner(pic_file.img_path))
    +42
    +43    # fmt: off
    +44    return ensure_endswith(ensure_startswith(
    +45        f"\n>   Photo Taken -> {pic_file.img_path}\n\n"
    +46        f"{image_description or ''}\n"
    +47        f"{face_description or ''}", "\n"
    +48    ), "\n")  # fmt: on
    +
    + + +

    Capture a photo using the webcam and save it locally. Optionally detect faces in the photo.

    + +
    Parameters
    + +
      +
    • photo_name: The name of the photo file. If None, a default name will be used.
    • +
    • detect_faces: Whether to detect faces in the photo.
    • +
    + +
    Returns
    + +
    +

    The file path of the saved photo.

    +
    +
    + + +
    +
    + +
    + + def + webcam_identifier(max_distance: int = 0.7) -> str: + + + +
    + +
    51def webcam_identifier(max_distance: int = configs.max_id_distance) -> str:
    +52    """Identifies the person in front of the webcam and provides a description of them.
    +53    :param max_distance: The maximum distance for identifying the person based on image similarity.
    +54    :return: A description of the identified person.
    +55    """
    +56    identity: str = "%ORANGE%  No identification was possible!%NC%"
    +57    display_text("Look at the camera...")
    +58    if photo := camera.identify(3, max_distance):
    +59        # fmt: off
    +60        identity = ensure_endswith(ensure_startswith(
    +61            f"\n>   Person Identified -> {photo.uri}\n\n"
    +62            f"- **Distance:** `{round(photo.distance, 4):.4f}/{round(max_distance, 4):.4f}`\n"
    +63            f"{photo.caption}", "\n"
    +64        ), "\n")  # fmt: on
    +65
    +66    return identity
    +
    + + +

    Identifies the person in front of the webcam and provides a description of them.

    + +
    Parameters
    + +
      +
    • max_distance: The maximum distance for identifying the person based on image similarity.
    • +
    + +
    Returns
    + +
    +

    A description of the identified person.

    +
    +
    + + +
    +
    + + \ No newline at end of file diff --git a/docs/api/askai/main/askai/core/model.html b/docs/api/askai/main/askai/core/model.html index f90faed3..61affe2a 100644 --- a/docs/api/askai/main/askai/core/model.html +++ b/docs/api/askai/main/askai/core/model.html @@ -3,7 +3,7 @@ - + main.askai.core.model API documentation @@ -30,7 +30,9 @@

    Submodules

    • action_plan
    • +
    • ai_reply
    • api_keys
    • +
    • image_result
    • model_result
    • search_result
    • summary_result
    • @@ -59,19 +61,21 @@

       1# _*_ coding: utf-8 _*_
        2#
      - 3# hspylib-askai v1.0.11
      + 3# hspylib-askai v1.0.13
        4#
        5# Package: main.askai.core.model
        6"""Package initialization."""
        7
        8__all__ = [
        9    'action_plan', 
      -10    'api_keys', 
      -11    'model_result', 
      -12    'search_result', 
      -13    'summary_result'
      -14]
      -15__version__ = '1.0.11'
      +10    'ai_reply', 
      +11    'api_keys', 
      +12    'image_result', 
      +13    'model_result', 
      +14    'search_result', 
      +15    'summary_result'
      +16]
      +17__version__ = '1.0.13'
       
      diff --git a/docs/api/askai/main/askai/core/model/action_plan.html b/docs/api/askai/main/askai/core/model/action_plan.html index 8cad5618..0e4331ef 100644 --- a/docs/api/askai/main/askai/core/model/action_plan.html +++ b/docs/api/askai/main/askai/core/model/action_plan.html @@ -3,7 +3,7 @@ - + main.askai.core.model.action_plan API documentation @@ -131,125 +131,99 @@

      26 27@dataclass 28class ActionPlan: - 29 """Keep track of the router action plan.""" - 30 - 31 question: str = None - 32 primary_goal: str = None - 33 sub_goals: list[str] = None - 34 thoughts: SimpleNamespace = None - 35 tasks: list[SimpleNamespace] = None - 36 model: ModelResult = field(default_factory=ModelResult.default) - 37 - 38 @staticmethod - 39 def _parse_response(question: str, response: str) -> "ActionPlan": - 40 """Parse the router response. - 41 :param response: The router response. This should be a JSON blob, but sometimes the AI responds with a - 42 straight JSON string. - 43 """ - 44 json_string = response - 45 if re.match(r"^`{3}(.+)`{3}$", response): - 46 _, json_string = extract_codeblock(response) - 47 plan: ActionPlan = object_mapper.of_json(json_string, ActionPlan) - 48 if not isinstance(plan, ActionPlan): - 49 plan = ActionPlan._final(question, json_string, ModelResult.default()) - 50 - 51 return plan - 52 - 53 @staticmethod - 54 def _final(question: str, response: str, model: ModelResult) -> "ActionPlan": - 55 """TODO""" - 56 return ActionPlan( - 57 question, - 58 f"Answer to the question: {question}", - 59 [], - 60 SimpleNamespace( - 61 reasoning="AI decided to respond directly", - 62 observations="AI had enough context to respond directly", - 63 criticism="The answer may not be good enough", - 64 speak=response, - 65 ), - 66 [SimpleNamespace(id="1", task=response)], - 67 model, - 68 ) - 69 - 70 @staticmethod - 71 def _browse(question: str, query: str, model: ModelResult) -> "ActionPlan": - 72 """TODO""" - 73 return ActionPlan( - 74 question, - 75 f"Answer to the question: {question}", - 76 [], - 77 SimpleNamespace( - 78 reasoning="AI requested internet browsing", - 79 observations="", - 80 criticism="The answer may not be too accurate", - 81 speak="I will search on the internet for you", - 82 ), - 83 [SimpleNamespace(id="1", task=f"Browse on te internet for: {query}")], - 84 model, - 85 ) + 29 """Represents and tracks the action plan for a router. + 30 This class is used to keep track of the sequence of actions or steps to be executed by the router. + 31 """ + 32 + 33 question: str = None + 34 primary_goal: str = None + 35 sub_goals: list[str] = None + 36 thoughts: SimpleNamespace = None + 37 tasks: list[SimpleNamespace] = None + 38 model: ModelResult = field(default_factory=ModelResult.default) + 39 + 40 @staticmethod + 41 def _parse_response(question: str, response: str) -> "ActionPlan": + 42 """Parse the router's response and convert it into an ActionPlan. + 43 :param question: The original question or command that was sent to the router. + 44 :param response: The router's response, typically a JSON blob. Note that the response might sometimes be a + 45 plain JSON string. + 46 :return: An instance of ActionPlan created from the parsed response. + 47 """ + 48 json_string = response + 49 if re.match(r"^`{3}(.+)`{3}$", response): + 50 _, json_string = extract_codeblock(response) + 51 plan: ActionPlan = object_mapper.of_json(json_string, ActionPlan) + 52 if not isinstance(plan, ActionPlan): + 53 plan = ActionPlan._direct(question, json_string, ModelResult.default()) + 54 return plan + 55 + 56 @staticmethod + 57 def _direct(question: str, response: str, model: ModelResult) -> "ActionPlan": + 58 """Create a simple ActionPlan from an AI's direct response in plain text. + 59 :param question: The original question that was sent to the AI. + 60 :param response: The AI's direct response in plain text (unformatted JSON). + 61 :param model: The result model. + 62 :return: An instance of ActionPlan created from the direct response. + 63 """ + 64 return ActionPlan( + 65 question, + 66 "N/A", + 67 [], + 68 SimpleNamespace(reasoning="N/A", observations="N/A", criticism="N/A", speak=response), + 69 [SimpleNamespace(id="1", task=response)], + 70 model, + 71 ) + 72 + 73 @staticmethod + 74 def create(question: str, message: AIMessage, model: ModelResult) -> "ActionPlan": + 75 """Create an ActionPlan based on the provided question, AI message, and result model. + 76 :param question: The original question or command that was sent to the AI. + 77 :param message: The AIMessage object containing the AI's response and metadata. + 78 :param model: The result model. + 79 :return: An instance of ActionPlan created from the provided inputs. + 80 """ + 81 plan: ActionPlan = ActionPlan._parse_response(question, message.content) + 82 check_state( + 83 plan is not None and isinstance(plan, ActionPlan), f"Invalid action plan received from LLM: {type(plan)}" + 84 ) + 85 plan.model = model 86 - 87 @staticmethod - 88 def _terminal(question: str, cmd_line: str, model: ModelResult) -> "ActionPlan": - 89 """TODO""" - 90 return ActionPlan( - 91 question, - 92 f"Execute terminal command: {question}", - 93 [], - 94 SimpleNamespace( - 95 reasoning="AI requested to execute a terminal command", - 96 observations="", - 97 criticism="The user needs to grant access", - 98 speak="", - 99 ), -100 [SimpleNamespace(id="1", task=f"Execute the following command on the terminal: {cmd_line}")], -101 model, -102 ) -103 -104 @staticmethod -105 def create(question: str, message: AIMessage, model: ModelResult) -> "ActionPlan": -106 """TODO""" -107 plan: ActionPlan = ActionPlan._parse_response(question, message.content) -108 check_state( -109 plan is not None and isinstance(plan, ActionPlan), f"Invalid action plan received from LLM: {type(plan)}" -110 ) -111 plan.model = model -112 -113 return plan + 87 return plan + 88 + 89 def __str__(self): + 90 sub_goals: str = " ".join(f"{i + 1}. {g}" for i, g in enumerate(self.sub_goals)) if self.sub_goals else "N/A" + 91 tasks: str = ". ".join([f"{i + 1}. {a.task}" for i, a in enumerate(self.tasks)]) if self.tasks else "N/A" + 92 return ( + 93 f"`Question:` {self.question} " + 94 f"`Reasoning:` {self.reasoning} " + 95 f"`Observations:` {self.observations} " + 96 f"`Criticism:` {self.criticism} " + 97 f"`Speak:` {self.speak} " + 98 f"`Sub-Goals:` [{sub_goals}] " + 99 f"`Tasks:` [{tasks}] ." +100 ) +101 +102 def __len__(self): +103 return len(self.tasks) +104 +105 @property +106 def reasoning(self) -> Optional[str]: +107 return self.thoughts.reasoning if hasattr(self, "thoughts") and hasattr(self.thoughts, "reasoning") else None +108 +109 @property +110 def observations(self) -> Optional[str]: +111 return ( +112 self.thoughts.observations if hasattr(self, "thoughts") and hasattr(self.thoughts, "observations") else None +113 ) 114 -115 def __str__(self): -116 sub_goals: str = " ".join(f"{i + 1}. {g}" for i, g in enumerate(self.sub_goals)) if self.sub_goals else "N/A" -117 tasks: str = ". ".join([f"{i + 1}. {a.task}" for i, a in enumerate(self.tasks)]) if self.tasks else "N/A" -118 return ( -119 f"`Question:` {self.question} " -120 f"`Reasoning:` {self.reasoning} " -121 f"`Observations:` {self.observations} " -122 f"`Criticism:` {self.criticism} " -123 f"`Speak:` {self.speak} " -124 f"`Sub-Goals:` [{sub_goals}] " -125 f"`Tasks:` [{tasks}] ." -126 ) -127 -128 def __len__(self): -129 return len(self.tasks) -130 -131 @property -132 def reasoning(self) -> Optional[str]: -133 return self.thoughts.reasoning if hasattr(self, "thoughts") and hasattr(self.thoughts, "reasoning") else None -134 -135 @property -136 def observations(self) -> Optional[str]: -137 return ( -138 self.thoughts.observations if hasattr(self, "thoughts") and hasattr(self.thoughts, "observations") else None -139 ) -140 -141 @property -142 def criticism(self) -> Optional[str]: -143 return self.thoughts.criticism if hasattr(self, "thoughts") and hasattr(self.thoughts, "criticism") else None -144 -145 @property -146 def speak(self) -> Optional[str]: -147 return self.thoughts.speak if hasattr(self, "thoughts") and hasattr(self.thoughts, "speak") else None +115 @property +116 def criticism(self) -> Optional[str]: +117 return self.thoughts.criticism if hasattr(self, "thoughts") and hasattr(self.thoughts, "criticism") else None +118 +119 @property +120 def speak(self) -> Optional[str]: +121 return self.thoughts.speak if hasattr(self, "thoughts") and hasattr(self.thoughts, "speak") else None

    @@ -268,129 +242,104 @@

     28@dataclass
      29class ActionPlan:
    - 30    """Keep track of the router action plan."""
    - 31
    - 32    question: str = None
    - 33    primary_goal: str = None
    - 34    sub_goals: list[str] = None
    - 35    thoughts: SimpleNamespace = None
    - 36    tasks: list[SimpleNamespace] = None
    - 37    model: ModelResult = field(default_factory=ModelResult.default)
    - 38
    - 39    @staticmethod
    - 40    def _parse_response(question: str, response: str) -> "ActionPlan":
    - 41        """Parse the router response.
    - 42        :param response: The router response. This should be a JSON blob, but sometimes the AI responds with a
    - 43        straight JSON string.
    - 44        """
    - 45        json_string = response
    - 46        if re.match(r"^`{3}(.+)`{3}$", response):
    - 47            _, json_string = extract_codeblock(response)
    - 48        plan: ActionPlan = object_mapper.of_json(json_string, ActionPlan)
    - 49        if not isinstance(plan, ActionPlan):
    - 50            plan = ActionPlan._final(question, json_string, ModelResult.default())
    - 51
    - 52        return plan
    - 53
    - 54    @staticmethod
    - 55    def _final(question: str, response: str, model: ModelResult) -> "ActionPlan":
    - 56        """TODO"""
    - 57        return ActionPlan(
    - 58            question,
    - 59            f"Answer to the question: {question}",
    - 60            [],
    - 61            SimpleNamespace(
    - 62                reasoning="AI decided to respond directly",
    - 63                observations="AI had enough context to respond directly",
    - 64                criticism="The answer may not be good enough",
    - 65                speak=response,
    - 66            ),
    - 67            [SimpleNamespace(id="1", task=response)],
    - 68            model,
    - 69        )
    - 70
    - 71    @staticmethod
    - 72    def _browse(question: str, query: str, model: ModelResult) -> "ActionPlan":
    - 73        """TODO"""
    - 74        return ActionPlan(
    - 75            question,
    - 76            f"Answer to the question: {question}",
    - 77            [],
    - 78            SimpleNamespace(
    - 79                reasoning="AI requested internet browsing",
    - 80                observations="",
    - 81                criticism="The answer may not be too accurate",
    - 82                speak="I will search on the internet for you",
    - 83            ),
    - 84            [SimpleNamespace(id="1", task=f"Browse on te internet for: {query}")],
    - 85            model,
    - 86        )
    + 30    """Represents and tracks the action plan for a router.
    + 31    This class is used to keep track of the sequence of actions or steps to be executed by the router.
    + 32    """
    + 33
    + 34    question: str = None
    + 35    primary_goal: str = None
    + 36    sub_goals: list[str] = None
    + 37    thoughts: SimpleNamespace = None
    + 38    tasks: list[SimpleNamespace] = None
    + 39    model: ModelResult = field(default_factory=ModelResult.default)
    + 40
    + 41    @staticmethod
    + 42    def _parse_response(question: str, response: str) -> "ActionPlan":
    + 43        """Parse the router's response and convert it into an ActionPlan.
    + 44        :param question: The original question or command that was sent to the router.
    + 45        :param response: The router's response, typically a JSON blob. Note that the response might sometimes be a
    + 46                         plain JSON string.
    + 47        :return: An instance of ActionPlan created from the parsed response.
    + 48        """
    + 49        json_string = response
    + 50        if re.match(r"^`{3}(.+)`{3}$", response):
    + 51            _, json_string = extract_codeblock(response)
    + 52        plan: ActionPlan = object_mapper.of_json(json_string, ActionPlan)
    + 53        if not isinstance(plan, ActionPlan):
    + 54            plan = ActionPlan._direct(question, json_string, ModelResult.default())
    + 55        return plan
    + 56
    + 57    @staticmethod
    + 58    def _direct(question: str, response: str, model: ModelResult) -> "ActionPlan":
    + 59        """Create a simple ActionPlan from an AI's direct response in plain text.
    + 60        :param question: The original question that was sent to the AI.
    + 61        :param response: The AI's direct response in plain text (unformatted JSON).
    + 62        :param model: The result model.
    + 63        :return: An instance of ActionPlan created from the direct response.
    + 64        """
    + 65        return ActionPlan(
    + 66            question,
    + 67            "N/A",
    + 68            [],
    + 69            SimpleNamespace(reasoning="N/A", observations="N/A", criticism="N/A", speak=response),
    + 70            [SimpleNamespace(id="1", task=response)],
    + 71            model,
    + 72        )
    + 73
    + 74    @staticmethod
    + 75    def create(question: str, message: AIMessage, model: ModelResult) -> "ActionPlan":
    + 76        """Create an ActionPlan based on the provided question, AI message, and result model.
    + 77        :param question: The original question or command that was sent to the AI.
    + 78        :param message: The AIMessage object containing the AI's response and metadata.
    + 79        :param model: The result model.
    + 80        :return: An instance of ActionPlan created from the provided inputs.
    + 81        """
    + 82        plan: ActionPlan = ActionPlan._parse_response(question, message.content)
    + 83        check_state(
    + 84            plan is not None and isinstance(plan, ActionPlan), f"Invalid action plan received from LLM: {type(plan)}"
    + 85        )
    + 86        plan.model = model
      87
    - 88    @staticmethod
    - 89    def _terminal(question: str, cmd_line: str, model: ModelResult) -> "ActionPlan":
    - 90        """TODO"""
    - 91        return ActionPlan(
    - 92            question,
    - 93            f"Execute terminal command: {question}",
    - 94            [],
    - 95            SimpleNamespace(
    - 96                reasoning="AI requested to execute a terminal command",
    - 97                observations="",
    - 98                criticism="The user needs to grant access",
    - 99                speak="",
    -100            ),
    -101            [SimpleNamespace(id="1", task=f"Execute the following command on the terminal: {cmd_line}")],
    -102            model,
    -103        )
    -104
    -105    @staticmethod
    -106    def create(question: str, message: AIMessage, model: ModelResult) -> "ActionPlan":
    -107        """TODO"""
    -108        plan: ActionPlan = ActionPlan._parse_response(question, message.content)
    -109        check_state(
    -110            plan is not None and isinstance(plan, ActionPlan), f"Invalid action plan received from LLM: {type(plan)}"
    -111        )
    -112        plan.model = model
    -113
    -114        return plan
    + 88        return plan
    + 89
    + 90    def __str__(self):
    + 91        sub_goals: str = "  ".join(f"{i + 1}. {g}" for i, g in enumerate(self.sub_goals)) if self.sub_goals else "N/A"
    + 92        tasks: str = ".  ".join([f"{i + 1}. {a.task}" for i, a in enumerate(self.tasks)]) if self.tasks else "N/A"
    + 93        return (
    + 94            f"`Question:` {self.question}  "
    + 95            f"`Reasoning:` {self.reasoning}  "
    + 96            f"`Observations:` {self.observations}  "
    + 97            f"`Criticism:` {self.criticism}  "
    + 98            f"`Speak:` {self.speak}  "
    + 99            f"`Sub-Goals:` [{sub_goals}]  "
    +100            f"`Tasks:` [{tasks}]  ."
    +101        )
    +102
    +103    def __len__(self):
    +104        return len(self.tasks)
    +105
    +106    @property
    +107    def reasoning(self) -> Optional[str]:
    +108        return self.thoughts.reasoning if hasattr(self, "thoughts") and hasattr(self.thoughts, "reasoning") else None
    +109
    +110    @property
    +111    def observations(self) -> Optional[str]:
    +112        return (
    +113            self.thoughts.observations if hasattr(self, "thoughts") and hasattr(self.thoughts, "observations") else None
    +114        )
     115
    -116    def __str__(self):
    -117        sub_goals: str = "  ".join(f"{i + 1}. {g}" for i, g in enumerate(self.sub_goals)) if self.sub_goals else "N/A"
    -118        tasks: str = ".  ".join([f"{i + 1}. {a.task}" for i, a in enumerate(self.tasks)]) if self.tasks else "N/A"
    -119        return (
    -120            f"`Question:` {self.question}  "
    -121            f"`Reasoning:` {self.reasoning}  "
    -122            f"`Observations:` {self.observations}  "
    -123            f"`Criticism:` {self.criticism}  "
    -124            f"`Speak:` {self.speak}  "
    -125            f"`Sub-Goals:` [{sub_goals}]  "
    -126            f"`Tasks:` [{tasks}]  ."
    -127        )
    -128
    -129    def __len__(self):
    -130        return len(self.tasks)
    -131
    -132    @property
    -133    def reasoning(self) -> Optional[str]:
    -134        return self.thoughts.reasoning if hasattr(self, "thoughts") and hasattr(self.thoughts, "reasoning") else None
    -135
    -136    @property
    -137    def observations(self) -> Optional[str]:
    -138        return (
    -139            self.thoughts.observations if hasattr(self, "thoughts") and hasattr(self.thoughts, "observations") else None
    -140        )
    -141
    -142    @property
    -143    def criticism(self) -> Optional[str]:
    -144        return self.thoughts.criticism if hasattr(self, "thoughts") and hasattr(self.thoughts, "criticism") else None
    -145
    -146    @property
    -147    def speak(self) -> Optional[str]:
    -148        return self.thoughts.speak if hasattr(self, "thoughts") and hasattr(self.thoughts, "speak") else None
    +116    @property
    +117    def criticism(self) -> Optional[str]:
    +118        return self.thoughts.criticism if hasattr(self, "thoughts") and hasattr(self.thoughts, "criticism") else None
    +119
    +120    @property
    +121    def speak(self) -> Optional[str]:
    +122        return self.thoughts.speak if hasattr(self, "thoughts") and hasattr(self.thoughts, "speak") else None
     
    -

    Keep track of the router action plan.

    +

    Represents and tracks the action plan for a router. +This class is used to keep track of the sequence of actions or steps to be executed by the router.

    @@ -489,20 +438,39 @@

    -
    105    @staticmethod
    -106    def create(question: str, message: AIMessage, model: ModelResult) -> "ActionPlan":
    -107        """TODO"""
    -108        plan: ActionPlan = ActionPlan._parse_response(question, message.content)
    -109        check_state(
    -110            plan is not None and isinstance(plan, ActionPlan), f"Invalid action plan received from LLM: {type(plan)}"
    -111        )
    -112        plan.model = model
    -113
    -114        return plan
    +            
    74    @staticmethod
    +75    def create(question: str, message: AIMessage, model: ModelResult) -> "ActionPlan":
    +76        """Create an ActionPlan based on the provided question, AI message, and result model.
    +77        :param question: The original question or command that was sent to the AI.
    +78        :param message: The AIMessage object containing the AI's response and metadata.
    +79        :param model: The result model.
    +80        :return: An instance of ActionPlan created from the provided inputs.
    +81        """
    +82        plan: ActionPlan = ActionPlan._parse_response(question, message.content)
    +83        check_state(
    +84            plan is not None and isinstance(plan, ActionPlan), f"Invalid action plan received from LLM: {type(plan)}"
    +85        )
    +86        plan.model = model
    +87
    +88        return plan
     
    -

    TODO

    +

    Create an ActionPlan based on the provided question, AI message, and result model.

    + +
    Parameters
    + +
      +
    • question: The original question or command that was sent to the AI.
    • +
    • message: The AIMessage object containing the AI's response and metadata.
    • +
    • model: The result model.
    • +
    + +
    Returns
    + +
    +

    An instance of ActionPlan created from the provided inputs.

    +
    @@ -516,9 +484,9 @@

    -
    132    @property
    -133    def reasoning(self) -> Optional[str]:
    -134        return self.thoughts.reasoning if hasattr(self, "thoughts") and hasattr(self.thoughts, "reasoning") else None
    +            
    106    @property
    +107    def reasoning(self) -> Optional[str]:
    +108        return self.thoughts.reasoning if hasattr(self, "thoughts") and hasattr(self.thoughts, "reasoning") else None
     
    @@ -534,11 +502,11 @@

    -
    136    @property
    -137    def observations(self) -> Optional[str]:
    -138        return (
    -139            self.thoughts.observations if hasattr(self, "thoughts") and hasattr(self.thoughts, "observations") else None
    -140        )
    +            
    110    @property
    +111    def observations(self) -> Optional[str]:
    +112        return (
    +113            self.thoughts.observations if hasattr(self, "thoughts") and hasattr(self.thoughts, "observations") else None
    +114        )
     
    @@ -554,9 +522,9 @@

    -
    142    @property
    -143    def criticism(self) -> Optional[str]:
    -144        return self.thoughts.criticism if hasattr(self, "thoughts") and hasattr(self.thoughts, "criticism") else None
    +            
    116    @property
    +117    def criticism(self) -> Optional[str]:
    +118        return self.thoughts.criticism if hasattr(self, "thoughts") and hasattr(self.thoughts, "criticism") else None
     
    @@ -572,9 +540,9 @@

    -
    146    @property
    -147    def speak(self) -> Optional[str]:
    -148        return self.thoughts.speak if hasattr(self, "thoughts") and hasattr(self.thoughts, "speak") else None
    +            
    120    @property
    +121    def speak(self) -> Optional[str]:
    +122        return self.thoughts.speak if hasattr(self, "thoughts") and hasattr(self.thoughts, "speak") else None
     
    diff --git a/docs/api/askai/main/askai/core/model/ai_model.html b/docs/api/askai/main/askai/core/model/ai_model.html deleted file mode 100644 index 629200e0..00000000 --- a/docs/api/askai/main/askai/core/model/ai_model.html +++ /dev/null @@ -1,409 +0,0 @@ - - - - - - - main.askai.core.model.ai_model API documentation - - - - - - - - - -
    -
    -

    -main.askai.core.model.ai_model

    - -

    @project: HsPyLib-AskAI -@package: askai.core.model - @file: ai_model.py -@created: Fri, 5 May 2024 - @author: Hugo Saporetti Junior" - @site: https://github.com/yorevs/askai -@license: MIT - Please refer to https://opensource.org/licenses/MIT

    - -

    Copyright (c) 2024, HomeSetup

    -
    - - - - - -
     1#!/usr/bin/env python3
    - 2# -*- coding: utf-8 -*-
    - 3
    - 4"""
    - 5   @project: HsPyLib-AskAI
    - 6   @package: askai.core.model
    - 7      @file: ai_model.py
    - 8   @created: Fri, 5 May 2024
    - 9    @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior"
    -10      @site: https://github.com/yorevs/askai
    -11   @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
    -12
    -13   Copyright (c) 2024, HomeSetup
    -14"""
    -15from typing import Protocol
    -16
    -17
    -18class AIModel(Protocol):
    -19    """Provide an interface for AI models."""
    -20
    -21    def model_name(self) -> str:
    -22        """Get the official model's name."""
    -23        ...
    -24
    -25    def token_limit(self) -> int:
    -26        """Get the official model tokens limit."""
    -27        ...
    -
    - - -
    -
    - -
    - - class - AIModel(typing.Protocol): - - - -
    - -
    19class AIModel(Protocol):
    -20    """Provide an interface for AI models."""
    -21
    -22    def model_name(self) -> str:
    -23        """Get the official model's name."""
    -24        ...
    -25
    -26    def token_limit(self) -> int:
    -27        """Get the official model tokens limit."""
    -28        ...
    -
    - - -

    Provide an interface for AI models.

    -
    - - -
    - -
    - - AIModel(*args, **kwargs) - - - -
    - -
    1934def _no_init_or_replace_init(self, *args, **kwargs):
    -1935    cls = type(self)
    -1936
    -1937    if cls._is_protocol:
    -1938        raise TypeError('Protocols cannot be instantiated')
    -1939
    -1940    # Already using a custom `__init__`. No need to calculate correct
    -1941    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
    -1942    if cls.__init__ is not _no_init_or_replace_init:
    -1943        return
    -1944
    -1945    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
    -1946    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
    -1947    # searches for a proper new `__init__` in the MRO. The new `__init__`
    -1948    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
    -1949    # instantiation of the protocol subclass will thus use the new
    -1950    # `__init__` and no longer call `_no_init_or_replace_init`.
    -1951    for base in cls.__mro__:
    -1952        init = base.__dict__.get('__init__', _no_init_or_replace_init)
    -1953        if init is not _no_init_or_replace_init:
    -1954            cls.__init__ = init
    -1955            break
    -1956    else:
    -1957        # should not happen
    -1958        cls.__init__ = object.__init__
    -1959
    -1960    cls.__init__(self, *args, **kwargs)
    -
    - - - - -
    -
    - -
    - - def - model_name(self) -> str: - - - -
    - -
    22    def model_name(self) -> str:
    -23        """Get the official model's name."""
    -24        ...
    -
    - - -

    Get the official model's name.

    -
    - - -
    -
    - -
    - - def - token_limit(self) -> int: - - - -
    - -
    26    def token_limit(self) -> int:
    -27        """Get the official model tokens limit."""
    -28        ...
    -
    - - -

    Get the official model tokens limit.

    -
    - - -
    -
    -
    - - \ No newline at end of file diff --git a/docs/api/askai/main/askai/core/model/ai_reply.html b/docs/api/askai/main/askai/core/model/ai_reply.html index 1eba80a5..924124f1 100644 --- a/docs/api/askai/main/askai/core/model/ai_reply.html +++ b/docs/api/askai/main/askai/core/model/ai_reply.html @@ -3,14 +3,14 @@ - + main.askai.core.model.ai_reply API documentation - +
    @@ -110,7 +211,7 @@

    -
    @dataclass
    +
    @dataclass(frozen=True)
    class AIReply: @@ -119,26 +220,105 @@

    -
    20@dataclass
    -21class AIReply:
    -22    """Data class that represent AI replies."""
    -23
    -24    message: str = None
    -25    is_success: bool = None
    -26
    -27    def __str__(self):
    -28        return f"Success = {self.is_success}  \nMessage = {self.message}"
    +            
    22@dataclass(frozen=True)
    +23class AIReply:
    +24    """Data class that represents AI replies.
    +25    :param message: The reply message.
    +26    :param is_success: Indicates whether the reply is successful.
    +27    :param is_debug: Indicates whether the reply is for debugging.
    +28    :param verbosity: The verbosity level of the reply.
    +29    :param is_speakable: Indicates whether the reply is speakable.
    +30    """
    +31
    +32    message: str = ""
    +33    is_success: bool = True
    +34    is_debug: bool = False
    +35    verbosity: Verbosity = Verbosity.MINIMUM
    +36    is_speakable: bool = True
    +37
    +38    def __str__(self) -> str:
    +39        return self.message
    +40
    +41    @staticmethod
    +42    def info(message: AnyStr, verbosity: Verbosity = Verbosity.NORMAL, speakable: bool = True) -> "AIReply":
    +43        """Creates an info reply.
    +44        :param message: The reply message.
    +45        :param verbosity: The verbosity level of the reply.
    +46        :param speakable: Indicates whether the reply is speakable.
    +47        :return: An AIReply instance with info settings.
    +48        """
    +49        return AIReply(str(message), True, False, verbosity, speakable)
    +50
    +51    @staticmethod
    +52    def detailed(message: AnyStr, speakable: bool = False) -> "AIReply":
    +53        """Creates a detailed verbosity reply.
    +54        :param message: The reply message.
    +55        :param speakable: Indicates whether the reply is speakable.
    +56        :return: An AIReply instance with detailed settings.
    +57        """
    +58        return AIReply(str(message), True, False, Verbosity.DETAILED, speakable)
    +59
    +60    @staticmethod
    +61    def full(message: AnyStr, speakable: bool = False) -> "AIReply":
    +62        """Creates a full verbose reply.
    +63        :param message: The reply message.
    +64        :param speakable: Indicates whether the reply is speakable.
    +65        :return: An AIReply instance with full settings.
    +66        """
    +67        return AIReply(str(message), True, False, Verbosity.FULL, speakable)
    +68
    +69    @staticmethod
    +70    def error(message: AnyStr) -> "AIReply":
    +71        """Creates an error reply.
    +72        :param message: The reply message.
    +73        :return: An AIReply instance with error settings.
    +74        """
    +75        return AIReply(str(message), False, False, Verbosity.NORMAL, False)
    +76
    +77    @staticmethod
    +78    def debug(message: AnyStr) -> "AIReply":
    +79        """Creates a debug reply.
    +80        :param message: The reply message.
    +81        :return: An AIReply instance with debug settings.
    +82        """
    +83        return AIReply(str(message), True, True, Verbosity.NORMAL, False)
    +84
    +85    @staticmethod
    +86    def mute(message: AnyStr, verbosity: Verbosity = Verbosity.NORMAL) -> "AIReply":
    +87        """Creates a mute reply.
    +88        :param message: The reply message.
    +89        :param verbosity: The verbosity level of the reply.
    +90        :return: An AIReply instance with mute settings.
    +91        """
    +92        return AIReply(str(message), True, False, verbosity, False)
    +93
    +94    @property
    +95    def is_error(self) -> bool:
    +96        """Checks if the reply indicates an error.
    +97        :return: True if the reply is not successful, otherwise False.
    +98        """
    +99        return not self.is_success
     
    -

    Data class that represent AI replies.

    +

    Data class that represents AI replies.

    + +
    Parameters
    + +
      +
    • message: The reply message.
    • +
    • is_success: Indicates whether the reply is successful.
    • +
    • is_debug: Indicates whether the reply is for debugging.
    • +
    • verbosity: The verbosity level of the reply.
    • +
    • is_speakable: Indicates whether the reply is speakable.
    • +
    - AIReply(message: str = None, is_success: bool = None) + AIReply( message: str = '', is_success: bool = True, is_debug: bool = False, verbosity: askai.core.enums.verbosity.Verbosity = MINIMUM, is_speakable: bool = True)
    @@ -150,7 +330,7 @@

    message: str = -None +''
    @@ -162,7 +342,7 @@

    is_success: bool = -None +True
    @@ -170,6 +350,315 @@

    +

    +
    +
    + is_debug: bool = +False + + +
    + + + + +
    +
    +
    + verbosity: askai.core.enums.verbosity.Verbosity = +MINIMUM + + +
    + + + + +
    +
    +
    + is_speakable: bool = +True + + +
    + + + + +
    +
    + +
    +
    @staticmethod
    + + def + info( message: ~AnyStr, verbosity: askai.core.enums.verbosity.Verbosity = NORMAL, speakable: bool = True) -> AIReply: + + + +
    + +
    41    @staticmethod
    +42    def info(message: AnyStr, verbosity: Verbosity = Verbosity.NORMAL, speakable: bool = True) -> "AIReply":
    +43        """Creates an info reply.
    +44        :param message: The reply message.
    +45        :param verbosity: The verbosity level of the reply.
    +46        :param speakable: Indicates whether the reply is speakable.
    +47        :return: An AIReply instance with info settings.
    +48        """
    +49        return AIReply(str(message), True, False, verbosity, speakable)
    +
    + + +

    Creates an info reply.

    + +
    Parameters
    + +
      +
    • message: The reply message.
    • +
    • verbosity: The verbosity level of the reply.
    • +
    • speakable: Indicates whether the reply is speakable.
    • +
    + +
    Returns
    + +
    +

    An AIReply instance with info settings.

    +
    +
    + + +
    +
    + +
    +
    @staticmethod
    + + def + detailed( message: ~AnyStr, speakable: bool = False) -> AIReply: + + + +
    + +
    51    @staticmethod
    +52    def detailed(message: AnyStr, speakable: bool = False) -> "AIReply":
    +53        """Creates a detailed verbosity reply.
    +54        :param message: The reply message.
    +55        :param speakable: Indicates whether the reply is speakable.
    +56        :return: An AIReply instance with detailed settings.
    +57        """
    +58        return AIReply(str(message), True, False, Verbosity.DETAILED, speakable)
    +
    + + +

    Creates a detailed verbosity reply.

    + +
    Parameters
    + +
      +
    • message: The reply message.
    • +
    • speakable: Indicates whether the reply is speakable.
    • +
    + +
    Returns
    + +
    +

    An AIReply instance with detailed settings.

    +
    +
    + + +
    +
    + +
    +
    @staticmethod
    + + def + full( message: ~AnyStr, speakable: bool = False) -> AIReply: + + + +
    + +
    60    @staticmethod
    +61    def full(message: AnyStr, speakable: bool = False) -> "AIReply":
    +62        """Creates a full verbose reply.
    +63        :param message: The reply message.
    +64        :param speakable: Indicates whether the reply is speakable.
    +65        :return: An AIReply instance with full settings.
    +66        """
    +67        return AIReply(str(message), True, False, Verbosity.FULL, speakable)
    +
    + + +

    Creates a full verbose reply.

    + +
    Parameters
    + +
      +
    • message: The reply message.
    • +
    • speakable: Indicates whether the reply is speakable.
    • +
    + +
    Returns
    + +
    +

    An AIReply instance with full settings.

    +
    +
    + + +
    +
    + +
    +
    @staticmethod
    + + def + error(message: ~AnyStr) -> AIReply: + + + +
    + +
    69    @staticmethod
    +70    def error(message: AnyStr) -> "AIReply":
    +71        """Creates an error reply.
    +72        :param message: The reply message.
    +73        :return: An AIReply instance with error settings.
    +74        """
    +75        return AIReply(str(message), False, False, Verbosity.NORMAL, False)
    +
    + + +

    Creates an error reply.

    + +
    Parameters
    + +
      +
    • message: The reply message.
    • +
    + +
    Returns
    + +
    +

    An AIReply instance with error settings.

    +
    +
    + + +
    +
    + +
    +
    @staticmethod
    + + def + debug(message: ~AnyStr) -> AIReply: + + + +
    + +
    77    @staticmethod
    +78    def debug(message: AnyStr) -> "AIReply":
    +79        """Creates a debug reply.
    +80        :param message: The reply message.
    +81        :return: An AIReply instance with debug settings.
    +82        """
    +83        return AIReply(str(message), True, True, Verbosity.NORMAL, False)
    +
    + + +

    Creates a debug reply.

    + +
    Parameters
    + +
      +
    • message: The reply message.
    • +
    + +
    Returns
    + +
    +

    An AIReply instance with debug settings.

    +
    +
    + + +
    +
    + +
    +
    @staticmethod
    + + def + mute( message: ~AnyStr, verbosity: askai.core.enums.verbosity.Verbosity = NORMAL) -> AIReply: + + + +
    + +
    85    @staticmethod
    +86    def mute(message: AnyStr, verbosity: Verbosity = Verbosity.NORMAL) -> "AIReply":
    +87        """Creates a mute reply.
    +88        :param message: The reply message.
    +89        :param verbosity: The verbosity level of the reply.
    +90        :return: An AIReply instance with mute settings.
    +91        """
    +92        return AIReply(str(message), True, False, verbosity, False)
    +
    + + +

    Creates a mute reply.

    + +
    Parameters
    + +
      +
    • message: The reply message.
    • +
    • verbosity: The verbosity level of the reply.
    • +
    + +
    Returns
    + +
    +

    An AIReply instance with mute settings.

    +
    +
    + + +
    +
    + +
    + is_error: bool + + + +
    + +
    94    @property
    +95    def is_error(self) -> bool:
    +96        """Checks if the reply indicates an error.
    +97        :return: True if the reply is not successful, otherwise False.
    +98        """
    +99        return not self.is_success
    +
    + + +

    Checks if the reply indicates an error.

    + +
    Returns
    + +
    +

    True if the reply is not successful, otherwise False.

    +
    +
    + +

    diff --git a/docs/api/askai/main/askai/core/model/api_keys.html b/docs/api/askai/main/askai/core/model/api_keys.html index 1338a80f..e91440c4 100644 --- a/docs/api/askai/main/askai/core/model/api_keys.html +++ b/docs/api/askai/main/askai/core/model/api_keys.html @@ -3,7 +3,7 @@ - + main.askai.core.model.api_keys API documentation @@ -48,6 +48,9 @@

    API Documentation

  • not_empty
  • +
  • + has_key +
  • ApiKeys.Config
  • @@ -97,94 +103,124 @@

    -
     1#!/usr/bin/env python3
    - 2# -*- coding: utf-8 -*-
    - 3
    - 4"""
    - 5   @project: HsPyLib-AskAI
    - 6   @package: askai.core.askai_configs
    - 7      @file: askai_configs.py
    - 8   @created: Fri, 5 Jan 2024
    - 9    @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior
    -10      @site: https://github.com/yorevs/askai
    -11   @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
    -12
    -13   Copyright (c) 2024, HomeSetup
    -14"""
    -15import dotenv
    -16from clitt.core.tui.minput.input_validator import InputValidator
    -17from clitt.core.tui.minput.menu_input import MenuInput
    -18from clitt.core.tui.minput.minput import minput
    -19from hspylib.core.enums.charset import Charset
    -20from pathlib import Path
    -21
    -22from pydantic.v1 import Field, BaseSettings, validator
    -23
    -24import os
    -25
    -26API_KEY_FILE: str = os.environ.get("HHS_ENV_FILE", str(os.path.join(Path.home(), ".env")))
    -27
    -28dotenv.load_dotenv(API_KEY_FILE)
    -29
    -30
    -31class ApiKeys(BaseSettings):
    -32    """Provide a class no handle the required Api Keys."""
    -33
    -34    OPENAI_API_KEY: str = Field(..., description="Open AI Api Key")
    -35    GOOGLE_API_KEY: str = Field(..., description="Google Api Key")
    -36    DEEPL_API_KEY: str = Field(..., description="DeepL Api Key")
    -37
    -38    @validator('OPENAI_API_KEY', 'GOOGLE_API_KEY', 'DEEPL_API_KEY')
    -39    def not_empty(cls, value):
    -40        if not value or not value.strip():
    -41            raise ValueError('must not be empty or blank')
    -42        return value
    -43
    -44    class Config:
    -45        env_file = API_KEY_FILE
    -46        env_file_encoding = Charset.UTF_8.val
    -47
    -48    @staticmethod
    -49    def prompt() -> bool:
    -50        """TODO"""
    -51
    -52        # fmt: off
    -53        form_fields = MenuInput.builder() \
    -54            .field() \
    -55                .label('OPENAI_API_KEY') \
    -56                .value(os.environ.get("OPENAI_API_KEY")) \
    -57                .min_max_length(51, 51) \
    -58                .validator(InputValidator.anything()) \
    -59                .build() \
    -60            .field() \
    -61                .label('GOOGLE_API_KEY') \
    -62                .value(os.environ.get("GOOGLE_API_KEY")) \
    -63                .min_max_length(39, 39) \
    -64                .validator(InputValidator.anything()) \
    -65                .build() \
    -66            .field() \
    -67                .label('DEEPL_API_KEY') \
    -68                .value(os.environ.get("DEEPL_API_KEY")) \
    -69                .min_max_length(39, 39) \
    -70                .validator(InputValidator.anything()) \
    -71                .build() \
    -72            .build()
    -73        # fmt: on
    -74
    -75        if result := minput(form_fields, "Please fill all required Api Keys"):
    -76            with open(API_KEY_FILE, 'r+', encoding=Charset.UTF_8.val) as f_envs:
    -77                envs = f_envs.readlines()
    -78            with open(API_KEY_FILE, 'w', encoding=Charset.UTF_8.val) as f_envs:
    -79                all_envs: set[str] = set(map(str.strip, envs))
    -80                for key, value in zip(result.attributes, result.values):
    -81                    os.environ[key.upper()] = value
    -82                    all_envs.add(f"export {key.upper()}={value}")
    -83                final_envs: list[str] = list(all_envs)
    -84                final_envs.sort()
    -85                f_envs.write(os.linesep.join(final_envs))
    -86            return True
    -87
    -88        return False
    +                        
      1#!/usr/bin/env python3
    +  2# -*- coding: utf-8 -*-
    +  3
    +  4"""
    +  5   @project: HsPyLib-AskAI
    +  6   @package: askai.core.askai_configs
    +  7      @file: askai_configs.py
    +  8   @created: Fri, 5 Jan 2024
    +  9    @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior
    + 10      @site: https://github.com/yorevs/askai
    + 11   @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
    + 12
    + 13   Copyright (c) 2024, HomeSetup
    + 14"""
    + 15from askai.exception.exceptions import MissingApiKeyError
    + 16from clitt.core.tui.minput.input_validator import InputValidator
    + 17from clitt.core.tui.minput.menu_input import MenuInput
    + 18from clitt.core.tui.minput.minput import minput
    + 19from hspylib.core.enums.charset import Charset
    + 20from pathlib import Path
    + 21from pydantic.v1 import BaseSettings, Field, validator
    + 22from typing import AnyStr
    + 23
    + 24import dotenv
    + 25import os
    + 26
    + 27API_KEY_FILE: str = os.environ.get("HHS_ENV_FILE", str(os.path.join(Path.home(), ".env")))
    + 28
    + 29dotenv.load_dotenv(API_KEY_FILE)
    + 30
    + 31
    + 32class ApiKeys(BaseSettings):
    + 33    """A class to manage and handle the required API keys.
    + 34    This class provides a structured way to store, access, and manage API keys necessary for various services.
    + 35    It inherits from BaseSettings to facilitate environment-based configuration.
    + 36    """
    + 37
    + 38    # fmt: off
    + 39    OPENAI_API_KEY: str = Field(..., description="Open AI Api Key")
    + 40    GOOGLE_API_KEY: str = Field(..., description="Google Api Key")
    + 41    DEEPL_API_KEY: str  = Field(..., description="DeepL Api Key")
    + 42    # fmt: on
    + 43
    + 44    @validator("OPENAI_API_KEY", "GOOGLE_API_KEY", "DEEPL_API_KEY")
    + 45    def not_empty(cls, value: AnyStr) -> AnyStr:
    + 46        """Pydantic validator to ensure that API key fields are not empty.
    + 47        :param value: The value of the API key being validated.
    + 48        :return: The value if it is not empty.
    + 49        :raises ValueError: If the value is empty or None.
    + 50        """
    + 51        if not value or not value.strip():
    + 52            raise ValueError("must not be empty or blank")
    + 53        return value
    + 54
    + 55    def has_key(self, key_name: str) -> bool:
    + 56        """Check if the specified API key exists and is not empty.
    + 57        :param key_name: The name of the API key to check.
    + 58        :return: True if the API key exists and is not empty, otherwise False.
    + 59        """
    + 60        api_key: str = key_name.upper()
    + 61        return hasattr(self, api_key) and ((kv := getattr(self, api_key)) is not None and len(kv) > 0)
    + 62
    + 63    class Config:
    + 64        """Configuration class for setting environment variables related to API keys."""
    + 65
    + 66        env_file = API_KEY_FILE
    + 67        env_file_encoding = Charset.UTF_8.val
    + 68
    + 69    @staticmethod
    + 70    def prompt() -> bool:
    + 71        """Prompt the user to input the required API keys.
    + 72        :return: True if all required API keys are successfully provided, otherwise False.
    + 73        """
    + 74
    + 75        # fmt: off
    + 76        form_fields = MenuInput.builder() \
    + 77            .field() \
    + 78                .label('OPENAI_API_KEY') \
    + 79                .value(os.environ.get("OPENAI_API_KEY")) \
    + 80                .min_max_length(51, 51) \
    + 81                .validator(InputValidator.anything()) \
    + 82                .build() \
    + 83            .field() \
    + 84                .label('GOOGLE_API_KEY') \
    + 85                .value(os.environ.get("GOOGLE_API_KEY")) \
    + 86                .min_max_length(39, 39) \
    + 87                .build() \
    + 88            .field() \
    + 89                .label('DEEPL_API_KEY') \
    + 90                .value(os.environ.get("DEEPL_API_KEY")) \
    + 91                .min_max_length(39, 39) \
    + 92                .build() \
    + 93            .build()
    + 94        # fmt: on
    + 95
    + 96        if result := minput(form_fields, "Please fill all required Api Keys"):
    + 97            with open(API_KEY_FILE, "r+", encoding=Charset.UTF_8.val) as f_envs:
    + 98                envs = f_envs.readlines()
    + 99            with open(API_KEY_FILE, "w", encoding=Charset.UTF_8.val) as f_envs:
    +100                all_envs: set[str] = set(map(str.strip, envs))
    +101                for key, value in zip(result.attributes, result.values):
    +102                    os.environ[key.upper()] = value
    +103                    all_envs.add(f"export {key.upper()}={value}")
    +104                final_envs: list[str] = list(all_envs)
    +105                final_envs.sort()
    +106                f_envs.write(os.linesep.join(final_envs))
    +107            return True
    +108
    +109        return False
    +110
    +111    def ensure(self, api_key: str, feature: str) -> None:
    +112        """Ensure that the provided API key is valid for the required method.
    +113        :param api_key: The API key to check.
    +114        :param feature: The feature for which the API key is required.
    +115        :raises MissingApiKeyError: If the API key is not valid.
    +116        """
    +117        if not self.has_key(api_key):
    +118            raise MissingApiKeyError(f"ApiKey '{api_key}' is required to use '{feature}'")
     
    @@ -212,68 +248,99 @@

    -
    32class ApiKeys(BaseSettings):
    -33    """Provide a class no handle the required Api Keys."""
    -34
    -35    OPENAI_API_KEY: str = Field(..., description="Open AI Api Key")
    -36    GOOGLE_API_KEY: str = Field(..., description="Google Api Key")
    -37    DEEPL_API_KEY: str = Field(..., description="DeepL Api Key")
    -38
    -39    @validator('OPENAI_API_KEY', 'GOOGLE_API_KEY', 'DEEPL_API_KEY')
    -40    def not_empty(cls, value):
    -41        if not value or not value.strip():
    -42            raise ValueError('must not be empty or blank')
    -43        return value
    -44
    -45    class Config:
    -46        env_file = API_KEY_FILE
    -47        env_file_encoding = Charset.UTF_8.val
    -48
    -49    @staticmethod
    -50    def prompt() -> bool:
    -51        """TODO"""
    -52
    -53        # fmt: off
    -54        form_fields = MenuInput.builder() \
    -55            .field() \
    -56                .label('OPENAI_API_KEY') \
    -57                .value(os.environ.get("OPENAI_API_KEY")) \
    -58                .min_max_length(51, 51) \
    -59                .validator(InputValidator.anything()) \
    -60                .build() \
    -61            .field() \
    -62                .label('GOOGLE_API_KEY') \
    -63                .value(os.environ.get("GOOGLE_API_KEY")) \
    -64                .min_max_length(39, 39) \
    -65                .validator(InputValidator.anything()) \
    -66                .build() \
    -67            .field() \
    -68                .label('DEEPL_API_KEY') \
    -69                .value(os.environ.get("DEEPL_API_KEY")) \
    -70                .min_max_length(39, 39) \
    -71                .validator(InputValidator.anything()) \
    -72                .build() \
    -73            .build()
    -74        # fmt: on
    -75
    -76        if result := minput(form_fields, "Please fill all required Api Keys"):
    -77            with open(API_KEY_FILE, 'r+', encoding=Charset.UTF_8.val) as f_envs:
    -78                envs = f_envs.readlines()
    -79            with open(API_KEY_FILE, 'w', encoding=Charset.UTF_8.val) as f_envs:
    -80                all_envs: set[str] = set(map(str.strip, envs))
    -81                for key, value in zip(result.attributes, result.values):
    -82                    os.environ[key.upper()] = value
    -83                    all_envs.add(f"export {key.upper()}={value}")
    -84                final_envs: list[str] = list(all_envs)
    -85                final_envs.sort()
    -86                f_envs.write(os.linesep.join(final_envs))
    -87            return True
    -88
    -89        return False
    +            
     33class ApiKeys(BaseSettings):
    + 34    """A class to manage and handle the required API keys.
    + 35    This class provides a structured way to store, access, and manage API keys necessary for various services.
    + 36    It inherits from BaseSettings to facilitate environment-based configuration.
    + 37    """
    + 38
    + 39    # fmt: off
    + 40    OPENAI_API_KEY: str = Field(..., description="Open AI Api Key")
    + 41    GOOGLE_API_KEY: str = Field(..., description="Google Api Key")
    + 42    DEEPL_API_KEY: str  = Field(..., description="DeepL Api Key")
    + 43    # fmt: on
    + 44
    + 45    @validator("OPENAI_API_KEY", "GOOGLE_API_KEY", "DEEPL_API_KEY")
    + 46    def not_empty(cls, value: AnyStr) -> AnyStr:
    + 47        """Pydantic validator to ensure that API key fields are not empty.
    + 48        :param value: The value of the API key being validated.
    + 49        :return: The value if it is not empty.
    + 50        :raises ValueError: If the value is empty or None.
    + 51        """
    + 52        if not value or not value.strip():
    + 53            raise ValueError("must not be empty or blank")
    + 54        return value
    + 55
    + 56    def has_key(self, key_name: str) -> bool:
    + 57        """Check if the specified API key exists and is not empty.
    + 58        :param key_name: The name of the API key to check.
    + 59        :return: True if the API key exists and is not empty, otherwise False.
    + 60        """
    + 61        api_key: str = key_name.upper()
    + 62        return hasattr(self, api_key) and ((kv := getattr(self, api_key)) is not None and len(kv) > 0)
    + 63
    + 64    class Config:
    + 65        """Configuration class for setting environment variables related to API keys."""
    + 66
    + 67        env_file = API_KEY_FILE
    + 68        env_file_encoding = Charset.UTF_8.val
    + 69
    + 70    @staticmethod
    + 71    def prompt() -> bool:
    + 72        """Prompt the user to input the required API keys.
    + 73        :return: True if all required API keys are successfully provided, otherwise False.
    + 74        """
    + 75
    + 76        # fmt: off
    + 77        form_fields = MenuInput.builder() \
    + 78            .field() \
    + 79                .label('OPENAI_API_KEY') \
    + 80                .value(os.environ.get("OPENAI_API_KEY")) \
    + 81                .min_max_length(51, 51) \
    + 82                .validator(InputValidator.anything()) \
    + 83                .build() \
    + 84            .field() \
    + 85                .label('GOOGLE_API_KEY') \
    + 86                .value(os.environ.get("GOOGLE_API_KEY")) \
    + 87                .min_max_length(39, 39) \
    + 88                .build() \
    + 89            .field() \
    + 90                .label('DEEPL_API_KEY') \
    + 91                .value(os.environ.get("DEEPL_API_KEY")) \
    + 92                .min_max_length(39, 39) \
    + 93                .build() \
    + 94            .build()
    + 95        # fmt: on
    + 96
    + 97        if result := minput(form_fields, "Please fill all required Api Keys"):
    + 98            with open(API_KEY_FILE, "r+", encoding=Charset.UTF_8.val) as f_envs:
    + 99                envs = f_envs.readlines()
    +100            with open(API_KEY_FILE, "w", encoding=Charset.UTF_8.val) as f_envs:
    +101                all_envs: set[str] = set(map(str.strip, envs))
    +102                for key, value in zip(result.attributes, result.values):
    +103                    os.environ[key.upper()] = value
    +104                    all_envs.add(f"export {key.upper()}={value}")
    +105                final_envs: list[str] = list(all_envs)
    +106                final_envs.sort()
    +107                f_envs.write(os.linesep.join(final_envs))
    +108            return True
    +109
    +110        return False
    +111
    +112    def ensure(self, api_key: str, feature: str) -> None:
    +113        """Ensure that the provided API key is valid for the required method.
    +114        :param api_key: The API key to check.
    +115        :param feature: The feature for which the API key is required.
    +116        :raises MissingApiKeyError: If the API key is not valid.
    +117        """
    +118        if not self.has_key(api_key):
    +119            raise MissingApiKeyError(f"ApiKey '{api_key}' is required to use '{feature}'")
     
    -

    Provide a class no handle the required Api Keys.

    +

    A class to manage and handle the required API keys. +This class provides a structured way to store, access, and manage API keys necessary for various services. +It inherits from BaseSettings to facilitate environment-based configuration.

    @@ -316,21 +383,84 @@

    @validator('OPENAI_API_KEY', 'GOOGLE_API_KEY', 'DEEPL_API_KEY')
    def - not_empty(cls, value): + not_empty(cls, value: ~AnyStr) -> ~AnyStr:

    -
    39    @validator('OPENAI_API_KEY', 'GOOGLE_API_KEY', 'DEEPL_API_KEY')
    -40    def not_empty(cls, value):
    -41        if not value or not value.strip():
    -42            raise ValueError('must not be empty or blank')
    -43        return value
    +            
    45    @validator("OPENAI_API_KEY", "GOOGLE_API_KEY", "DEEPL_API_KEY")
    +46    def not_empty(cls, value: AnyStr) -> AnyStr:
    +47        """Pydantic validator to ensure that API key fields are not empty.
    +48        :param value: The value of the API key being validated.
    +49        :return: The value if it is not empty.
    +50        :raises ValueError: If the value is empty or None.
    +51        """
    +52        if not value or not value.strip():
    +53            raise ValueError("must not be empty or blank")
    +54        return value
     
    - +

    Pydantic validator to ensure that API key fields are not empty.

    + +
    Parameters
    + +
      +
    • value: The value of the API key being validated.
    • +
    + +
    Returns
    + +
    +

    The value if it is not empty.

    +
    + +
    Raises
    + +
      +
    • ValueError: If the value is empty or None.
    • +
    +
    + + +
    +
    + +
    + + def + has_key(self, key_name: str) -> bool: + + + +
    + +
    56    def has_key(self, key_name: str) -> bool:
    +57        """Check if the specified API key exists and is not empty.
    +58        :param key_name: The name of the API key to check.
    +59        :return: True if the API key exists and is not empty, otherwise False.
    +60        """
    +61        api_key: str = key_name.upper()
    +62        return hasattr(self, api_key) and ((kv := getattr(self, api_key)) is not None and len(kv) > 0)
    +
    + + +

    Check if the specified API key exists and is not empty.

    + +
    Parameters
    + +
      +
    • key_name: The name of the API key to check.
    • +
    + +
    Returns
    + +
    +

    True if the API key exists and is not empty, otherwise False.

    +
    +
    +
    @@ -345,51 +475,97 @@

    -
    49    @staticmethod
    -50    def prompt() -> bool:
    -51        """TODO"""
    -52
    -53        # fmt: off
    -54        form_fields = MenuInput.builder() \
    -55            .field() \
    -56                .label('OPENAI_API_KEY') \
    -57                .value(os.environ.get("OPENAI_API_KEY")) \
    -58                .min_max_length(51, 51) \
    -59                .validator(InputValidator.anything()) \
    -60                .build() \
    -61            .field() \
    -62                .label('GOOGLE_API_KEY') \
    -63                .value(os.environ.get("GOOGLE_API_KEY")) \
    -64                .min_max_length(39, 39) \
    -65                .validator(InputValidator.anything()) \
    -66                .build() \
    -67            .field() \
    -68                .label('DEEPL_API_KEY') \
    -69                .value(os.environ.get("DEEPL_API_KEY")) \
    -70                .min_max_length(39, 39) \
    -71                .validator(InputValidator.anything()) \
    -72                .build() \
    -73            .build()
    -74        # fmt: on
    -75
    -76        if result := minput(form_fields, "Please fill all required Api Keys"):
    -77            with open(API_KEY_FILE, 'r+', encoding=Charset.UTF_8.val) as f_envs:
    -78                envs = f_envs.readlines()
    -79            with open(API_KEY_FILE, 'w', encoding=Charset.UTF_8.val) as f_envs:
    -80                all_envs: set[str] = set(map(str.strip, envs))
    -81                for key, value in zip(result.attributes, result.values):
    -82                    os.environ[key.upper()] = value
    -83                    all_envs.add(f"export {key.upper()}={value}")
    -84                final_envs: list[str] = list(all_envs)
    -85                final_envs.sort()
    -86                f_envs.write(os.linesep.join(final_envs))
    -87            return True
    -88
    -89        return False
    +            
     70    @staticmethod
    + 71    def prompt() -> bool:
    + 72        """Prompt the user to input the required API keys.
    + 73        :return: True if all required API keys are successfully provided, otherwise False.
    + 74        """
    + 75
    + 76        # fmt: off
    + 77        form_fields = MenuInput.builder() \
    + 78            .field() \
    + 79                .label('OPENAI_API_KEY') \
    + 80                .value(os.environ.get("OPENAI_API_KEY")) \
    + 81                .min_max_length(51, 51) \
    + 82                .validator(InputValidator.anything()) \
    + 83                .build() \
    + 84            .field() \
    + 85                .label('GOOGLE_API_KEY') \
    + 86                .value(os.environ.get("GOOGLE_API_KEY")) \
    + 87                .min_max_length(39, 39) \
    + 88                .build() \
    + 89            .field() \
    + 90                .label('DEEPL_API_KEY') \
    + 91                .value(os.environ.get("DEEPL_API_KEY")) \
    + 92                .min_max_length(39, 39) \
    + 93                .build() \
    + 94            .build()
    + 95        # fmt: on
    + 96
    + 97        if result := minput(form_fields, "Please fill all required Api Keys"):
    + 98            with open(API_KEY_FILE, "r+", encoding=Charset.UTF_8.val) as f_envs:
    + 99                envs = f_envs.readlines()
    +100            with open(API_KEY_FILE, "w", encoding=Charset.UTF_8.val) as f_envs:
    +101                all_envs: set[str] = set(map(str.strip, envs))
    +102                for key, value in zip(result.attributes, result.values):
    +103                    os.environ[key.upper()] = value
    +104                    all_envs.add(f"export {key.upper()}={value}")
    +105                final_envs: list[str] = list(all_envs)
    +106                final_envs.sort()
    +107                f_envs.write(os.linesep.join(final_envs))
    +108            return True
    +109
    +110        return False
    +
    + + +

    Prompt the user to input the required API keys.

    + +
    Returns
    + +
    +

    True if all required API keys are successfully provided, otherwise False.

    +
    +
    + + +
    +
    + +
    + + def + ensure(self, api_key: str, feature: str) -> None: + + + +
    + +
    112    def ensure(self, api_key: str, feature: str) -> None:
    +113        """Ensure that the provided API key is valid for the required method.
    +114        :param api_key: The API key to check.
    +115        :param feature: The feature for which the API key is required.
    +116        :raises MissingApiKeyError: If the API key is not valid.
    +117        """
    +118        if not self.has_key(api_key):
    +119            raise MissingApiKeyError(f"ApiKey '{api_key}' is required to use '{feature}'")
     
    -

    TODO

    +

    Ensure that the provided API key is valid for the required method.

    + +
    Parameters
    + +
      +
    • api_key: The API key to check.
    • +
    • feature: The feature for which the API key is required.
    • +
    + +
    Raises
    + +
      +
    • MissingApiKeyError: If the API key is not valid.
    • +
    @@ -430,13 +606,17 @@
    Inherited Members
    -
    45    class Config:
    -46        env_file = API_KEY_FILE
    -47        env_file_encoding = Charset.UTF_8.val
    +            
    64    class Config:
    +65        """Configuration class for setting environment variables related to API keys."""
    +66
    +67        env_file = API_KEY_FILE
    +68        env_file_encoding = Charset.UTF_8.val
     
    - +

    Configuration class for setting environment variables related to API keys.

    +
    +
    diff --git a/docs/api/askai/main/askai/core/features/router/procs.html b/docs/api/askai/main/askai/core/model/image_result.html similarity index 65% rename from docs/api/askai/main/askai/core/features/router/procs.html rename to docs/api/askai/main/askai/core/model/image_result.html index cd7c56e5..e576bb88 100644 --- a/docs/api/askai/main/askai/core/features/router/procs.html +++ b/docs/api/askai/main/askai/core/model/image_result.html @@ -3,8 +3,8 @@ - - main.askai.core.features.router.procs API documentation + + main.askai.core.model.image_result API documentation @@ -16,24 +16,51 @@

    -

    Package initialization.

    -
    + + - - - - -
     1# _*_ coding: utf-8 _*_
    - 2#
    - 3# hspylib-askai v1.0.11
    - 4#
    - 5# Package: main.askai.core.features.router.procs
    - 6"""Package initialization."""
    - 7
    - 8__all__ = [
    - 9    'qna', 
    -10    'qstring', 
    -11    'rag', 
    -12    'task_splitter'
    -13]
    -14__version__ = '1.0.11'
    +                        
    +
    +                        
     1from pydantic import BaseModel, Field
    + 2from typing import AnyStr
    + 3
    + 4
    + 5class ImageResult(BaseModel):
    + 6    """Information about an image. This class provides a schema for storing and validating image-related information
    + 7    using Pydantic's data validation features.
    + 8    """
    + 9
    +10    people_count: int = Field(description="Number of beings on the picture")
    +11    main_objects: list[str] = Field(description="List of the main objects on the picture")
    +12    env_description: str = Field(description="Description of the atmosphere of the environment")
    +13    people_description: list[str] = Field(description="List of people description")
    +14
    +15    @staticmethod
    +16    def of(image_caption: AnyStr) -> "ImageResult":
    +17        return ImageResult.model_validate_json(str(image_caption).replace("'", '"'))
     
    +
    + +
    + + class + ImageResult(pydantic.main.BaseModel): + + + +
    + +
     6class ImageResult(BaseModel):
    + 7    """Information about an image. This class provides a schema for storing and validating image-related information
    + 8    using Pydantic's data validation features.
    + 9    """
    +10
    +11    people_count: int = Field(description="Number of beings on the picture")
    +12    main_objects: list[str] = Field(description="List of the main objects on the picture")
    +13    env_description: str = Field(description="Description of the atmosphere of the environment")
    +14    people_description: list[str] = Field(description="List of people description")
    +15
    +16    @staticmethod
    +17    def of(image_caption: AnyStr) -> "ImageResult":
    +18        return ImageResult.model_validate_json(str(image_caption).replace("'", '"'))
    +
    + + +

    Information about an image. This class provides a schema for storing and validating image-related information +using Pydantic's data validation features.

    +
    + + +
    +
    + people_count: int + + +
    + + + + +
    +
    +
    + main_objects: list[str] + + +
    + + + + +
    +
    +
    + env_description: str + + +
    + + + + +
    +
    +
    + people_description: list[str] + + +
    + + + + +
    +
    + +
    +
    @staticmethod
    + + def + of(image_caption: ~AnyStr) -> ImageResult: + + + +
    + +
    16    @staticmethod
    +17    def of(image_caption: AnyStr) -> "ImageResult":
    +18        return ImageResult.model_validate_json(str(image_caption).replace("'", '"'))
    +
    + + + + +
    +
    +
    + model_config = +{} + + +
    + + + + +
    +
    +
    + model_fields = + + {'people_count': FieldInfo(annotation=int, required=True, description='Number of beings on the picture'), 'main_objects': FieldInfo(annotation=list[str], required=True, description='List of the main objects on the picture'), 'env_description': FieldInfo(annotation=str, required=True, description='Description of the atmosphere of the environment'), 'people_description': FieldInfo(annotation=list[str], required=True, description='List of people description')} + + +
    + + + + +
    +
    +
    + model_computed_fields = +{} + + +
    + + + + +
    +
    +
    Inherited Members
    +
    +
    pydantic.main.BaseModel
    +
    BaseModel
    +
    model_extra
    +
    model_fields_set
    +
    model_construct
    +
    model_copy
    +
    model_dump
    +
    model_dump_json
    +
    model_json_schema
    +
    model_parametrized_name
    +
    model_post_init
    +
    model_rebuild
    +
    model_validate
    +
    model_validate_json
    +
    model_validate_strings
    +
    dict
    +
    json
    +
    parse_obj
    +
    parse_raw
    +
    parse_file
    +
    from_orm
    +
    construct
    +
    copy
    +
    schema
    +
    schema_json
    +
    validate
    +
    update_forward_refs
    + +
    +
    +
    +
    - \ No newline at end of file diff --git a/docs/api/askai/main/askai/core/support/platform.html b/docs/api/askai/main/askai/core/support/platform.html index 989fb912..5b6792a9 100644 --- a/docs/api/askai/main/askai/core/support/platform.html +++ b/docs/api/askai/main/askai/core/support/platform.html @@ -3,7 +3,7 @@ - + main.askai.core.support.platform API documentation @@ -102,19 +102,28 @@

    24 25@lru_cache 26def get_os() -> SupportedPlatforms: -27 os_name = platform.system().lower() -28 return os_name if os_name and os_name in ["linux", "windows", "darwin"] else None -29 -30 -31@lru_cache -32def get_shell() -> SupportedShells: -33 shell = basename(os.getenv("SHELL", "bash")).lower() -34 return shell if shell and shell in ["bash", "csh", "dash", "ksh", "tcsh", "zsh", "sh"] else None -35 -36 -37@lru_cache -38def get_user() -> str: -39 return os.getenv("USER", "user") +27 """Retrieve the current operating system platform. +28 :return: The current operating system as a `SupportedPlatforms` literal value. +29 """ +30 os_name = platform.system().lower() +31 return os_name if os_name and os_name in ["linux", "windows", "darwin"] else None +32 +33 +34@lru_cache +35def get_shell() -> SupportedShells: +36 """Retrieve the current shell being used. +37 :return: The current shell as a `SupportedShells` literal value. +38 """ +39 shell = basename(os.getenv("SHELL", "bash")).lower() +40 return shell if shell and shell in ["bash", "csh", "dash", "ksh", "tcsh", "zsh", "sh"] else None +41 +42 +43@lru_cache +44def get_user() -> str: +45 """Retrieve the current user's username. +46 :return: The username of the current user as a string. Returns "user" if the username is not found. +47 """ +48 return os.getenv("USER", "user")

    @@ -157,12 +166,23 @@

    26@lru_cache
     27def get_os() -> SupportedPlatforms:
    -28    os_name = platform.system().lower()
    -29    return os_name if os_name and os_name in ["linux", "windows", "darwin"] else None
    +28    """Retrieve the current operating system platform.
    +29    :return: The current operating system as a `SupportedPlatforms` literal value.
    +30    """
    +31    os_name = platform.system().lower()
    +32    return os_name if os_name and os_name in ["linux", "windows", "darwin"] else None
     
    - +

    Retrieve the current operating system platform.

    + +
    Returns
    + +
    +

    The current operating system as a SupportedPlatforms literal value.

    +
    +
    +
    @@ -177,14 +197,25 @@

    -
    32@lru_cache
    -33def get_shell() -> SupportedShells:
    -34    shell = basename(os.getenv("SHELL", "bash")).lower()
    -35    return shell if shell and shell in ["bash", "csh", "dash", "ksh", "tcsh", "zsh", "sh"] else None
    +            
    35@lru_cache
    +36def get_shell() -> SupportedShells:
    +37    """Retrieve the current shell being used.
    +38    :return: The current shell as a `SupportedShells` literal value.
    +39    """
    +40    shell = basename(os.getenv("SHELL", "bash")).lower()
    +41    return shell if shell and shell in ["bash", "csh", "dash", "ksh", "tcsh", "zsh", "sh"] else None
     
    - +

    Retrieve the current shell being used.

    + +
    Returns
    + +
    +

    The current shell as a SupportedShells literal value.

    +
    +
    +
    @@ -199,13 +230,24 @@

    -
    38@lru_cache
    -39def get_user() -> str:
    -40    return os.getenv("USER", "user")
    +            
    44@lru_cache
    +45def get_user() -> str:
    +46    """Retrieve the current user's username.
    +47    :return: The username of the current user as a string. Returns "user" if the username is not found.
    +48    """
    +49    return os.getenv("USER", "user")
     
    - +

    Retrieve the current user's username.

    + +
    Returns
    + +
    +

    The username of the current user as a string. Returns "user" if the username is not found.

    +
    +
    + diff --git a/docs/api/askai/main/askai/core/support/presets.html b/docs/api/askai/main/askai/core/support/presets.html index 1f047b2a..b151bb5a 100644 --- a/docs/api/askai/main/askai/core/support/presets.html +++ b/docs/api/askai/main/askai/core/support/presets.html @@ -3,7 +3,7 @@ - + main.askai.core.support.presets API documentation @@ -152,96 +152,102 @@

    50 @classmethod 51 @lru_cache 52 def get(cls, lang: str = "en", tempo: int = 1, base_interval: float = 0.010) -> "Presets": - 53 base_speed = base_interval / max(1, tempo) - 54 presets = cls._ALL_RESETS[lang] if hasattr(cls._ALL_RESETS, lang) else cls._ALL_RESETS["en"] - 55 return Presets( - 56 lang, - 57 int(presets["words.per.breath"]), - 58 base_speed, - 59 float(eval(presets["words.interval.sec"].substitute(base_speed=base_speed))), - 60 float(eval(presets["breath.interval.sec"].substitute(base_speed=base_speed))), - 61 float(eval(presets["number.interval.sec"].substitute(base_speed=base_speed))), - 62 float(eval(presets["comma.interval.sec"].substitute(base_speed=base_speed))), - 63 float(eval(presets["punct.interval.sec"].substitute(base_speed=base_speed))), - 64 float(eval(presets["enum.interval.sec"].substitute(base_speed=base_speed))), - 65 float(eval(presets["period.interval.sec"].substitute(base_speed=base_speed))), - 66 ) - 67 - 68 def __init__( - 69 self, - 70 lang: str, - 71 words_per_breath: int, - 72 base_speed: float, - 73 words_interval: float, - 74 breath_interval: float, - 75 number_interval: float, - 76 comma_interval: float, - 77 punct_interval: float, - 78 enum_interval: float, - 79 period_interval: float, - 80 ): - 81 self._lang = lang - 82 self._words_per_breath = words_per_breath - 83 self._base_speed = base_speed - 84 self._words_interval = words_interval - 85 self._breath_interval = breath_interval - 86 self._number_interval = number_interval - 87 self._comma_interval = comma_interval - 88 self._punct_interval = punct_interval - 89 self._enum_interval = enum_interval - 90 self._period_interval = period_interval - 91 - 92 def __str__(self) -> str: - 93 return dedent( - 94 ( - 95 f"Presets.{self._lang}(" - 96 f"WordsPerBreath={self.words_per_breath}[sec], " - 97 f"Base Speed={self.base_speed}[sec], " - 98 f"Words={self.words_interval}[sec], " - 99 f"Breaths={self.breath_interval}[sec], " -100 f"Numbers={self.number_interval}[sec], " -101 f"Commas={self.comma_interval}[sec], " -102 f"Punctuation={self.punct_interval}[sec], " -103 f"Enums={self.enum_interval}[sec], " -104 f"Period={self.period_interval}[sec]" -105 ) -106 ) -107 -108 @property -109 def words_per_breath(self) -> int: -110 return self._words_per_breath -111 -112 @property -113 def base_speed(self) -> float: -114 return self._base_speed -115 -116 @property -117 def words_interval(self) -> float: -118 return self._words_interval -119 -120 @property -121 def breath_interval(self) -> float: -122 return self._breath_interval -123 -124 @property -125 def number_interval(self) -> float: -126 return self._number_interval -127 -128 @property -129 def comma_interval(self) -> float: -130 return self._comma_interval -131 -132 @property -133 def punct_interval(self) -> float: -134 return self._punct_interval -135 -136 @property -137 def enum_interval(self) -> float: -138 return self._enum_interval -139 -140 @property -141 def period_interval(self) -> float: -142 return self._period_interval + 53 """Retrieve the text-to-speech preset based on the specified parameters. + 54 :param lang: The language code for the preset (default is "en" -> English). + 55 :param tempo: The tempo of the speech, where 1 is the default speed (natural). + 56 :param base_interval: The base interval between speech units (default is 0.010). + 57 :return: An instance of the Presets class configured with the specified parameters. + 58 """ + 59 base_speed = base_interval / max(1, tempo) + 60 presets = cls._ALL_RESETS[lang] if hasattr(cls._ALL_RESETS, lang) else cls._ALL_RESETS["en"] + 61 return Presets( + 62 lang, + 63 int(presets["words.per.breath"]), + 64 base_speed, + 65 float(eval(presets["words.interval.sec"].substitute(base_speed=base_speed))), + 66 float(eval(presets["breath.interval.sec"].substitute(base_speed=base_speed))), + 67 float(eval(presets["number.interval.sec"].substitute(base_speed=base_speed))), + 68 float(eval(presets["comma.interval.sec"].substitute(base_speed=base_speed))), + 69 float(eval(presets["punct.interval.sec"].substitute(base_speed=base_speed))), + 70 float(eval(presets["enum.interval.sec"].substitute(base_speed=base_speed))), + 71 float(eval(presets["period.interval.sec"].substitute(base_speed=base_speed))), + 72 ) + 73 + 74 def __init__( + 75 self, + 76 lang: str, + 77 words_per_breath: int, + 78 base_speed: float, + 79 words_interval: float, + 80 breath_interval: float, + 81 number_interval: float, + 82 comma_interval: float, + 83 punct_interval: float, + 84 enum_interval: float, + 85 period_interval: float, + 86 ): + 87 self._lang = lang + 88 self._words_per_breath = words_per_breath + 89 self._base_speed = base_speed + 90 self._words_interval = words_interval + 91 self._breath_interval = breath_interval + 92 self._number_interval = number_interval + 93 self._comma_interval = comma_interval + 94 self._punct_interval = punct_interval + 95 self._enum_interval = enum_interval + 96 self._period_interval = period_interval + 97 + 98 def __str__(self) -> str: + 99 return dedent( +100 ( +101 f"Presets.{self._lang}(" +102 f"WordsPerBreath={self.words_per_breath}[sec], " +103 f"Base Speed={self.base_speed}[sec], " +104 f"Words={self.words_interval}[sec], " +105 f"Breaths={self.breath_interval}[sec], " +106 f"Numbers={self.number_interval}[sec], " +107 f"Commas={self.comma_interval}[sec], " +108 f"Punctuation={self.punct_interval}[sec], " +109 f"Enums={self.enum_interval}[sec], " +110 f"Period={self.period_interval}[sec]" +111 ) +112 ) +113 +114 @property +115 def words_per_breath(self) -> int: +116 return self._words_per_breath +117 +118 @property +119 def base_speed(self) -> float: +120 return self._base_speed +121 +122 @property +123 def words_interval(self) -> float: +124 return self._words_interval +125 +126 @property +127 def breath_interval(self) -> float: +128 return self._breath_interval +129 +130 @property +131 def number_interval(self) -> float: +132 return self._number_interval +133 +134 @property +135 def comma_interval(self) -> float: +136 return self._comma_interval +137 +138 @property +139 def punct_interval(self) -> float: +140 return self._punct_interval +141 +142 @property +143 def enum_interval(self) -> float: +144 return self._enum_interval +145 +146 @property +147 def period_interval(self) -> float: +148 return self._period_interval

    @@ -290,96 +296,102 @@

    51 @classmethod 52 @lru_cache 53 def get(cls, lang: str = "en", tempo: int = 1, base_interval: float = 0.010) -> "Presets": - 54 base_speed = base_interval / max(1, tempo) - 55 presets = cls._ALL_RESETS[lang] if hasattr(cls._ALL_RESETS, lang) else cls._ALL_RESETS["en"] - 56 return Presets( - 57 lang, - 58 int(presets["words.per.breath"]), - 59 base_speed, - 60 float(eval(presets["words.interval.sec"].substitute(base_speed=base_speed))), - 61 float(eval(presets["breath.interval.sec"].substitute(base_speed=base_speed))), - 62 float(eval(presets["number.interval.sec"].substitute(base_speed=base_speed))), - 63 float(eval(presets["comma.interval.sec"].substitute(base_speed=base_speed))), - 64 float(eval(presets["punct.interval.sec"].substitute(base_speed=base_speed))), - 65 float(eval(presets["enum.interval.sec"].substitute(base_speed=base_speed))), - 66 float(eval(presets["period.interval.sec"].substitute(base_speed=base_speed))), - 67 ) - 68 - 69 def __init__( - 70 self, - 71 lang: str, - 72 words_per_breath: int, - 73 base_speed: float, - 74 words_interval: float, - 75 breath_interval: float, - 76 number_interval: float, - 77 comma_interval: float, - 78 punct_interval: float, - 79 enum_interval: float, - 80 period_interval: float, - 81 ): - 82 self._lang = lang - 83 self._words_per_breath = words_per_breath - 84 self._base_speed = base_speed - 85 self._words_interval = words_interval - 86 self._breath_interval = breath_interval - 87 self._number_interval = number_interval - 88 self._comma_interval = comma_interval - 89 self._punct_interval = punct_interval - 90 self._enum_interval = enum_interval - 91 self._period_interval = period_interval - 92 - 93 def __str__(self) -> str: - 94 return dedent( - 95 ( - 96 f"Presets.{self._lang}(" - 97 f"WordsPerBreath={self.words_per_breath}[sec], " - 98 f"Base Speed={self.base_speed}[sec], " - 99 f"Words={self.words_interval}[sec], " -100 f"Breaths={self.breath_interval}[sec], " -101 f"Numbers={self.number_interval}[sec], " -102 f"Commas={self.comma_interval}[sec], " -103 f"Punctuation={self.punct_interval}[sec], " -104 f"Enums={self.enum_interval}[sec], " -105 f"Period={self.period_interval}[sec]" -106 ) -107 ) -108 -109 @property -110 def words_per_breath(self) -> int: -111 return self._words_per_breath -112 -113 @property -114 def base_speed(self) -> float: -115 return self._base_speed -116 -117 @property -118 def words_interval(self) -> float: -119 return self._words_interval -120 -121 @property -122 def breath_interval(self) -> float: -123 return self._breath_interval -124 -125 @property -126 def number_interval(self) -> float: -127 return self._number_interval -128 -129 @property -130 def comma_interval(self) -> float: -131 return self._comma_interval -132 -133 @property -134 def punct_interval(self) -> float: -135 return self._punct_interval -136 -137 @property -138 def enum_interval(self) -> float: -139 return self._enum_interval -140 -141 @property -142 def period_interval(self) -> float: -143 return self._period_interval + 54 """Retrieve the text-to-speech preset based on the specified parameters. + 55 :param lang: The language code for the preset (default is "en" -> English). + 56 :param tempo: The tempo of the speech, where 1 is the default speed (natural). + 57 :param base_interval: The base interval between speech units (default is 0.010). + 58 :return: An instance of the Presets class configured with the specified parameters. + 59 """ + 60 base_speed = base_interval / max(1, tempo) + 61 presets = cls._ALL_RESETS[lang] if hasattr(cls._ALL_RESETS, lang) else cls._ALL_RESETS["en"] + 62 return Presets( + 63 lang, + 64 int(presets["words.per.breath"]), + 65 base_speed, + 66 float(eval(presets["words.interval.sec"].substitute(base_speed=base_speed))), + 67 float(eval(presets["breath.interval.sec"].substitute(base_speed=base_speed))), + 68 float(eval(presets["number.interval.sec"].substitute(base_speed=base_speed))), + 69 float(eval(presets["comma.interval.sec"].substitute(base_speed=base_speed))), + 70 float(eval(presets["punct.interval.sec"].substitute(base_speed=base_speed))), + 71 float(eval(presets["enum.interval.sec"].substitute(base_speed=base_speed))), + 72 float(eval(presets["period.interval.sec"].substitute(base_speed=base_speed))), + 73 ) + 74 + 75 def __init__( + 76 self, + 77 lang: str, + 78 words_per_breath: int, + 79 base_speed: float, + 80 words_interval: float, + 81 breath_interval: float, + 82 number_interval: float, + 83 comma_interval: float, + 84 punct_interval: float, + 85 enum_interval: float, + 86 period_interval: float, + 87 ): + 88 self._lang = lang + 89 self._words_per_breath = words_per_breath + 90 self._base_speed = base_speed + 91 self._words_interval = words_interval + 92 self._breath_interval = breath_interval + 93 self._number_interval = number_interval + 94 self._comma_interval = comma_interval + 95 self._punct_interval = punct_interval + 96 self._enum_interval = enum_interval + 97 self._period_interval = period_interval + 98 + 99 def __str__(self) -> str: +100 return dedent( +101 ( +102 f"Presets.{self._lang}(" +103 f"WordsPerBreath={self.words_per_breath}[sec], " +104 f"Base Speed={self.base_speed}[sec], " +105 f"Words={self.words_interval}[sec], " +106 f"Breaths={self.breath_interval}[sec], " +107 f"Numbers={self.number_interval}[sec], " +108 f"Commas={self.comma_interval}[sec], " +109 f"Punctuation={self.punct_interval}[sec], " +110 f"Enums={self.enum_interval}[sec], " +111 f"Period={self.period_interval}[sec]" +112 ) +113 ) +114 +115 @property +116 def words_per_breath(self) -> int: +117 return self._words_per_breath +118 +119 @property +120 def base_speed(self) -> float: +121 return self._base_speed +122 +123 @property +124 def words_interval(self) -> float: +125 return self._words_interval +126 +127 @property +128 def breath_interval(self) -> float: +129 return self._breath_interval +130 +131 @property +132 def number_interval(self) -> float: +133 return self._number_interval +134 +135 @property +136 def comma_interval(self) -> float: +137 return self._comma_interval +138 +139 @property +140 def punct_interval(self) -> float: +141 return self._punct_interval +142 +143 @property +144 def enum_interval(self) -> float: +145 return self._enum_interval +146 +147 @property +148 def period_interval(self) -> float: +149 return self._period_interval @@ -397,29 +409,29 @@

    -
    69    def __init__(
    -70        self,
    -71        lang: str,
    -72        words_per_breath: int,
    -73        base_speed: float,
    -74        words_interval: float,
    -75        breath_interval: float,
    -76        number_interval: float,
    -77        comma_interval: float,
    -78        punct_interval: float,
    -79        enum_interval: float,
    -80        period_interval: float,
    -81    ):
    -82        self._lang = lang
    -83        self._words_per_breath = words_per_breath
    -84        self._base_speed = base_speed
    -85        self._words_interval = words_interval
    -86        self._breath_interval = breath_interval
    -87        self._number_interval = number_interval
    -88        self._comma_interval = comma_interval
    -89        self._punct_interval = punct_interval
    -90        self._enum_interval = enum_interval
    -91        self._period_interval = period_interval
    +            
    75    def __init__(
    +76        self,
    +77        lang: str,
    +78        words_per_breath: int,
    +79        base_speed: float,
    +80        words_interval: float,
    +81        breath_interval: float,
    +82        number_interval: float,
    +83        comma_interval: float,
    +84        punct_interval: float,
    +85        enum_interval: float,
    +86        period_interval: float,
    +87    ):
    +88        self._lang = lang
    +89        self._words_per_breath = words_per_breath
    +90        self._base_speed = base_speed
    +91        self._words_interval = words_interval
    +92        self._breath_interval = breath_interval
    +93        self._number_interval = number_interval
    +94        self._comma_interval = comma_interval
    +95        self._punct_interval = punct_interval
    +96        self._enum_interval = enum_interval
    +97        self._period_interval = period_interval
     
    @@ -442,24 +454,46 @@

    51    @classmethod
     52    @lru_cache
     53    def get(cls, lang: str = "en", tempo: int = 1, base_interval: float = 0.010) -> "Presets":
    -54        base_speed = base_interval / max(1, tempo)
    -55        presets = cls._ALL_RESETS[lang] if hasattr(cls._ALL_RESETS, lang) else cls._ALL_RESETS["en"]
    -56        return Presets(
    -57            lang,
    -58            int(presets["words.per.breath"]),
    -59            base_speed,
    -60            float(eval(presets["words.interval.sec"].substitute(base_speed=base_speed))),
    -61            float(eval(presets["breath.interval.sec"].substitute(base_speed=base_speed))),
    -62            float(eval(presets["number.interval.sec"].substitute(base_speed=base_speed))),
    -63            float(eval(presets["comma.interval.sec"].substitute(base_speed=base_speed))),
    -64            float(eval(presets["punct.interval.sec"].substitute(base_speed=base_speed))),
    -65            float(eval(presets["enum.interval.sec"].substitute(base_speed=base_speed))),
    -66            float(eval(presets["period.interval.sec"].substitute(base_speed=base_speed))),
    -67        )
    +54        """Retrieve the text-to-speech preset based on the specified parameters.
    +55        :param lang: The language code for the preset (default is "en" -> English).
    +56        :param tempo: The tempo of the speech, where 1 is the default speed (natural).
    +57        :param base_interval: The base interval between speech units (default is 0.010).
    +58        :return: An instance of the Presets class configured with the specified parameters.
    +59        """
    +60        base_speed = base_interval / max(1, tempo)
    +61        presets = cls._ALL_RESETS[lang] if hasattr(cls._ALL_RESETS, lang) else cls._ALL_RESETS["en"]
    +62        return Presets(
    +63            lang,
    +64            int(presets["words.per.breath"]),
    +65            base_speed,
    +66            float(eval(presets["words.interval.sec"].substitute(base_speed=base_speed))),
    +67            float(eval(presets["breath.interval.sec"].substitute(base_speed=base_speed))),
    +68            float(eval(presets["number.interval.sec"].substitute(base_speed=base_speed))),
    +69            float(eval(presets["comma.interval.sec"].substitute(base_speed=base_speed))),
    +70            float(eval(presets["punct.interval.sec"].substitute(base_speed=base_speed))),
    +71            float(eval(presets["enum.interval.sec"].substitute(base_speed=base_speed))),
    +72            float(eval(presets["period.interval.sec"].substitute(base_speed=base_speed))),
    +73        )
     
    - +

    Retrieve the text-to-speech preset based on the specified parameters.

    + +
    Parameters
    + +
      +
    • lang: The language code for the preset (default is "en" -> English).
    • +
    • tempo: The tempo of the speech, where 1 is the default speed (natural).
    • +
    • base_interval: The base interval between speech units (default is 0.010).
    • +
    + +
    Returns
    + +
    +

    An instance of the Presets class configured with the specified parameters.

    +
    +
    +

    @@ -471,9 +505,9 @@

    -
    109    @property
    -110    def words_per_breath(self) -> int:
    -111        return self._words_per_breath
    +            
    115    @property
    +116    def words_per_breath(self) -> int:
    +117        return self._words_per_breath
     
    @@ -489,9 +523,9 @@

    -
    113    @property
    -114    def base_speed(self) -> float:
    -115        return self._base_speed
    +            
    119    @property
    +120    def base_speed(self) -> float:
    +121        return self._base_speed
     
    @@ -507,9 +541,9 @@

    -
    117    @property
    -118    def words_interval(self) -> float:
    -119        return self._words_interval
    +            
    123    @property
    +124    def words_interval(self) -> float:
    +125        return self._words_interval
     
    @@ -525,9 +559,9 @@

    -
    121    @property
    -122    def breath_interval(self) -> float:
    -123        return self._breath_interval
    +            
    127    @property
    +128    def breath_interval(self) -> float:
    +129        return self._breath_interval
     
    @@ -543,9 +577,9 @@

    -
    125    @property
    -126    def number_interval(self) -> float:
    -127        return self._number_interval
    +            
    131    @property
    +132    def number_interval(self) -> float:
    +133        return self._number_interval
     
    @@ -561,9 +595,9 @@

    -
    129    @property
    -130    def comma_interval(self) -> float:
    -131        return self._comma_interval
    +            
    135    @property
    +136    def comma_interval(self) -> float:
    +137        return self._comma_interval
     
    @@ -579,9 +613,9 @@

    -
    133    @property
    -134    def punct_interval(self) -> float:
    -135        return self._punct_interval
    +            
    139    @property
    +140    def punct_interval(self) -> float:
    +141        return self._punct_interval
     
    @@ -597,9 +631,9 @@

    -
    137    @property
    -138    def enum_interval(self) -> float:
    -139        return self._enum_interval
    +            
    143    @property
    +144    def enum_interval(self) -> float:
    +145        return self._enum_interval
     
    @@ -615,9 +649,9 @@

    -
    141    @property
    -142    def period_interval(self) -> float:
    -143        return self._period_interval
    +            
    147    @property
    +148    def period_interval(self) -> float:
    +149        return self._period_interval
     
    diff --git a/docs/api/askai/main/askai/core/support/rag_provider.html b/docs/api/askai/main/askai/core/support/rag_provider.html new file mode 100644 index 00000000..fe12a340 --- /dev/null +++ b/docs/api/askai/main/askai/core/support/rag_provider.html @@ -0,0 +1,466 @@ + + + + + + + main.askai.core.support.rag_provider API documentation + + + + + + + + + +
    +
    +

    +main.askai.core.support.rag_provider

    + +

    @project: HsPyLib-AskAI +@package: askai.core.support + @file: rag_provider.py +@created: Wed, 28 Aug 2024 + @author: Hugo Saporetti Junior + @site: https://github.com/yorevs/askai +@license: MIT - Please refer to https://opensource.org/licenses/MIT

    + +

    Copyright (c) 2024, HomeSetup

    +
    + + + + + +
     1#!/usr/bin/env python3
    + 2# -*- coding: utf-8 -*-
    + 3
    + 4"""
    + 5   @project: HsPyLib-AskAI
    + 6   @package: askai.core.support
    + 7      @file: rag_provider.py
    + 8   @created: Wed, 28 Aug 2024
    + 9    @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior
    +10      @site: https://github.com/yorevs/askai
    +11   @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
    +12
    +13   Copyright (c) 2024, HomeSetup
    +14"""
    +15from askai.__classpath__ import classpath
    +16from askai.core.askai_configs import configs
    +17from askai.core.support.langchain_support import lc_llm
    +18from functools import lru_cache
    +19from hspylib.core.preconditions import check_state
    +20from hspylib.core.tools.commons import file_is_not_empty
    +21from langchain_community.document_loaders import CSVLoader
    +22from langchain_community.vectorstores import FAISS
    +23from langchain_core.documents import Document
    +24from langchain_core.vectorstores import VectorStore
    +25from pathlib import Path
    +26from textwrap import dedent
    +27
    +28import os
    +29
    +30
    +31class RAGProvider:
    +32    """A class responsible for implementing the Retrieval-Augmented Generation (RAG) mechanism."""
    +33
    +34    RAG_DIR: Path = Path(os.path.join(classpath.resource_path(), "rag"))
    +35
    +36    def __init__(self, rag_filepath: str):
    +37        self._rag_db = None
    +38        self._rag_path: str = os.path.join(str(self.RAG_DIR), rag_filepath)
    +39        self._rag_docs: list[Document] = CSVLoader(file_path=self._rag_path).load()
    +40        self._rag_db: VectorStore | None = None
    +41        check_state(file_is_not_empty(self._rag_path))
    +42
    +43    @lru_cache
    +44    def get_rag_examples(self, query: str, k: int = configs.rag_retrival_amount) -> str:
    +45        """Retrieve a list of relevant examples based on the provided query.
    +46        :param query: The search query used to retrieve examples.
    +47        :param k: The number of examples to retrieve (default is 3).
    +48        :return: A list of strings representing the retrieved examples.
    +49        """
    +50        if configs.is_rag:
    +51            if self._rag_db is None:
    +52                self._rag_db = FAISS.from_documents(self._rag_docs, lc_llm.create_embeddings())
    +53            example_docs: list[Document] = self._rag_db.similarity_search(query, k=k)
    +54            rag_examples = os.linesep.join([doc.page_content for doc in example_docs])
    +55            return dedent(
    +56                f"""
    +57            **Examples:**
    +58            \"\"\"
    +59            {rag_examples}
    +60            \"\"\"
    +61            """
    +62            ).strip()
    +
    + + +
    +
    + +
    + + class + RAGProvider: + + + +
    + +
    32class RAGProvider:
    +33    """A class responsible for implementing the Retrieval-Augmented Generation (RAG) mechanism."""
    +34
    +35    RAG_DIR: Path = Path(os.path.join(classpath.resource_path(), "rag"))
    +36
    +37    def __init__(self, rag_filepath: str):
    +38        self._rag_db = None
    +39        self._rag_path: str = os.path.join(str(self.RAG_DIR), rag_filepath)
    +40        self._rag_docs: list[Document] = CSVLoader(file_path=self._rag_path).load()
    +41        self._rag_db: VectorStore | None = None
    +42        check_state(file_is_not_empty(self._rag_path))
    +43
    +44    @lru_cache
    +45    def get_rag_examples(self, query: str, k: int = configs.rag_retrival_amount) -> str:
    +46        """Retrieve a list of relevant examples based on the provided query.
    +47        :param query: The search query used to retrieve examples.
    +48        :param k: The number of examples to retrieve (default is 3).
    +49        :return: A list of strings representing the retrieved examples.
    +50        """
    +51        if configs.is_rag:
    +52            if self._rag_db is None:
    +53                self._rag_db = FAISS.from_documents(self._rag_docs, lc_llm.create_embeddings())
    +54            example_docs: list[Document] = self._rag_db.similarity_search(query, k=k)
    +55            rag_examples = os.linesep.join([doc.page_content for doc in example_docs])
    +56            return dedent(
    +57                f"""
    +58            **Examples:**
    +59            \"\"\"
    +60            {rag_examples}
    +61            \"\"\"
    +62            """
    +63            ).strip()
    +
    + + +

    A class responsible for implementing the Retrieval-Augmented Generation (RAG) mechanism.

    +
    + + +
    + +
    + + RAGProvider(rag_filepath: str) + + + +
    + +
    37    def __init__(self, rag_filepath: str):
    +38        self._rag_db = None
    +39        self._rag_path: str = os.path.join(str(self.RAG_DIR), rag_filepath)
    +40        self._rag_docs: list[Document] = CSVLoader(file_path=self._rag_path).load()
    +41        self._rag_db: VectorStore | None = None
    +42        check_state(file_is_not_empty(self._rag_path))
    +
    + + + + +
    +
    +
    + RAG_DIR: pathlib.Path = +PosixPath('/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources/rag') + + +
    + + + + +
    +
    + +
    +
    @lru_cache
    + + def + get_rag_examples(self, query: str, k: int = 3) -> str: + + + +
    + +
    44    @lru_cache
    +45    def get_rag_examples(self, query: str, k: int = configs.rag_retrival_amount) -> str:
    +46        """Retrieve a list of relevant examples based on the provided query.
    +47        :param query: The search query used to retrieve examples.
    +48        :param k: The number of examples to retrieve (default is 3).
    +49        :return: A list of strings representing the retrieved examples.
    +50        """
    +51        if configs.is_rag:
    +52            if self._rag_db is None:
    +53                self._rag_db = FAISS.from_documents(self._rag_docs, lc_llm.create_embeddings())
    +54            example_docs: list[Document] = self._rag_db.similarity_search(query, k=k)
    +55            rag_examples = os.linesep.join([doc.page_content for doc in example_docs])
    +56            return dedent(
    +57                f"""
    +58            **Examples:**
    +59            \"\"\"
    +60            {rag_examples}
    +61            \"\"\"
    +62            """
    +63            ).strip()
    +
    + + +

    Retrieve a list of relevant examples based on the provided query.

    + +
    Parameters
    + +
      +
    • query: The search query used to retrieve examples.
    • +
    • k: The number of examples to retrieve (default is 3).
    • +
    + +
    Returns
    + +
    +

    A list of strings representing the retrieved examples.

    +
    +
    + + +
    +
    +
    + + \ No newline at end of file diff --git a/docs/api/askai/main/askai/core/support/shared_instances.html b/docs/api/askai/main/askai/core/support/shared_instances.html index adabaf48..f6e13507 100644 --- a/docs/api/askai/main/askai/core/support/shared_instances.html +++ b/docs/api/askai/main/askai/core/support/shared_instances.html @@ -3,7 +3,7 @@ - + main.askai.core.support.shared_instances API documentation @@ -135,161 +135,174 @@

    12 13 Copyright (c) 2024, HomeSetup 14""" - 15from askai.__classpath__ import classpath - 16from askai.core.askai_configs import configs - 17from askai.core.askai_messages import msg - 18from askai.core.askai_prompt import prompt - 19from askai.core.component.cache_service import cache - 20from askai.core.component.geo_location import geo_location - 21from askai.core.component.recorder import recorder - 22from askai.core.engine.ai_engine import AIEngine - 23from askai.core.engine.engine_factory import EngineFactory - 24from askai.core.support.chat_context import ChatContext - 25from askai.core.support.utilities import display_text - 26from clitt.core.term.terminal import terminal - 27from clitt.core.tui.line_input.line_input import line_input - 28from hspylib.core.metaclass.singleton import Singleton - 29from hspylib.core.preconditions import check_state - 30from hspylib.core.tools.text_tools import elide_text - 31from hspylib.modules.application.version import Version - 32from hspylib.modules.cli.keyboard import Keyboard - 33from langchain.memory import ConversationBufferWindowMemory - 34from pathlib import Path - 35from typing import Optional - 36 - 37import os + 15 + 16from askai.__classpath__ import classpath + 17from askai.core.askai_configs import configs + 18from askai.core.askai_messages import msg + 19from askai.core.askai_prompt import prompt + 20from askai.core.component.cache_service import cache + 21from askai.core.component.geo_location import geo_location + 22from askai.core.component.recorder import recorder + 23from askai.core.engine.ai_engine import AIEngine + 24from askai.core.engine.engine_factory import EngineFactory + 25from askai.core.support.chat_context import ChatContext + 26from askai.core.support.utilities import display_text + 27from clitt.core.term.terminal import terminal + 28from clitt.core.tui.line_input.line_input import line_input + 29from hspylib.core.metaclass.singleton import Singleton + 30from hspylib.core.preconditions import check_state + 31from hspylib.core.tools.text_tools import elide_text + 32from hspylib.modules.application.version import Version + 33from hspylib.modules.cli.keyboard import Keyboard + 34from langchain.memory import ConversationBufferWindowMemory + 35from langchain.memory.chat_memory import BaseChatMemory + 36from pathlib import Path + 37from typing import Optional 38 - 39 - 40class SharedInstances(metaclass=Singleton): - 41 """Provides access to shared instances.""" - 42 - 43 INSTANCE: "SharedInstances" + 39import os + 40 + 41 + 42class SharedInstances(metaclass=Singleton): + 43 """Provides access to shared instances.""" 44 - 45 # This is the uuid used in the prompts to indicate that the AI does not know the answer. - 46 UNCERTAIN_ID: str = "bde6f44d-c1a0-4b0c-bd74-8278e468e50c" - 47 - 48 def __init__(self) -> None: - 49 self._engine: AIEngine | None = None - 50 self._context: ChatContext | None = None - 51 self._memory: ConversationBufferWindowMemory | None = None - 52 self._idiom: str = configs.language.idiom - 53 self._max_short_memory_size: int = configs.max_short_memory_size - 54 self._max_iteractions: int = configs.max_iteractions - 55 - 56 @property - 57 def engine(self) -> Optional[AIEngine]: - 58 return self._engine - 59 - 60 @engine.setter - 61 def engine(self, value: AIEngine) -> None: - 62 check_state(self._engine is None, "Once set, this instance is immutable.") - 63 self._engine = value - 64 - 65 @property - 66 def context(self) -> Optional[ChatContext]: - 67 return self._context - 68 - 69 @context.setter - 70 def context(self, value: ChatContext) -> None: - 71 check_state(self._context is None, "Once set, this instance is immutable.") - 72 self._context = value - 73 - 74 @property - 75 def nickname(self) -> str: - 76 return f"%WHITE% Taius:%NC% " - 77 - 78 @property - 79 def username(self) -> str: - 80 return f"%GREEN% {prompt.user.title()}:%NC% " - 81 - 82 @property - 83 def nickname_md(self) -> str: - 84 return f"* Taius:* " - 85 - 86 @property - 87 def username_md(self) -> str: - 88 return f"** {prompt.user.title()}:** " - 89 - 90 @property - 91 def idiom(self) -> str: - 92 return self._idiom - 93 - 94 @property - 95 def memory(self) -> ConversationBufferWindowMemory: - 96 return self.create_memory() - 97 - 98 @property - 99 def max_short_memory_size(self) -> int: -100 return self._max_short_memory_size -101 -102 @property -103 def max_iteractions(self) -> int: -104 return self._max_iteractions -105 -106 @property -107 def app_info(self) -> str: -108 device_info = f"{recorder.input_device[1]}" if recorder.input_device else "" -109 device_info += f", AUTO-SWAP {'ON' if recorder.is_auto_swap else '%RED%OFF'}" -110 dtm = f" {geo_location.datetime} " -111 speak_info = str(configs.tempo) + " @" + self.engine.configs().tts_voice -112 cur_dir = elide_text(str(Path(os.getcwd()).absolute()), 67, "…") -113 translator = f"translated by '{msg.translator.name()}'" if configs.language.name.title() != "English" else "" -114 return ( -115 f"%GREEN%" -116 f"AskAI v{Version.load(load_dir=classpath.source_path())} %EOL%" -117 f"{dtm.center(80, '=')} %EOL%" -118 f" Language: {configs.language} {translator} %EOL%" -119 f" Engine: {shared.engine} %EOL%" -120 f" Dir: {cur_dir} %EOL%" -121 f" OS: {prompt.os_type}/{prompt.shell} %EOL%" -122 f"{'-' * 80} %EOL%" -123 f" Microphone: {device_info or '%RED%Undetected'} %GREEN%%EOL%" -124 f" Debugging: {'ON' if configs.is_debug else '%RED%OFF'} %GREEN%%EOL%" -125 f" Speaking: {'ON, tempo: ' + speak_info if configs.is_speak else '%RED%OFF'} %GREEN%%EOL%" -126 f" Caching: {'ON, TTL: ' + str(configs.ttl) if configs.is_cache else '%RED%OFF'} %GREEN%%EOL%" -127 f"{'=' * 80}%EOL%%NC%" -128 ) -129 -130 def create_engine(self, engine_name: str, model_name: str) -> AIEngine: -131 """Create an AI engine specified by the engine and model names.""" -132 if self._engine is None: -133 self._engine = EngineFactory.create_engine(engine_name, model_name) -134 return self._engine -135 -136 def create_context(self, token_limit: int) -> ChatContext: -137 """Create the chat context, limiting to the specified token""" -138 if self._context is None: -139 if configs.is_cache: -140 self._context = ChatContext.of(cache.read_context(), token_limit, self.max_short_memory_size) -141 else: -142 self._context = ChatContext(token_limit, self.max_short_memory_size) -143 return self._context -144 -145 def create_memory(self, memory_key: str = "chat_history") -> ConversationBufferWindowMemory: -146 """TODO""" -147 if self._memory is None: -148 self._memory = ConversationBufferWindowMemory( -149 memory_key=memory_key, k=configs.max_short_memory_size, return_messages=True -150 ) -151 return self._memory -152 -153 def input_text(self, input_prompt: str, placeholder: str | None = None) -> Optional[str]: -154 """Prompt for user input. -155 :param input_prompt: The prompt to display to the user. -156 :param placeholder: The input placeholder text. -157 """ -158 ret = None -159 while ret is None: -160 if (ret := line_input(input_prompt, placeholder)) == Keyboard.VK_CTRL_L: # Use STT as input method. -161 terminal.cursor.erase_line() -162 if spoken_text := self.engine.speech_to_text(): -163 display_text(f"{self.username}: {spoken_text}") -164 ret = spoken_text -165 -166 return ret if not ret or isinstance(ret, str) else ret.val -167 -168 -169assert (shared := SharedInstances().INSTANCE) is not None + 45 INSTANCE: "SharedInstances" + 46 + 47 # This is the uuid used in the prompts to indicate that the AI does not know the answer. + 48 UNCERTAIN_ID: str = "bde6f44d-c1a0-4b0c-bd74-8278e468e50c" + 49 + 50 def __init__(self) -> None: + 51 self._engine: AIEngine | None = None + 52 self._context: ChatContext | None = None + 53 self._memory: ConversationBufferWindowMemory | None = None + 54 self._idiom: str = configs.language.idiom + 55 self._max_short_memory_size: int = configs.max_short_memory_size + 56 self._max_iteractions: int = configs.max_iteractions + 57 + 58 @property + 59 def engine(self) -> Optional[AIEngine]: + 60 return self._engine + 61 + 62 @engine.setter + 63 def engine(self, value: AIEngine) -> None: + 64 check_state(self._engine is None, "Once set, this instance is immutable.") + 65 self._engine = value + 66 + 67 @property + 68 def context(self) -> Optional[ChatContext]: + 69 return self._context + 70 + 71 @context.setter + 72 def context(self, value: ChatContext) -> None: + 73 check_state(self._context is None, "Once set, this instance is immutable.") + 74 self._context = value + 75 + 76 @property + 77 def nickname(self) -> str: + 78 return f"%GREEN% Taius:%NC% " + 79 + 80 @property + 81 def username(self) -> str: + 82 return f"%WHITE% {prompt.user.title()}:%NC% " + 83 + 84 @property + 85 def nickname_md(self) -> str: + 86 return f"* Taius:* " + 87 + 88 @property + 89 def username_md(self) -> str: + 90 return f"** {prompt.user.title()}:** " + 91 + 92 @property + 93 def idiom(self) -> str: + 94 return self._idiom + 95 + 96 @property + 97 def memory(self) -> BaseChatMemory: + 98 return self.create_memory() + 99 +100 @property +101 def max_short_memory_size(self) -> int: +102 return self._max_short_memory_size +103 +104 @property +105 def max_iteractions(self) -> int: +106 return self._max_iteractions +107 +108 @property +109 def app_info(self) -> str: +110 device_info = f"{recorder.input_device[1]}" if recorder.input_device else "" +111 device_info += f", AUTO-SWAP {'ON' if recorder.is_auto_swap else '%RED%OFF'}" +112 dtm = f" {geo_location.datetime} " +113 speak_info = str(configs.tempo) + " @" + self.engine.configs().tts_voice +114 cur_dir = elide_text(str(Path(os.getcwd()).absolute()), 67, "…") +115 translator = f"translated by '{msg.translator.name()}'" if configs.language.name.title() != "English" else "" +116 return ( +117 f"%GREEN%" +118 f"AskAI v{Version.load(load_dir=classpath.source_path())} %EOL%" +119 f"{dtm.center(80, '=')} %EOL%" +120 f" Language: {configs.language} {translator} %EOL%" +121 f" Engine: {shared.engine} %EOL%" +122 f" Dir: {cur_dir} %EOL%" +123 f" OS: {prompt.os_type}/{prompt.shell} %EOL%" +124 f"{'-' * 80} %EOL%" +125 f" Microphone: {device_info or '%RED%Undetected'} %GREEN%%EOL%" +126 f" Debugging: {'ON' if configs.is_debug else '%RED%OFF'} %GREEN%%EOL%" +127 f" Speaking: {'ON, tempo: ' + speak_info if configs.is_speak else '%RED%OFF'} %GREEN%%EOL%" +128 f" Caching: {'ON, TTL: ' + str(configs.ttl) if configs.is_cache else '%RED%OFF'} %GREEN%%EOL%" +129 f"{'=' * 80}%EOL%%NC%" +130 ) +131 +132 def create_engine(self, engine_name: str, model_name: str) -> AIEngine: +133 """Create or retrieve an AI engine instance based on the specified engine and model names. +134 :param engine_name: The name of the AI engine to create or retrieve. +135 :param model_name: The name of the model to use with the AI engine. +136 :return: An instance of the AIEngine configured with the specified engine and model. +137 """ +138 if self._engine is None: +139 self._engine = EngineFactory.create_engine(engine_name, model_name) +140 return self._engine +141 +142 def create_context(self, token_limit: int) -> ChatContext: +143 """Create or retrieve a chat context with the specified token limit. +144 :param token_limit: The maximum number of tokens allowed in the chat context. +145 :return: An instance of the ChatContext configured with the specified token limit. +146 """ +147 if self._context is None: +148 if configs.is_cache: +149 self._context = ChatContext.of(cache.read_context(), token_limit, self.max_short_memory_size) +150 else: +151 self._context = ChatContext(token_limit, self.max_short_memory_size) +152 return self._context +153 +154 def create_memory(self, memory_key: str = "chat_history") -> BaseChatMemory: +155 """Create or retrieve the conversation window memory. +156 :param memory_key: The key used to identify the memory (default is "chat_history"). +157 :return: An instance of BaseChatMemory associated with the specified memory key. +158 """ +159 if self._memory is None: +160 self._memory = ConversationBufferWindowMemory( +161 memory_key=memory_key, k=configs.max_short_memory_size, return_messages=True +162 ) +163 return self._memory +164 +165 def input_text(self, input_prompt: str, placeholder: str | None = None) -> Optional[str]: +166 """Prompt the user for input. +167 :param input_prompt: The text prompt to display to the user. +168 :param placeholder: The placeholder text to display in the input field (optional). +169 :return: The user's input as a string, or None if no input is provided. +170 """ +171 ret = None +172 while ret is None: +173 if (ret := line_input(input_prompt, placeholder)) == Keyboard.VK_CTRL_L: # Use STT as input method. +174 terminal.cursor.erase_line() +175 if spoken_text := self.engine.speech_to_text(): +176 display_text(f"{self.username}: {spoken_text}") +177 ret = spoken_text +178 +179 return ret if not ret or isinstance(ret, str) else ret.val +180 +181 +182assert (shared := SharedInstances().INSTANCE) is not None

    @@ -305,133 +318,144 @@

    -
     41class SharedInstances(metaclass=Singleton):
    - 42    """Provides access to shared instances."""
    - 43
    - 44    INSTANCE: "SharedInstances"
    +            
     43class SharedInstances(metaclass=Singleton):
    + 44    """Provides access to shared instances."""
      45
    - 46    # This is the uuid used in the prompts to indicate that the AI does not know the answer.
    - 47    UNCERTAIN_ID: str = "bde6f44d-c1a0-4b0c-bd74-8278e468e50c"
    - 48
    - 49    def __init__(self) -> None:
    - 50        self._engine: AIEngine | None = None
    - 51        self._context: ChatContext | None = None
    - 52        self._memory: ConversationBufferWindowMemory | None = None
    - 53        self._idiom: str = configs.language.idiom
    - 54        self._max_short_memory_size: int = configs.max_short_memory_size
    - 55        self._max_iteractions: int = configs.max_iteractions
    - 56
    - 57    @property
    - 58    def engine(self) -> Optional[AIEngine]:
    - 59        return self._engine
    - 60
    - 61    @engine.setter
    - 62    def engine(self, value: AIEngine) -> None:
    - 63        check_state(self._engine is None, "Once set, this instance is immutable.")
    - 64        self._engine = value
    - 65
    - 66    @property
    - 67    def context(self) -> Optional[ChatContext]:
    - 68        return self._context
    - 69
    - 70    @context.setter
    - 71    def context(self, value: ChatContext) -> None:
    - 72        check_state(self._context is None, "Once set, this instance is immutable.")
    - 73        self._context = value
    - 74
    - 75    @property
    - 76    def nickname(self) -> str:
    - 77        return f"%WHITE%  Taius:%NC% "
    - 78
    - 79    @property
    - 80    def username(self) -> str:
    - 81        return f"%GREEN%  {prompt.user.title()}:%NC% "
    - 82
    - 83    @property
    - 84    def nickname_md(self) -> str:
    - 85        return f"*  Taius:* "
    - 86
    - 87    @property
    - 88    def username_md(self) -> str:
    - 89        return f"**  {prompt.user.title()}:** "
    - 90
    - 91    @property
    - 92    def idiom(self) -> str:
    - 93        return self._idiom
    - 94
    - 95    @property
    - 96    def memory(self) -> ConversationBufferWindowMemory:
    - 97        return self.create_memory()
    - 98
    - 99    @property
    -100    def max_short_memory_size(self) -> int:
    -101        return self._max_short_memory_size
    -102
    -103    @property
    -104    def max_iteractions(self) -> int:
    -105        return self._max_iteractions
    -106
    -107    @property
    -108    def app_info(self) -> str:
    -109        device_info = f"{recorder.input_device[1]}" if recorder.input_device else ""
    -110        device_info += f", AUTO-SWAP {'ON' if recorder.is_auto_swap else '%RED%OFF'}"
    -111        dtm = f" {geo_location.datetime} "
    -112        speak_info = str(configs.tempo) + " @" + self.engine.configs().tts_voice
    -113        cur_dir = elide_text(str(Path(os.getcwd()).absolute()), 67, "…")
    -114        translator = f"translated by '{msg.translator.name()}'" if configs.language.name.title() != "English" else ""
    -115        return (
    -116            f"%GREEN%"
    -117            f"AskAI v{Version.load(load_dir=classpath.source_path())} %EOL%"
    -118            f"{dtm.center(80, '=')} %EOL%"
    -119            f"   Language: {configs.language} {translator} %EOL%"
    -120            f"     Engine: {shared.engine} %EOL%"
    -121            f"        Dir: {cur_dir} %EOL%"
    -122            f"         OS: {prompt.os_type}/{prompt.shell} %EOL%"
    -123            f"{'-' * 80} %EOL%"
    -124            f" Microphone: {device_info or '%RED%Undetected'} %GREEN%%EOL%"
    -125            f"  Debugging: {'ON' if configs.is_debug else '%RED%OFF'} %GREEN%%EOL%"
    -126            f"   Speaking: {'ON, tempo: ' + speak_info if configs.is_speak else '%RED%OFF'} %GREEN%%EOL%"
    -127            f"    Caching: {'ON, TTL: ' + str(configs.ttl) if configs.is_cache else '%RED%OFF'} %GREEN%%EOL%"
    -128            f"{'=' * 80}%EOL%%NC%"
    -129        )
    -130
    -131    def create_engine(self, engine_name: str, model_name: str) -> AIEngine:
    -132        """Create an AI engine specified by the engine and model names."""
    -133        if self._engine is None:
    -134            self._engine = EngineFactory.create_engine(engine_name, model_name)
    -135        return self._engine
    -136
    -137    def create_context(self, token_limit: int) -> ChatContext:
    -138        """Create the chat context, limiting to the specified token"""
    -139        if self._context is None:
    -140            if configs.is_cache:
    -141                self._context = ChatContext.of(cache.read_context(), token_limit, self.max_short_memory_size)
    -142            else:
    -143                self._context = ChatContext(token_limit, self.max_short_memory_size)
    -144        return self._context
    -145
    -146    def create_memory(self, memory_key: str = "chat_history") -> ConversationBufferWindowMemory:
    -147        """TODO"""
    -148        if self._memory is None:
    -149            self._memory = ConversationBufferWindowMemory(
    -150                memory_key=memory_key, k=configs.max_short_memory_size, return_messages=True
    -151            )
    -152        return self._memory
    -153
    -154    def input_text(self, input_prompt: str, placeholder: str | None = None) -> Optional[str]:
    -155        """Prompt for user input.
    -156        :param input_prompt: The prompt to display to the user.
    -157        :param placeholder: The input placeholder text.
    -158        """
    -159        ret = None
    -160        while ret is None:
    -161            if (ret := line_input(input_prompt, placeholder)) == Keyboard.VK_CTRL_L:  # Use STT as input method.
    -162                terminal.cursor.erase_line()
    -163                if spoken_text := self.engine.speech_to_text():
    -164                    display_text(f"{self.username}: {spoken_text}")
    -165                ret = spoken_text
    -166
    -167        return ret if not ret or isinstance(ret, str) else ret.val
    + 46    INSTANCE: "SharedInstances"
    + 47
    + 48    # This is the uuid used in the prompts to indicate that the AI does not know the answer.
    + 49    UNCERTAIN_ID: str = "bde6f44d-c1a0-4b0c-bd74-8278e468e50c"
    + 50
    + 51    def __init__(self) -> None:
    + 52        self._engine: AIEngine | None = None
    + 53        self._context: ChatContext | None = None
    + 54        self._memory: ConversationBufferWindowMemory | None = None
    + 55        self._idiom: str = configs.language.idiom
    + 56        self._max_short_memory_size: int = configs.max_short_memory_size
    + 57        self._max_iteractions: int = configs.max_iteractions
    + 58
    + 59    @property
    + 60    def engine(self) -> Optional[AIEngine]:
    + 61        return self._engine
    + 62
    + 63    @engine.setter
    + 64    def engine(self, value: AIEngine) -> None:
    + 65        check_state(self._engine is None, "Once set, this instance is immutable.")
    + 66        self._engine = value
    + 67
    + 68    @property
    + 69    def context(self) -> Optional[ChatContext]:
    + 70        return self._context
    + 71
    + 72    @context.setter
    + 73    def context(self, value: ChatContext) -> None:
    + 74        check_state(self._context is None, "Once set, this instance is immutable.")
    + 75        self._context = value
    + 76
    + 77    @property
    + 78    def nickname(self) -> str:
    + 79        return f"%GREEN%  Taius:%NC% "
    + 80
    + 81    @property
    + 82    def username(self) -> str:
    + 83        return f"%WHITE%  {prompt.user.title()}:%NC% "
    + 84
    + 85    @property
    + 86    def nickname_md(self) -> str:
    + 87        return f"*  Taius:* "
    + 88
    + 89    @property
    + 90    def username_md(self) -> str:
    + 91        return f"**  {prompt.user.title()}:** "
    + 92
    + 93    @property
    + 94    def idiom(self) -> str:
    + 95        return self._idiom
    + 96
    + 97    @property
    + 98    def memory(self) -> BaseChatMemory:
    + 99        return self.create_memory()
    +100
    +101    @property
    +102    def max_short_memory_size(self) -> int:
    +103        return self._max_short_memory_size
    +104
    +105    @property
    +106    def max_iteractions(self) -> int:
    +107        return self._max_iteractions
    +108
    +109    @property
    +110    def app_info(self) -> str:
    +111        device_info = f"{recorder.input_device[1]}" if recorder.input_device else ""
    +112        device_info += f", AUTO-SWAP {'ON' if recorder.is_auto_swap else '%RED%OFF'}"
    +113        dtm = f" {geo_location.datetime} "
    +114        speak_info = str(configs.tempo) + " @" + self.engine.configs().tts_voice
    +115        cur_dir = elide_text(str(Path(os.getcwd()).absolute()), 67, "…")
    +116        translator = f"translated by '{msg.translator.name()}'" if configs.language.name.title() != "English" else ""
    +117        return (
    +118            f"%GREEN%"
    +119            f"AskAI v{Version.load(load_dir=classpath.source_path())} %EOL%"
    +120            f"{dtm.center(80, '=')} %EOL%"
    +121            f"   Language: {configs.language} {translator} %EOL%"
    +122            f"     Engine: {shared.engine} %EOL%"
    +123            f"        Dir: {cur_dir} %EOL%"
    +124            f"         OS: {prompt.os_type}/{prompt.shell} %EOL%"
    +125            f"{'-' * 80} %EOL%"
    +126            f" Microphone: {device_info or '%RED%Undetected'} %GREEN%%EOL%"
    +127            f"  Debugging: {'ON' if configs.is_debug else '%RED%OFF'} %GREEN%%EOL%"
    +128            f"   Speaking: {'ON, tempo: ' + speak_info if configs.is_speak else '%RED%OFF'} %GREEN%%EOL%"
    +129            f"    Caching: {'ON, TTL: ' + str(configs.ttl) if configs.is_cache else '%RED%OFF'} %GREEN%%EOL%"
    +130            f"{'=' * 80}%EOL%%NC%"
    +131        )
    +132
    +133    def create_engine(self, engine_name: str, model_name: str) -> AIEngine:
    +134        """Create or retrieve an AI engine instance based on the specified engine and model names.
    +135        :param engine_name: The name of the AI engine to create or retrieve.
    +136        :param model_name: The name of the model to use with the AI engine.
    +137        :return: An instance of the AIEngine configured with the specified engine and model.
    +138        """
    +139        if self._engine is None:
    +140            self._engine = EngineFactory.create_engine(engine_name, model_name)
    +141        return self._engine
    +142
    +143    def create_context(self, token_limit: int) -> ChatContext:
    +144        """Create or retrieve a chat context with the specified token limit.
    +145        :param token_limit: The maximum number of tokens allowed in the chat context.
    +146        :return: An instance of the ChatContext configured with the specified token limit.
    +147        """
    +148        if self._context is None:
    +149            if configs.is_cache:
    +150                self._context = ChatContext.of(cache.read_context(), token_limit, self.max_short_memory_size)
    +151            else:
    +152                self._context = ChatContext(token_limit, self.max_short_memory_size)
    +153        return self._context
    +154
    +155    def create_memory(self, memory_key: str = "chat_history") -> BaseChatMemory:
    +156        """Create or retrieve the conversation window memory.
    +157        :param memory_key: The key used to identify the memory (default is "chat_history").
    +158        :return: An instance of BaseChatMemory associated with the specified memory key.
    +159        """
    +160        if self._memory is None:
    +161            self._memory = ConversationBufferWindowMemory(
    +162                memory_key=memory_key, k=configs.max_short_memory_size, return_messages=True
    +163            )
    +164        return self._memory
    +165
    +166    def input_text(self, input_prompt: str, placeholder: str | None = None) -> Optional[str]:
    +167        """Prompt the user for input.
    +168        :param input_prompt: The text prompt to display to the user.
    +169        :param placeholder: The placeholder text to display in the input field (optional).
    +170        :return: The user's input as a string, or None if no input is provided.
    +171        """
    +172        ret = None
    +173        while ret is None:
    +174            if (ret := line_input(input_prompt, placeholder)) == Keyboard.VK_CTRL_L:  # Use STT as input method.
    +175                terminal.cursor.erase_line()
    +176                if spoken_text := self.engine.speech_to_text():
    +177                    display_text(f"{self.username}: {spoken_text}")
    +178                ret = spoken_text
    +179
    +180        return ret if not ret or isinstance(ret, str) else ret.val
     
    @@ -502,9 +526,9 @@

    -
    57    @property
    -58    def engine(self) -> Optional[AIEngine]:
    -59        return self._engine
    +            
    59    @property
    +60    def engine(self) -> Optional[AIEngine]:
    +61        return self._engine
     
    @@ -520,9 +544,9 @@

    -
    66    @property
    -67    def context(self) -> Optional[ChatContext]:
    -68        return self._context
    +            
    68    @property
    +69    def context(self) -> Optional[ChatContext]:
    +70        return self._context
     
    @@ -538,9 +562,9 @@

    -
    75    @property
    -76    def nickname(self) -> str:
    -77        return f"%WHITE%  Taius:%NC% "
    +            
    77    @property
    +78    def nickname(self) -> str:
    +79        return f"%GREEN%  Taius:%NC% "
     
    @@ -556,9 +580,9 @@

    -
    79    @property
    -80    def username(self) -> str:
    -81        return f"%GREEN%  {prompt.user.title()}:%NC% "
    +            
    81    @property
    +82    def username(self) -> str:
    +83        return f"%WHITE%  {prompt.user.title()}:%NC% "
     
    @@ -574,9 +598,9 @@

    -
    83    @property
    -84    def nickname_md(self) -> str:
    -85        return f"*  Taius:* "
    +            
    85    @property
    +86    def nickname_md(self) -> str:
    +87        return f"*  Taius:* "
     
    @@ -592,9 +616,9 @@

    -
    87    @property
    -88    def username_md(self) -> str:
    -89        return f"**  {prompt.user.title()}:** "
    +            
    89    @property
    +90    def username_md(self) -> str:
    +91        return f"**  {prompt.user.title()}:** "
     
    @@ -610,9 +634,9 @@

    -
    91    @property
    -92    def idiom(self) -> str:
    -93        return self._idiom
    +            
    93    @property
    +94    def idiom(self) -> str:
    +95        return self._idiom
     
    @@ -622,15 +646,15 @@

    - memory: langchain.memory.buffer_window.ConversationBufferWindowMemory + memory: langchain.memory.chat_memory.BaseChatMemory
    -
    95    @property
    -96    def memory(self) -> ConversationBufferWindowMemory:
    -97        return self.create_memory()
    +            
    97    @property
    +98    def memory(self) -> BaseChatMemory:
    +99        return self.create_memory()
     
    @@ -646,9 +670,9 @@

    -
     99    @property
    -100    def max_short_memory_size(self) -> int:
    -101        return self._max_short_memory_size
    +            
    101    @property
    +102    def max_short_memory_size(self) -> int:
    +103        return self._max_short_memory_size
     
    @@ -664,9 +688,9 @@

    -
    103    @property
    -104    def max_iteractions(self) -> int:
    -105        return self._max_iteractions
    +            
    105    @property
    +106    def max_iteractions(self) -> int:
    +107        return self._max_iteractions
     
    @@ -682,29 +706,29 @@

    -
    107    @property
    -108    def app_info(self) -> str:
    -109        device_info = f"{recorder.input_device[1]}" if recorder.input_device else ""
    -110        device_info += f", AUTO-SWAP {'ON' if recorder.is_auto_swap else '%RED%OFF'}"
    -111        dtm = f" {geo_location.datetime} "
    -112        speak_info = str(configs.tempo) + " @" + self.engine.configs().tts_voice
    -113        cur_dir = elide_text(str(Path(os.getcwd()).absolute()), 67, "…")
    -114        translator = f"translated by '{msg.translator.name()}'" if configs.language.name.title() != "English" else ""
    -115        return (
    -116            f"%GREEN%"
    -117            f"AskAI v{Version.load(load_dir=classpath.source_path())} %EOL%"
    -118            f"{dtm.center(80, '=')} %EOL%"
    -119            f"   Language: {configs.language} {translator} %EOL%"
    -120            f"     Engine: {shared.engine} %EOL%"
    -121            f"        Dir: {cur_dir} %EOL%"
    -122            f"         OS: {prompt.os_type}/{prompt.shell} %EOL%"
    -123            f"{'-' * 80} %EOL%"
    -124            f" Microphone: {device_info or '%RED%Undetected'} %GREEN%%EOL%"
    -125            f"  Debugging: {'ON' if configs.is_debug else '%RED%OFF'} %GREEN%%EOL%"
    -126            f"   Speaking: {'ON, tempo: ' + speak_info if configs.is_speak else '%RED%OFF'} %GREEN%%EOL%"
    -127            f"    Caching: {'ON, TTL: ' + str(configs.ttl) if configs.is_cache else '%RED%OFF'} %GREEN%%EOL%"
    -128            f"{'=' * 80}%EOL%%NC%"
    -129        )
    +            
    109    @property
    +110    def app_info(self) -> str:
    +111        device_info = f"{recorder.input_device[1]}" if recorder.input_device else ""
    +112        device_info += f", AUTO-SWAP {'ON' if recorder.is_auto_swap else '%RED%OFF'}"
    +113        dtm = f" {geo_location.datetime} "
    +114        speak_info = str(configs.tempo) + " @" + self.engine.configs().tts_voice
    +115        cur_dir = elide_text(str(Path(os.getcwd()).absolute()), 67, "…")
    +116        translator = f"translated by '{msg.translator.name()}'" if configs.language.name.title() != "English" else ""
    +117        return (
    +118            f"%GREEN%"
    +119            f"AskAI v{Version.load(load_dir=classpath.source_path())} %EOL%"
    +120            f"{dtm.center(80, '=')} %EOL%"
    +121            f"   Language: {configs.language} {translator} %EOL%"
    +122            f"     Engine: {shared.engine} %EOL%"
    +123            f"        Dir: {cur_dir} %EOL%"
    +124            f"         OS: {prompt.os_type}/{prompt.shell} %EOL%"
    +125            f"{'-' * 80} %EOL%"
    +126            f" Microphone: {device_info or '%RED%Undetected'} %GREEN%%EOL%"
    +127            f"  Debugging: {'ON' if configs.is_debug else '%RED%OFF'} %GREEN%%EOL%"
    +128            f"   Speaking: {'ON, tempo: ' + speak_info if configs.is_speak else '%RED%OFF'} %GREEN%%EOL%"
    +129            f"    Caching: {'ON, TTL: ' + str(configs.ttl) if configs.is_cache else '%RED%OFF'} %GREEN%%EOL%"
    +130            f"{'=' * 80}%EOL%%NC%"
    +131        )
     
    @@ -722,15 +746,32 @@

    -
    131    def create_engine(self, engine_name: str, model_name: str) -> AIEngine:
    -132        """Create an AI engine specified by the engine and model names."""
    -133        if self._engine is None:
    -134            self._engine = EngineFactory.create_engine(engine_name, model_name)
    -135        return self._engine
    +            
    133    def create_engine(self, engine_name: str, model_name: str) -> AIEngine:
    +134        """Create or retrieve an AI engine instance based on the specified engine and model names.
    +135        :param engine_name: The name of the AI engine to create or retrieve.
    +136        :param model_name: The name of the model to use with the AI engine.
    +137        :return: An instance of the AIEngine configured with the specified engine and model.
    +138        """
    +139        if self._engine is None:
    +140            self._engine = EngineFactory.create_engine(engine_name, model_name)
    +141        return self._engine
     
    -

    Create an AI engine specified by the engine and model names.

    +

    Create or retrieve an AI engine instance based on the specified engine and model names.

    + +
    Parameters
    + +
      +
    • engine_name: The name of the AI engine to create or retrieve.
    • +
    • model_name: The name of the model to use with the AI engine.
    • +
    + +
    Returns
    + +
    +

    An instance of the AIEngine configured with the specified engine and model.

    +
    @@ -746,18 +787,33 @@

    -
    137    def create_context(self, token_limit: int) -> ChatContext:
    -138        """Create the chat context, limiting to the specified token"""
    -139        if self._context is None:
    -140            if configs.is_cache:
    -141                self._context = ChatContext.of(cache.read_context(), token_limit, self.max_short_memory_size)
    -142            else:
    -143                self._context = ChatContext(token_limit, self.max_short_memory_size)
    -144        return self._context
    +            
    143    def create_context(self, token_limit: int) -> ChatContext:
    +144        """Create or retrieve a chat context with the specified token limit.
    +145        :param token_limit: The maximum number of tokens allowed in the chat context.
    +146        :return: An instance of the ChatContext configured with the specified token limit.
    +147        """
    +148        if self._context is None:
    +149            if configs.is_cache:
    +150                self._context = ChatContext.of(cache.read_context(), token_limit, self.max_short_memory_size)
    +151            else:
    +152                self._context = ChatContext(token_limit, self.max_short_memory_size)
    +153        return self._context
     
    -

    Create the chat context, limiting to the specified token

    +

    Create or retrieve a chat context with the specified token limit.

    + +
    Parameters
    + +
      +
    • token_limit: The maximum number of tokens allowed in the chat context.
    • +
    + +
    Returns
    + +
    +

    An instance of the ChatContext configured with the specified token limit.

    +
    @@ -767,23 +823,38 @@

    def - create_memory( self, memory_key: str = 'chat_history') -> langchain.memory.buffer_window.ConversationBufferWindowMemory: + create_memory( self, memory_key: str = 'chat_history') -> langchain.memory.chat_memory.BaseChatMemory:
    -
    146    def create_memory(self, memory_key: str = "chat_history") -> ConversationBufferWindowMemory:
    -147        """TODO"""
    -148        if self._memory is None:
    -149            self._memory = ConversationBufferWindowMemory(
    -150                memory_key=memory_key, k=configs.max_short_memory_size, return_messages=True
    -151            )
    -152        return self._memory
    +            
    155    def create_memory(self, memory_key: str = "chat_history") -> BaseChatMemory:
    +156        """Create or retrieve the conversation window memory.
    +157        :param memory_key: The key used to identify the memory (default is "chat_history").
    +158        :return: An instance of BaseChatMemory associated with the specified memory key.
    +159        """
    +160        if self._memory is None:
    +161            self._memory = ConversationBufferWindowMemory(
    +162                memory_key=memory_key, k=configs.max_short_memory_size, return_messages=True
    +163            )
    +164        return self._memory
     
    -

    TODO

    +

    Create or retrieve the conversation window memory.

    + +
    Parameters
    + +
      +
    • memory_key: The key used to identify the memory (default is "chat_history").
    • +
    + +
    Returns
    + +
    +

    An instance of BaseChatMemory associated with the specified memory key.

    +
    @@ -799,31 +870,38 @@

    -
    154    def input_text(self, input_prompt: str, placeholder: str | None = None) -> Optional[str]:
    -155        """Prompt for user input.
    -156        :param input_prompt: The prompt to display to the user.
    -157        :param placeholder: The input placeholder text.
    -158        """
    -159        ret = None
    -160        while ret is None:
    -161            if (ret := line_input(input_prompt, placeholder)) == Keyboard.VK_CTRL_L:  # Use STT as input method.
    -162                terminal.cursor.erase_line()
    -163                if spoken_text := self.engine.speech_to_text():
    -164                    display_text(f"{self.username}: {spoken_text}")
    -165                ret = spoken_text
    -166
    -167        return ret if not ret or isinstance(ret, str) else ret.val
    +            
    166    def input_text(self, input_prompt: str, placeholder: str | None = None) -> Optional[str]:
    +167        """Prompt the user for input.
    +168        :param input_prompt: The text prompt to display to the user.
    +169        :param placeholder: The placeholder text to display in the input field (optional).
    +170        :return: The user's input as a string, or None if no input is provided.
    +171        """
    +172        ret = None
    +173        while ret is None:
    +174            if (ret := line_input(input_prompt, placeholder)) == Keyboard.VK_CTRL_L:  # Use STT as input method.
    +175                terminal.cursor.erase_line()
    +176                if spoken_text := self.engine.speech_to_text():
    +177                    display_text(f"{self.username}: {spoken_text}")
    +178                ret = spoken_text
    +179
    +180        return ret if not ret or isinstance(ret, str) else ret.val
     
    -

    Prompt for user input.

    +

    Prompt the user for input.

    Parameters
      -
    • input_prompt: The prompt to display to the user.
    • -
    • placeholder: The input placeholder text.
    • +
    • input_prompt: The text prompt to display to the user.
    • +
    • placeholder: The placeholder text to display in the input field (optional).
    + +
    Returns
    + +
    +

    The user's input as a string, or None if no input is provided.

    +
    diff --git a/docs/api/askai/main/askai/core/support/text_formatter.html b/docs/api/askai/main/askai/core/support/text_formatter.html index e55960c8..c9b4469b 100644 --- a/docs/api/askai/main/askai/core/support/text_formatter.html +++ b/docs/api/askai/main/askai/core/support/text_formatter.html @@ -3,7 +3,7 @@ - + main.askai.core.support.text_formatter API documentation @@ -54,9 +54,6 @@

    API Documentation

  • ensure_ln
  • -
  • - console -
  • beautify
  • @@ -69,6 +66,12 @@

    API Documentation

  • cmd_print
  • +
  • + remove_markdown +
  • +
  • + strip_format +
  • @@ -115,121 +118,143 @@

    10 Copyright (c) 2024, HomeSetup 11""" 12 - 13from hspylib.core.metaclass.singleton import Singleton - 14from hspylib.core.tools.text_tools import ensure_endswith, ensure_startswith - 15from hspylib.modules.cli.vt100.vt_code import VtCode - 16from hspylib.modules.cli.vt100.vt_color import VtColor - 17from rich.console import Console - 18from rich.markdown import Markdown - 19from rich.text import Text - 20from textwrap import dedent - 21from typing import Any - 22 - 23import os - 24import re - 25 - 26 - 27class TextFormatter(metaclass=Singleton): - 28 """TODO""" - 29 - 30 INSTANCE: "TextFormatter" + 13from clitt.core.term.cursor import cursor + 14from hspylib.core.metaclass.singleton import Singleton + 15from hspylib.core.tools.text_tools import ensure_endswith, ensure_startswith, strip_escapes + 16from hspylib.modules.cli.vt100.vt_code import VtCode + 17from hspylib.modules.cli.vt100.vt_color import VtColor + 18from textwrap import dedent + 19from typing import Any, AnyStr + 20 + 21import os + 22import re + 23 + 24 + 25class TextFormatter(metaclass=Singleton): + 26 """A utility class for formatting text according to specified rules or styles. + 27 This class provides various methods for transforming and formatting text, + 28 such as adjusting indentation, line breaks, or applying specific text styles. + 29 The Singleton metaclass ensures that only one instance of this class exists throughout the application. + 30 """ 31 - 32 RE_URL = ( - 33 r"(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s')]{2,}|" - 34 r"www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s')]{2,}|https?:\/\/(?:www\.|(?!www))" - 35 r"[a-zA-Z0-9]+\.[^\s')]{2,}|www\.[a-zA-Z0-9]+\.[^\s')]{2,})" - 36 ) - 37 - 38 RE_MD_CODE_BLOCK = r"(```.+```)" + 32 INSTANCE: "TextFormatter" + 33 + 34 RE_URL = ( + 35 r"(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s')]{2,}|" + 36 r"www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s')]{2,}|https?:\/\/(?:www\.|(?!www))" + 37 r"[a-zA-Z0-9]+\.[^\s')]{2,}|www\.[a-zA-Z0-9]+\.[^\s')]{2,})" + 38 ) 39 - 40 CHAT_ICONS = { - 41 "": " Oops!\n>  An Exception Occurred: \n#### ", - 42 "": "\n>  *Tip:* ", - 43 "": "\n>  *Analysis:* ", - 44 "": "\n>  *Summary:* ", - 45 "": "\n>  *Joke:* ", - 46 "": "\n>  *Fun-Fact:* ", - 47 "": "\n>  *Advice:* ", - 48 "﬽": "\n> ﬽ *Conclusion:* ", - 49 } - 50 - 51 RE_TYPES = { - 52 "MD": RE_MD_CODE_BLOCK, - 53 "": RE_URL, - 54 "": r"[\s*_]*Errors?[_*-:\s]+", - 55 "": r"[\s*_]*Hints?( ([Aa]nd|&) [Tt]ips?)?[_*-:\s]+", - 56 "": r"[\s*_]*Analysis[_*-:\s]+", - 57 "": r"[\s*_]*Summary[_*-:\s]+", - 58 "": r"[\s*_]*Fun[\s-]+[Ff]acts?[_*-:\s]+", - 59 "": r"[\s*_]*(Jokes?(\s+[Tt]ime)?)[_*-:\s]+", - 60 "": r"[\s*_]*Advice[_*-:\s]+", - 61 "﬽": r"[\s*_]*Conclusion[_*-:\s]+", - 62 } - 63 - 64 @staticmethod - 65 def ensure_ln(text: str) -> str: - 66 """Ensure text starts and ends with a lien separator. - 67 :param text: The text to be formatted. - 68 """ - 69 return ensure_endswith(ensure_startswith(text, os.linesep), os.linesep * 2) - 70 - 71 def __init__(self): - 72 self._console: Console = Console() - 73 - 74 @property - 75 def console(self) -> Console: - 76 return self._console - 77 - 78 def beautify(self, text: Any) -> str: - 79 """Beautify the provided text with icons and other formatting improvements. - 80 :param text: The text to be beautified. - 81 """ - 82 # fmt: off - 83 - 84 text = dedent(str(text)) - 85 text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text) - 86 text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text) - 87 text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text) - 88 text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text) - 89 text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text) - 90 text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text) - 91 text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text) - 92 text = re.sub(self.RE_TYPES['﬽'], self.CHAT_ICONS['﬽'], text) - 93 # Improve links - 94 text = re.sub(self.RE_TYPES[''], r" [\1](\1)", text) - 95 # Make sure markdown is prefixed and suffixed with new lines - 96 text = re.sub(self.RE_TYPES['MD'], r"\n\1\n", text) - 97 - 98 # fmt: on - 99 -100 return text -101 -102 def display_markdown(self, text: str) -> None: -103 """Display a markdown formatted text. -104 :param text: The text to be displayed. -105 """ -106 colorized: str = VtColor.colorize(VtCode.decode(self.beautify(text))) -107 self.console.print(Markdown(colorized)) -108 -109 def display_text(self, text: str) -> None: -110 """Display a vt100 formatted text. -111 :param text: The text to be displayed. -112 """ -113 colorized: str = VtColor.colorize(VtCode.decode(self.beautify(text))) -114 self.console.print(Text.from_ansi(colorized)) -115 -116 def cmd_print(self, text: str): -117 """Display an AskAI commander text. -118 :param text: The text to be displayed. -119 """ -120 self.display_markdown(f"%ORANGE% Commander%NC%: {self.beautify(text)}") -121 -122 -123assert (text_formatter := TextFormatter().INSTANCE) is not None -124 -125 -126if __name__ == "__main__": -127 text_formatter.display_markdown(" Oops!\n>  An Exception Occurred: \n#### &nbsp;") + 40 RE_MD_CODE_BLOCK = r"(```.+```)" + 41 + 42 CHAT_ICONS = { + 43 "": " Oops!\n>  An Exception Occurred: \n#### ", + 44 "": "\n>  *Tip:* ", + 45 "": "\n>  *Analysis:* ", + 46 "": "\n>  *Summary:* ", + 47 "": "\n>  *Joke:* ", + 48 "": "\n>  *Fun-Fact:* ", + 49 "": "\n>  *Advice:* ", + 50 "﬽": "\n> ﬽ *Conclusion:* ", + 51 } + 52 + 53 RE_TYPES = { + 54 "MD": RE_MD_CODE_BLOCK, + 55 "": RE_URL, + 56 "": r"[\s*_]*Errors?[_*-:\s]+", + 57 "": r"[\s*_]*Hints?( ([Aa]nd|&) [Tt]ips?)?[_*-:\s]+", + 58 "": r"[\s*_]*Analysis[_*-:\s]+", + 59 "": r"[\s*_]*Summary[_*-:\s]+", + 60 "": r"[\s*_]*Fun[\s-]+[Ff]acts?[_*-:\s]+", + 61 "": r"[\s*_]*(Jokes?(\s+[Tt]ime)?)[_*-:\s]+", + 62 "": r"[\s*_]*Advice[_*-:\s]+", + 63 "﬽": r"[\s*_]*Conclusion[_*-:\s]+", + 64 } + 65 + 66 @staticmethod + 67 def ensure_ln(text: str, separator: str = os.linesep) -> str: + 68 """Ensure the text starts and ends with a line separator. + 69 :param text: The text to be formatted. + 70 :param separator: The line separator to use (default is the system's line separator). + 71 :return: The formatted text with the specified line separator at the beginning and end. + 72 """ + 73 return ensure_endswith(ensure_startswith(text, separator), separator * 2) + 74 + 75 def beautify(self, text: Any) -> str: + 76 """Beautify the provided text with icons and other formatting enhancements. + 77 :param text: The text to be beautified. + 78 :return: The beautified text as a string with applied icons and formatting improvements. + 79 """ + 80 # fmt: off + 81 + 82 text = dedent(str(text)) + 83 text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text) + 84 text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text) + 85 text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text) + 86 text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text) + 87 text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text) + 88 text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text) + 89 text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text) + 90 text = re.sub(self.RE_TYPES['﬽'], self.CHAT_ICONS['﬽'], text) + 91 # Improve links + 92 text = re.sub(self.RE_TYPES[''], r" [\1](\1)", text) + 93 # Make sure markdown is prefixed and suffixed with new lines + 94 text = re.sub(self.RE_TYPES['MD'], r"\n\1\n", text) + 95 text = re.sub(r'```(.+)```\s+', r"\n```\1```\n", text) + 96 + 97 # fmt: on + 98 + 99 return text +100 +101 def display_markdown(self, text: AnyStr) -> None: +102 """Display a markdown-formatted text. +103 :param text: The markdown-formatted text to be displayed. +104 """ +105 colorized: str = VtColor.colorize(VtCode.decode(self.beautify(str(text)))) +106 cursor.write(colorized, markdown=True) +107 +108 def display_text(self, text: AnyStr) -> None: +109 """Display a VT100 formatted text. +110 :param text: The VT100 formatted text to be displayed. +111 """ +112 colorized: str = VtColor.colorize(VtCode.decode(self.beautify(str(text)))) +113 cursor.write(colorized) +114 +115 def cmd_print(self, text: AnyStr): +116 """Display an AskAI commander formatted text. +117 :param text: The text to be displayed. +118 """ +119 self.display_markdown(f"%ORANGE% Commander%NC%: {self.beautify(str(text))}") +120 +121 def remove_markdown(self, text: AnyStr) -> str: +122 """ +123 Remove Markdown formatting from a string. +124 """ +125 plain_text = re.sub(r"```(.*?)```", r"\1", str(text), flags=re.DOTALL) +126 plain_text = re.sub(r"`[^`]+`", "", plain_text) +127 plain_text = re.sub(r"^(#+\s+)", "", plain_text) +128 plain_text = re.sub(r"\*\*([^*]+)\*\*", r"\1", plain_text) +129 plain_text = re.sub(r"\*([^*]+)\*", r"\1", plain_text) +130 plain_text = re.sub(r"__([^_]+)__", r"\1", plain_text) +131 plain_text = re.sub(r"_([^_]+)_", r"\1", plain_text) +132 plain_text = re.sub(r"\[([^]]+)]\([^)]+\)", r"\1", plain_text) +133 plain_text = re.sub(r"!\[([^]]*)]\([^)]+\)", r"\1", plain_text) +134 plain_text = re.sub(r"---|___|\*\*\*", "", plain_text) +135 plain_text = re.sub(r">\s+", "", plain_text) +136 plain_text = re.sub(r"[-*+]\s+", "", plain_text) +137 plain_text = re.sub(r"^\d+\.\s+", "", plain_text) +138 +139 return plain_text.strip() +140 +141 def strip_format(self, text: AnyStr) -> str: +142 """Remove the markdown code block formatting from the text. +143 :param text: The text containing the markdown code block formatting. +144 :return: The text with the markdown code block formatting stripped away. +145 """ +146 return strip_escapes(self.remove_markdown(text)) +147 +148 +149assert (text_formatter := TextFormatter().INSTANCE) is not None

    @@ -245,104 +270,135 @@

    -
     28class TextFormatter(metaclass=Singleton):
    - 29    """TODO"""
    - 30
    - 31    INSTANCE: "TextFormatter"
    +            
     26class TextFormatter(metaclass=Singleton):
    + 27    """A utility class for formatting text according to specified rules or styles.
    + 28    This class provides various methods for transforming and formatting text,
    + 29    such as adjusting indentation, line breaks, or applying specific text styles.
    + 30    The Singleton metaclass ensures that only one instance of this class exists throughout the application.
    + 31    """
      32
    - 33    RE_URL = (
    - 34        r"(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s')]{2,}|"
    - 35        r"www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s')]{2,}|https?:\/\/(?:www\.|(?!www))"
    - 36        r"[a-zA-Z0-9]+\.[^\s')]{2,}|www\.[a-zA-Z0-9]+\.[^\s')]{2,})"
    - 37    )
    - 38
    - 39    RE_MD_CODE_BLOCK = r"(```.+```)"
    + 33    INSTANCE: "TextFormatter"
    + 34
    + 35    RE_URL = (
    + 36        r"(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s')]{2,}|"
    + 37        r"www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s')]{2,}|https?:\/\/(?:www\.|(?!www))"
    + 38        r"[a-zA-Z0-9]+\.[^\s')]{2,}|www\.[a-zA-Z0-9]+\.[^\s')]{2,})"
    + 39    )
      40
    - 41    CHAT_ICONS = {
    - 42        "": " Oops!\n>  An Exception Occurred: \n####  ",
    - 43        "": "\n>   *Tip:* ",
    - 44        "": "\n>   *Analysis:* ",
    - 45        "": "\n>   *Summary:* ",
    - 46        "": "\n>   *Joke:* ",
    - 47        "": "\n>   *Fun-Fact:* ",
    - 48        "": "\n>   *Advice:* ",
    - 49        "﬽": "\n> ﬽  *Conclusion:* ",
    - 50    }
    - 51
    - 52    RE_TYPES = {
    - 53        "MD": RE_MD_CODE_BLOCK,
    - 54        "": RE_URL,
    - 55        "": r"[\s*_]*Errors?[_*-:\s]+",
    - 56        "": r"[\s*_]*Hints?( ([Aa]nd|&) [Tt]ips?)?[_*-:\s]+",
    - 57        "": r"[\s*_]*Analysis[_*-:\s]+",
    - 58        "": r"[\s*_]*Summary[_*-:\s]+",
    - 59        "": r"[\s*_]*Fun[\s-]+[Ff]acts?[_*-:\s]+",
    - 60        "": r"[\s*_]*(Jokes?(\s+[Tt]ime)?)[_*-:\s]+",
    - 61        "": r"[\s*_]*Advice[_*-:\s]+",
    - 62        "﬽": r"[\s*_]*Conclusion[_*-:\s]+",
    - 63    }
    - 64
    - 65    @staticmethod
    - 66    def ensure_ln(text: str) -> str:
    - 67        """Ensure text starts and ends with a lien separator.
    - 68        :param text: The text to be formatted.
    - 69        """
    - 70        return ensure_endswith(ensure_startswith(text, os.linesep), os.linesep * 2)
    - 71
    - 72    def __init__(self):
    - 73        self._console: Console = Console()
    - 74
    - 75    @property
    - 76    def console(self) -> Console:
    - 77        return self._console
    - 78
    - 79    def beautify(self, text: Any) -> str:
    - 80        """Beautify the provided text with icons and other formatting improvements.
    - 81        :param text: The text to be beautified.
    - 82        """
    - 83        # fmt: off
    - 84
    - 85        text = dedent(str(text))
    - 86        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    - 87        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    - 88        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    - 89        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    - 90        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    - 91        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    - 92        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    - 93        text = re.sub(self.RE_TYPES['﬽'], self.CHAT_ICONS['﬽'], text)
    - 94        # Improve links
    - 95        text = re.sub(self.RE_TYPES[''], r" [\1](\1)", text)
    - 96        # Make sure markdown is prefixed and suffixed with new lines
    - 97        text = re.sub(self.RE_TYPES['MD'], r"\n\1\n", text)
    - 98
    - 99        # fmt: on
    -100
    -101        return text
    -102
    -103    def display_markdown(self, text: str) -> None:
    -104        """Display a markdown formatted text.
    -105        :param text: The text to be displayed.
    -106        """
    -107        colorized: str = VtColor.colorize(VtCode.decode(self.beautify(text)))
    -108        self.console.print(Markdown(colorized))
    -109
    -110    def display_text(self, text: str) -> None:
    -111        """Display a vt100 formatted text.
    -112        :param text: The text to be displayed.
    -113        """
    -114        colorized: str = VtColor.colorize(VtCode.decode(self.beautify(text)))
    -115        self.console.print(Text.from_ansi(colorized))
    -116
    -117    def cmd_print(self, text: str):
    -118        """Display an AskAI commander text.
    -119        :param text: The text to be displayed.
    -120        """
    -121        self.display_markdown(f"%ORANGE%  Commander%NC%: {self.beautify(text)}")
    + 41    RE_MD_CODE_BLOCK = r"(```.+```)"
    + 42
    + 43    CHAT_ICONS = {
    + 44        "": " Oops!\n>  An Exception Occurred: \n####  ",
    + 45        "": "\n>   *Tip:* ",
    + 46        "": "\n>   *Analysis:* ",
    + 47        "": "\n>   *Summary:* ",
    + 48        "": "\n>   *Joke:* ",
    + 49        "": "\n>   *Fun-Fact:* ",
    + 50        "": "\n>   *Advice:* ",
    + 51        "﬽": "\n> ﬽  *Conclusion:* ",
    + 52    }
    + 53
    + 54    RE_TYPES = {
    + 55        "MD": RE_MD_CODE_BLOCK,
    + 56        "": RE_URL,
    + 57        "": r"[\s*_]*Errors?[_*-:\s]+",
    + 58        "": r"[\s*_]*Hints?( ([Aa]nd|&) [Tt]ips?)?[_*-:\s]+",
    + 59        "": r"[\s*_]*Analysis[_*-:\s]+",
    + 60        "": r"[\s*_]*Summary[_*-:\s]+",
    + 61        "": r"[\s*_]*Fun[\s-]+[Ff]acts?[_*-:\s]+",
    + 62        "": r"[\s*_]*(Jokes?(\s+[Tt]ime)?)[_*-:\s]+",
    + 63        "": r"[\s*_]*Advice[_*-:\s]+",
    + 64        "﬽": r"[\s*_]*Conclusion[_*-:\s]+",
    + 65    }
    + 66
    + 67    @staticmethod
    + 68    def ensure_ln(text: str, separator: str = os.linesep) -> str:
    + 69        """Ensure the text starts and ends with a line separator.
    + 70        :param text: The text to be formatted.
    + 71        :param separator: The line separator to use (default is the system's line separator).
    + 72        :return: The formatted text with the specified line separator at the beginning and end.
    + 73        """
    + 74        return ensure_endswith(ensure_startswith(text, separator), separator * 2)
    + 75
    + 76    def beautify(self, text: Any) -> str:
    + 77        """Beautify the provided text with icons and other formatting enhancements.
    + 78        :param text: The text to be beautified.
    + 79        :return: The beautified text as a string with applied icons and formatting improvements.
    + 80        """
    + 81        # fmt: off
    + 82
    + 83        text = dedent(str(text))
    + 84        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    + 85        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    + 86        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    + 87        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    + 88        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    + 89        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    + 90        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    + 91        text = re.sub(self.RE_TYPES['﬽'], self.CHAT_ICONS['﬽'], text)
    + 92        # Improve links
    + 93        text = re.sub(self.RE_TYPES[''], r" [\1](\1)", text)
    + 94        # Make sure markdown is prefixed and suffixed with new lines
    + 95        text = re.sub(self.RE_TYPES['MD'], r"\n\1\n", text)
    + 96        text = re.sub(r'```(.+)```\s+', r"\n```\1```\n", text)
    + 97
    + 98        # fmt: on
    + 99
    +100        return text
    +101
    +102    def display_markdown(self, text: AnyStr) -> None:
    +103        """Display a markdown-formatted text.
    +104        :param text: The markdown-formatted text to be displayed.
    +105        """
    +106        colorized: str = VtColor.colorize(VtCode.decode(self.beautify(str(text))))
    +107        cursor.write(colorized, markdown=True)
    +108
    +109    def display_text(self, text: AnyStr) -> None:
    +110        """Display a VT100 formatted text.
    +111        :param text: The VT100 formatted text to be displayed.
    +112        """
    +113        colorized: str = VtColor.colorize(VtCode.decode(self.beautify(str(text))))
    +114        cursor.write(colorized)
    +115
    +116    def cmd_print(self, text: AnyStr):
    +117        """Display an AskAI commander formatted text.
    +118        :param text: The text to be displayed.
    +119        """
    +120        self.display_markdown(f"%ORANGE%  Commander%NC%: {self.beautify(str(text))}")
    +121
    +122    def remove_markdown(self, text: AnyStr) -> str:
    +123        """
    +124        Remove Markdown formatting from a string.
    +125        """
    +126        plain_text = re.sub(r"```(.*?)```", r"\1", str(text), flags=re.DOTALL)
    +127        plain_text = re.sub(r"`[^`]+`", "", plain_text)
    +128        plain_text = re.sub(r"^(#+\s+)", "", plain_text)
    +129        plain_text = re.sub(r"\*\*([^*]+)\*\*", r"\1", plain_text)
    +130        plain_text = re.sub(r"\*([^*]+)\*", r"\1", plain_text)
    +131        plain_text = re.sub(r"__([^_]+)__", r"\1", plain_text)
    +132        plain_text = re.sub(r"_([^_]+)_", r"\1", plain_text)
    +133        plain_text = re.sub(r"\[([^]]+)]\([^)]+\)", r"\1", plain_text)
    +134        plain_text = re.sub(r"!\[([^]]*)]\([^)]+\)", r"\1", plain_text)
    +135        plain_text = re.sub(r"---|___|\*\*\*", "", plain_text)
    +136        plain_text = re.sub(r">\s+", "", plain_text)
    +137        plain_text = re.sub(r"[-*+]\s+", "", plain_text)
    +138        plain_text = re.sub(r"^\d+\.\s+", "", plain_text)
    +139
    +140        return plain_text.strip()
    +141
    +142    def strip_format(self, text: AnyStr) -> str:
    +143        """Remove the markdown code block formatting from the text.
    +144        :param text: The text containing the markdown code block formatting.
    +145        :return: The text with the markdown code block formatting stripped away.
    +146        """
    +147        return strip_escapes(self.remove_markdown(text))
     
    -

    TODO

    +

    A utility class for formatting text according to specified rules or styles. +This class provides various methods for transforming and formatting text, +such as adjusting indentation, line breaks, or applying specific text styles. +The Singleton metaclass ensures that only one instance of this class exists throughout the application.

    @@ -445,48 +501,39 @@

    @staticmethod
    def - ensure_ln(text: str) -> str: + ensure_ln(text: str, separator: str = '\n') -> str:

    -
    65    @staticmethod
    -66    def ensure_ln(text: str) -> str:
    -67        """Ensure text starts and ends with a lien separator.
    -68        :param text: The text to be formatted.
    -69        """
    -70        return ensure_endswith(ensure_startswith(text, os.linesep), os.linesep * 2)
    +            
    67    @staticmethod
    +68    def ensure_ln(text: str, separator: str = os.linesep) -> str:
    +69        """Ensure the text starts and ends with a line separator.
    +70        :param text: The text to be formatted.
    +71        :param separator: The line separator to use (default is the system's line separator).
    +72        :return: The formatted text with the specified line separator at the beginning and end.
    +73        """
    +74        return ensure_endswith(ensure_startswith(text, separator), separator * 2)
     
    -

    Ensure text starts and ends with a lien separator.

    +

    Ensure the text starts and ends with a line separator.

    Parameters
    • text: The text to be formatted.
    • +
    • separator: The line separator to use (default is the system's line separator).
    -
    +
    Returns
    -
    -
    - -
    - console: rich.console.Console - - - -
    - -
    75    @property
    -76    def console(self) -> Console:
    -77        return self._console
    -
    - +
    +

    The formatted text with the specified line separator at the beginning and end.

    +
    +
    -
    @@ -500,39 +547,47 @@
    Parameters
    -
     79    def beautify(self, text: Any) -> str:
    - 80        """Beautify the provided text with icons and other formatting improvements.
    - 81        :param text: The text to be beautified.
    - 82        """
    - 83        # fmt: off
    - 84
    - 85        text = dedent(str(text))
    - 86        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    - 87        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    - 88        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    - 89        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    - 90        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    - 91        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    - 92        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    - 93        text = re.sub(self.RE_TYPES['﬽'], self.CHAT_ICONS['﬽'], text)
    - 94        # Improve links
    - 95        text = re.sub(self.RE_TYPES[''], r" [\1](\1)", text)
    - 96        # Make sure markdown is prefixed and suffixed with new lines
    - 97        text = re.sub(self.RE_TYPES['MD'], r"\n\1\n", text)
    - 98
    - 99        # fmt: on
    -100
    -101        return text
    +            
     76    def beautify(self, text: Any) -> str:
    + 77        """Beautify the provided text with icons and other formatting enhancements.
    + 78        :param text: The text to be beautified.
    + 79        :return: The beautified text as a string with applied icons and formatting improvements.
    + 80        """
    + 81        # fmt: off
    + 82
    + 83        text = dedent(str(text))
    + 84        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    + 85        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    + 86        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    + 87        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    + 88        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    + 89        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    + 90        text = re.sub(self.RE_TYPES[''], self.CHAT_ICONS[''], text)
    + 91        text = re.sub(self.RE_TYPES['﬽'], self.CHAT_ICONS['﬽'], text)
    + 92        # Improve links
    + 93        text = re.sub(self.RE_TYPES[''], r" [\1](\1)", text)
    + 94        # Make sure markdown is prefixed and suffixed with new lines
    + 95        text = re.sub(self.RE_TYPES['MD'], r"\n\1\n", text)
    + 96        text = re.sub(r'```(.+)```\s+', r"\n```\1```\n", text)
    + 97
    + 98        # fmt: on
    + 99
    +100        return text
     
    -

    Beautify the provided text with icons and other formatting improvements.

    +

    Beautify the provided text with icons and other formatting enhancements.

    Parameters
    • text: The text to be beautified.
    + +
    Returns
    + +
    +

    The beautified text as a string with applied icons and formatting improvements.

    +
    @@ -542,27 +597,27 @@
    Parameters
    def - display_markdown(self, text: str) -> None: + display_markdown(self, text: ~AnyStr) -> None:
    -
    103    def display_markdown(self, text: str) -> None:
    -104        """Display a markdown formatted text.
    -105        :param text: The text to be displayed.
    -106        """
    -107        colorized: str = VtColor.colorize(VtCode.decode(self.beautify(text)))
    -108        self.console.print(Markdown(colorized))
    +            
    102    def display_markdown(self, text: AnyStr) -> None:
    +103        """Display a markdown-formatted text.
    +104        :param text: The markdown-formatted text to be displayed.
    +105        """
    +106        colorized: str = VtColor.colorize(VtCode.decode(self.beautify(str(text))))
    +107        cursor.write(colorized, markdown=True)
     
    -

    Display a markdown formatted text.

    +

    Display a markdown-formatted text.

    Parameters
      -
    • text: The text to be displayed.
    • +
    • text: The markdown-formatted text to be displayed.
    @@ -573,27 +628,27 @@
    Parameters
    def - display_text(self, text: str) -> None: + display_text(self, text: ~AnyStr) -> None:
    -
    110    def display_text(self, text: str) -> None:
    -111        """Display a vt100 formatted text.
    -112        :param text: The text to be displayed.
    -113        """
    -114        colorized: str = VtColor.colorize(VtCode.decode(self.beautify(text)))
    -115        self.console.print(Text.from_ansi(colorized))
    +            
    109    def display_text(self, text: AnyStr) -> None:
    +110        """Display a VT100 formatted text.
    +111        :param text: The VT100 formatted text to be displayed.
    +112        """
    +113        colorized: str = VtColor.colorize(VtCode.decode(self.beautify(str(text))))
    +114        cursor.write(colorized)
     
    -

    Display a vt100 formatted text.

    +

    Display a VT100 formatted text.

    Parameters
      -
    • text: The text to be displayed.
    • +
    • text: The VT100 formatted text to be displayed.
    @@ -604,21 +659,21 @@
    Parameters
    def - cmd_print(self, text: str): + cmd_print(self, text: ~AnyStr):
    -
    117    def cmd_print(self, text: str):
    -118        """Display an AskAI commander text.
    -119        :param text: The text to be displayed.
    -120        """
    -121        self.display_markdown(f"%ORANGE%  Commander%NC%: {self.beautify(text)}")
    +            
    116    def cmd_print(self, text: AnyStr):
    +117        """Display an AskAI commander formatted text.
    +118        :param text: The text to be displayed.
    +119        """
    +120        self.display_markdown(f"%ORANGE%  Commander%NC%: {self.beautify(str(text))}")
     
    -

    Display an AskAI commander text.

    +

    Display an AskAI commander formatted text.

    Parameters
    @@ -628,6 +683,81 @@
    Parameters
    +
    +
    + +
    + + def + remove_markdown(self, text: ~AnyStr) -> str: + + + +
    + +
    122    def remove_markdown(self, text: AnyStr) -> str:
    +123        """
    +124        Remove Markdown formatting from a string.
    +125        """
    +126        plain_text = re.sub(r"```(.*?)```", r"\1", str(text), flags=re.DOTALL)
    +127        plain_text = re.sub(r"`[^`]+`", "", plain_text)
    +128        plain_text = re.sub(r"^(#+\s+)", "", plain_text)
    +129        plain_text = re.sub(r"\*\*([^*]+)\*\*", r"\1", plain_text)
    +130        plain_text = re.sub(r"\*([^*]+)\*", r"\1", plain_text)
    +131        plain_text = re.sub(r"__([^_]+)__", r"\1", plain_text)
    +132        plain_text = re.sub(r"_([^_]+)_", r"\1", plain_text)
    +133        plain_text = re.sub(r"\[([^]]+)]\([^)]+\)", r"\1", plain_text)
    +134        plain_text = re.sub(r"!\[([^]]*)]\([^)]+\)", r"\1", plain_text)
    +135        plain_text = re.sub(r"---|___|\*\*\*", "", plain_text)
    +136        plain_text = re.sub(r">\s+", "", plain_text)
    +137        plain_text = re.sub(r"[-*+]\s+", "", plain_text)
    +138        plain_text = re.sub(r"^\d+\.\s+", "", plain_text)
    +139
    +140        return plain_text.strip()
    +
    + + +

    Remove Markdown formatting from a string.

    +
    + + +
    +
    + +
    + + def + strip_format(self, text: ~AnyStr) -> str: + + + +
    + +
    142    def strip_format(self, text: AnyStr) -> str:
    +143        """Remove the markdown code block formatting from the text.
    +144        :param text: The text containing the markdown code block formatting.
    +145        :return: The text with the markdown code block formatting stripped away.
    +146        """
    +147        return strip_escapes(self.remove_markdown(text))
    +
    + + +

    Remove the markdown code block formatting from the text.

    + +
    Parameters
    + +
      +
    • text: The text containing the markdown code block formatting.
    • +
    + +
    Returns
    + +
    +

    The text with the markdown code block formatting stripped away.

    +
    +
    + +
    diff --git a/docs/api/askai/main/askai/core/support/utilities.html b/docs/api/askai/main/askai/core/support/utilities.html index f613019a..6840caff 100644 --- a/docs/api/askai/main/askai/core/support/utilities.html +++ b/docs/api/askai/main/askai/core/support/utilities.html @@ -3,7 +3,7 @@ - + main.askai.core.support.utilities API documentation @@ -36,9 +36,6 @@

    API Documentation

  • display_text
  • -
  • - stream_text -
  • find_file
  • @@ -48,9 +45,18 @@

    API Documentation

  • build_img_path
  • +
  • + join_path +
  • read_resource
  • +
  • + read_file +
  • +
  • + encode_image +
  • extract_path
  • @@ -63,9 +69,6 @@

    API Documentation

  • seconds
  • -
  • - ensure_ln -
  • @@ -111,42 +114,42 @@

    12 13 Copyright (c) 2024, HomeSetup 14""" - 15from askai.core.support.presets import Presets - 16from askai.core.support.text_formatter import text_formatter - 17from askai.language.language import Language - 18from clitt.core.term.cursor import Cursor - 19from hspylib.core.config.path_object import PathObject - 20from hspylib.core.enums.charset import Charset - 21from hspylib.core.preconditions import check_argument - 22from hspylib.core.tools.commons import file_is_not_empty, sysout - 23from hspylib.core.tools.text_tools import ensure_endswith, ensure_startswith, strip_escapes - 24from hspylib.core.zoned_datetime import now_ms - 25from hspylib.modules.cli.vt100.vt_color import VtColor - 26from os.path import basename, dirname - 27from pathlib import Path - 28from typing import Any, Optional, Tuple - 29 - 30import mimetypes - 31import os - 32import pause - 33import re - 34import shutil - 35import sys - 36 - 37 - 38def read_stdin() -> Optional[str]: - 39 """TODO""" + 15from askai.core.support.text_formatter import text_formatter + 16from clitt.core.term.cursor import Cursor + 17from hspylib.core.config.path_object import PathObject + 18from hspylib.core.enums.charset import Charset + 19from hspylib.core.metaclass.classpath import AnyPath + 20from hspylib.core.preconditions import check_argument + 21from hspylib.core.tools.commons import file_is_not_empty + 22from hspylib.core.tools.text_tools import ensure_endswith, strip_escapes + 23from hspylib.core.zoned_datetime import now_ms + 24from os.path import basename, dirname + 25from pathlib import Path + 26from typing import AnyStr, Optional + 27 + 28import base64 + 29import mimetypes + 30import os + 31import re + 32import shutil + 33import sys + 34 + 35 + 36def read_stdin() -> Optional[str]: + 37 """Read input from the standard input (stdin). + 38 :return: The input read from stdin as a string, or None if no input is provided. + 39 """ 40 if not sys.stdin.isatty(): 41 return sys.stdin.read() 42 return None 43 44 - 45def display_text(text: Any, prefix: Any = "", markdown: bool = True, erase_last=False) -> None: - 46 """Display the provided text ina proper way. + 45def display_text(text: AnyStr, prefix: AnyStr = "", markdown: bool = True, erase_last=False) -> None: + 46 """Display the provided text in a formatted way. 47 :param text: The text to be displayed. - 48 :param prefix: the text prefix. - 49 :param markdown: Whether to enable markdown rendering. - 50 :param erase_last: Whether to erase the last displayed line. + 48 :param prefix: A prefix to prepend to the text (optional). + 49 :param markdown: Whether to render the text using markdown formatting (default is True). + 50 :param erase_last: Whether to erase the last displayed line before displaying the new text (default is False). 51 """ 52 if erase_last: 53 Cursor.INSTANCE.erase_line() @@ -156,172 +159,141 @@

    57 text_formatter.display_text(f"{str(prefix)}{text}") 58 59 - 60def stream_text(text: Any, prefix: Any = "", tempo: int = 1, language: Language = Language.EN_US) -> None: - 61 """Stream the text on the screen. Simulates a typewriter effect. The following presets were - 62 benchmarked according to the selected language. - 63 :param text: the text to stream. - 64 :param prefix: the streaming prefix. - 65 :param tempo: the speed multiplier of the typewriter effect. Defaults to 1. - 66 :param language: the language used to stream the text. Defaults to en_US. - 67 """ - 68 text: str = text_formatter.beautify(text) - 69 presets: Presets = Presets.get(language.language, tempo=tempo) - 70 word_count: int = 0 - 71 ln: str = os.linesep - 72 hide: bool = False - 73 idx: int = 0 + 60def find_file(filename: AnyPath) -> Optional[Path]: + 61 """Find the specified file by name in the most common locations. + 62 :param filename: The name or path of the file to find. + 63 :return: The full path to the file if found, otherwise None. + 64 """ + 65 prompt_path: Path = Path(filename) if filename else None + 66 if prompt_path and not prompt_path.exists(): + 67 prompt_path = Path(os.path.expandvars(os.path.expanduser(filename))) + 68 if not prompt_path.exists(): + 69 prompt_path = Path(os.path.join(os.getcwd(), filename)) + 70 if not prompt_path.exists(): + 71 prompt_path = Path(os.path.join(Path.home(), filename)) + 72 return prompt_path if prompt_path and prompt_path.exists() else None + 73 74 - 75 # The following algorithm was created based on the whisper voice. - 76 sysout(f"{str(prefix)}", end="") - 77 for i, char in enumerate(text): - 78 if char == "%" and (i + 1) < len(text): - 79 try: - 80 if (color := text[i + 1 : text.index("%", i + 1)]) in VtColor.names(): - 81 hide, idx = True, text.index("%", i + 1) - 82 sysout(f"%{color}%", end="") - 83 continue - 84 except ValueError: - 85 pass # this means that this '%' is not a VtColor specification - 86 if hide and idx is not None and i <= idx: - 87 continue - 88 sysout(char, end="") - 89 if char.isalpha(): - 90 pause.seconds(presets.base_speed) - 91 elif char.isnumeric(): - 92 pause.seconds( - 93 presets.breath_interval if i + 1 < len(text) and text[i + 1] == "." else presets.number_interval - 94 ) - 95 elif char.isspace(): - 96 if i - 1 >= 0 and not text[i - 1].isspace(): - 97 word_count += 1 - 98 pause.seconds( - 99 presets.breath_interval if word_count % presets.words_per_breath == 0 else presets.words_interval -100 ) -101 elif i - 1 >= 0 and not text[i - 1] in [".", "?", "!"]: -102 word_count += 1 -103 pause.seconds( -104 presets.period_interval if word_count % presets.words_per_breath == 0 else presets.punct_interval -105 ) -106 elif char == "/": -107 pause.seconds( -108 presets.base_speed if i + 1 < len(text) and text[i + 1].isnumeric() else presets.punct_interval -109 ) -110 elif char == ln: -111 pause.seconds( -112 presets.period_interval if i + 1 < len(text) and text[i + 1] == ln else presets.punct_interval -113 ) -114 word_count = 0 -115 elif char in [":", "-"]: -116 pause.seconds( -117 presets.enum_interval -118 if i + 1 < len(text) and (text[i + 1].isnumeric() or text[i + 1] in [" ", ln, "-"]) -119 else presets.base_speed -120 ) -121 elif char in [",", ";"]: -122 pause.seconds(presets.comma_interval if i + 1 < len(text) and text[i + 1].isspace() else presets.base_speed) -123 elif char in [".", "?", "!", ln]: -124 pause.seconds(presets.punct_interval) -125 word_count = 0 -126 pause.seconds(presets.base_speed) -127 sysout("%NC%") -128 -129 -130def find_file(filename: str | None) -> Optional[Path]: -131 """Find the specified file, specified by name, from the most common locations.""" -132 prompt_path: Path = Path(filename) if filename else None -133 if prompt_path and not prompt_path.exists(): -134 prompt_path = Path(os.path.expandvars(os.path.expanduser(filename))) -135 if not prompt_path.exists(): -136 prompt_path = Path(os.path.join(os.getcwd(), filename)) -137 if not prompt_path.exists(): -138 prompt_path = Path(os.path.join(Path.home(), filename)) -139 return prompt_path if prompt_path and prompt_path.exists() else None -140 -141 -142def copy_file(filename: str | Path, destfile: str | Path) -> str: -143 """Copy the specified file to another folder path. -144 :param filename: The file name to be moved. -145 :param destfile: The destination path to move to. -146 """ -147 filepath: PathObject = PathObject.of(filename) -148 dest_dir: PathObject = PathObject.of(destfile) -149 check_argument(filepath.exists and filepath.is_file and dest_dir.exists and dest_dir.is_dir) -150 dest_file: str = os.path.join(dest_dir.abs_dir, filepath.filename) -151 shutil.copy(filename, dest_file) -152 return dest_file -153 -154 -155def build_img_path(base_dir: Path, filename: str, suffix: str) -> Optional[str]: -156 """TODO""" -157 if not filename: -158 return None -159 img_path: str = str(Path.joinpath(base_dir, basename(filename or f"ASKAI-{now_ms()}"))).strip() -160 img_path = re.sub(r'\s+', '-', ensure_endswith(img_path, suffix)) -161 img_path = re.sub(r'-+', '-', img_path) -162 return strip_escapes(img_path) -163 -164 -165def read_resource(base_dir: str, filename: str, file_ext: str = ".txt") -> str: -166 """Read the prompt template specified by the filename. -167 :param base_dir: The base directory, relative to the resources folder. -168 :param filename: The filename of the prompt. -169 :param file_ext: The file extension of. -170 """ -171 filename = f"{base_dir}/{ensure_endswith(basename(filename), file_ext)}" -172 check_argument(file_is_not_empty(filename), f"Resource file is empty does not exist: {filename}") -173 return Path(filename).read_text(encoding=Charset.UTF_8.val) -174 + 75def copy_file(srcfile: AnyPath | Path, destfile: AnyPath | Path) -> str: + 76 """Copy the specified file to the given destination path. + 77 :param srcfile: The path of the source file to be copied. + 78 :param destfile: The destination path where the file should be copied. + 79 :return: The path of the copied file as a string. + 80 """ + 81 filepath: PathObject = PathObject.of(srcfile) + 82 dest_dir: PathObject = PathObject.of(destfile) + 83 check_argument(filepath.exists and filepath.is_file and dest_dir.exists and dest_dir.is_dir) + 84 dest_file: str = os.path.join(dest_dir.abs_dir, filepath.filename) + 85 shutil.copy(srcfile, dest_file) + 86 return dest_file + 87 + 88 + 89def build_img_path(base_dir: Path, filename: str, suffix: str) -> Optional[str]: + 90 """Construct the full path for an image file based on the base directory, filename, and suffix. + 91 :param base_dir: The base directory where the image file is located. + 92 :param filename: The name of the image file (without the suffix). + 93 :param suffix: The suffix or extension to append to the filename (e.g., ".jpg", ".png"). + 94 :return: The full path to the image file as a string, or None if the path cannot be constructed. + 95 """ + 96 if not filename: + 97 return None + 98 img_path: str = str(Path.joinpath(base_dir, basename(filename or f"ASKAI-{now_ms()}"))).strip() + 99 img_path = re.sub(r"\s+", "-", ensure_endswith(img_path, suffix)) +100 img_path = re.sub(r"-+", "-", img_path) +101 return strip_escapes(img_path) +102 +103 +104def join_path(dir_name: AnyPath, path_name: AnyPath) -> str: +105 """Join the the given path name with the specified directory name. +106 :param dir_name: The new directory name to replace the existing one. +107 :param path_name: The original path where the directory will be replaced. +108 :return: The modified path with the new directory name. +109 """ +110 +111 return os.path.join(str(dir_name), str(path_name)) +112 +113 +114def read_resource(base_dir: AnyPath, filename: AnyPath, file_ext: str = ".txt") -> str: +115 """Read the resource file specified by the filename and return its content. +116 :param base_dir: The base directory, relative to the resources folder. +117 :param filename: The name of the file to read. +118 :param file_ext: The file extension of the file (default is ".txt"). +119 :return: The content of the file as a string. +120 """ +121 _, ext = os.path.splitext(filename) +122 filename = f"{str(base_dir)}/{ensure_endswith(basename(str(filename)), ext or file_ext)}" +123 check_argument(file_is_not_empty(str(filename)), f"Resource file is empty or does not exist: {filename}") +124 return Path(filename).read_text(encoding=Charset.UTF_8.val) +125 +126 +127def read_file(load_dir: AnyPath, path_name: str) -> Optional[str]: +128 """Reads the contents of a file from the specified directory. +129 :param load_dir: The directory where the file is located. +130 :param path_name: The path name of the file to read. +131 :return: The contents of the file as a string, or None if the file cannot be read. +132 """ +133 file: Path = find_file(Path(join_path(load_dir, path_name))) +134 return file.read_text(encoding=Charset.UTF_8.val) if file_is_not_empty(str(file)) else "" +135 +136 +137def encode_image(file_path: str): +138 """Encode an image file to a base64 string. +139 :param file_path: Path to the image file to be encoded. +140 :return: Base64 encoded string of the image file. +141 """ +142 with open(file_path, "rb") as f_image: +143 return base64.b64encode(f_image.read()).decode(Charset.UTF_8.val) +144 +145 +146def extract_path(command_line: str, flags: int = re.IGNORECASE | re.MULTILINE) -> Optional[str]: +147 """Extract the first identifiable path from the provided command line text. +148 :param command_line: The command line text from which to extract the path. +149 :param flags: Regex match flags to control the extraction process (default is re.IGNORECASE | re.MULTILINE). +150 :return: The first identified path as a string, or None if no path could be extracted. +151 """ +152 command_line = re.sub("([12&]>|2>&1|1>&2).+", "", command_line.split("|")[0]) +153 re_path = r'(?:\w)\s+(?:-[\w\d]+\s)*(?:([\/\w\d\s"-]+)|(".*?"))' +154 if command_line and (cmd_path := re.search(re_path, command_line, flags)): +155 if (extracted := cmd_path.group(1).strip().replace("\\ ", " ")) and (_path_ := Path(extracted)).exists(): +156 if _path_.is_dir() or (extracted := dirname(extracted)): +157 return extracted if extracted and Path(extracted).is_dir() else None +158 return None +159 +160 +161def extract_codeblock(text: str, flags: int = re.IGNORECASE | re.MULTILINE) -> tuple[Optional[str], str]: +162 """Extract the programming language and actual code from a markdown multi-line code block. +163 :param text: The markdown-formatted text containing the code block. +164 :param flags: Regex match flags to control the extraction process (default is re.IGNORECASE | re.MULTILINE). +165 :return: A tuple where the first element is the programming language (or None if not specified), +166 and the second element is the extracted code as a string. +167 """ +168 # Match a terminal command formatted in a markdown code block. +169 re_command = r".*```((?:\w+)?\s*)\n(.*?)(?=^```)\s*\n?```.*" +170 if text and (mat := re.search(re_command, text, flags=flags)): +171 if mat and len(mat.groups()) == 2: +172 lang, code = mat.group(1) or "", mat.group(2) or "" +173 return lang, code +174 return None, text 175 -176def extract_path(command_line: str, flags: int = re.IGNORECASE | re.MULTILINE) -> Optional[str]: -177 """Extract the first identifiable path of the executed command line. -178 :param command_line: The command line text. -179 :param flags: Regex match flags. -180 """ -181 command_line = re.sub("([12&]>|2>&1|1>&2).+", "", command_line.split("|")[0]) -182 re_path = r'(?:\w)\s+(?:-[\w\d]+\s)*(?:([\/\w\d\s"-]+)|(".*?"))' -183 if command_line and (cmd_path := re.search(re_path, command_line, flags)): -184 if (extracted := cmd_path.group(1).strip().replace("\\ ", " ")) and (_path_ := Path(extracted)).exists(): -185 if _path_.is_dir() or (extracted := dirname(extracted)): -186 return extracted if extracted and Path(extracted).is_dir() else None -187 return None +176 +177def media_type_of(pathname: str) -> Optional[Optional[tuple[str, ...]]]: +178 """Return the media type of the specified file, or None if the type could not be determined. +179 :param pathname: The file path to check. +180 :return: A tuple representing the media type (e.g., ("image", "jpeg")), or None if guessing was not possible. +181 """ +182 mimetypes.init() +183 mtype, _ = mimetypes.guess_type(basename(pathname)) +184 if mtype is not None: +185 return tuple(mtype.split("/")) +186 return None +187 188 -189 -190def extract_codeblock(text: str) -> Tuple[Optional[str], str]: -191 """Extract language and actual code from a markdown multi-line code block. -192 :param text: The markdown formatted text. +189def seconds(millis: int) -> float: +190 """Convert milliseconds to seconds. +191 :param millis: The time in milliseconds. +192 :return: The equivalent time in seconds as a float. 193 """ -194 # Match a terminal command formatted in a markdown code block. -195 re_command = r".*```((?:\w+)?\s*)\n(.*?)(?=^```)\s*\n?```.*" -196 if text and (mat := re.search(re_command, text, flags=re.DOTALL | re.MULTILINE)): -197 if mat and len(mat.groups()) == 2: -198 lang, code = mat.group(1) or "", mat.group(2) or "" -199 return lang.strip(), code.strip() -200 -201 return None, text -202 -203 -204def media_type_of(pathname: str) -> Optional[tuple[str, ...]] | None: -205 """Return the file media type, or none is guessing was not possible. -206 :param pathname: The file path to check. -207 """ -208 mimetypes.init() -209 mtype, _ = mimetypes.guess_type(basename(pathname)) -210 -211 if mtype is not None: -212 return tuple(mtype.split("/")) -213 -214 return None -215 -216 -217def seconds(millis: int) -> float: -218 """Return the amount of second from milliseconds.""" -219 return millis / 1000 -220 -221 -222def ensure_ln(text: str) -> str: -223 """Ensure text starts and ends with new line.""" -224 ln: str = os.linesep -225 return ensure_endswith(ensure_startswith(text, ln), ln) +194 return millis / 1000

    @@ -337,15 +309,23 @@

    -
    39def read_stdin() -> Optional[str]:
    -40    """TODO"""
    +            
    37def read_stdin() -> Optional[str]:
    +38    """Read input from the standard input (stdin).
    +39    :return: The input read from stdin as a string, or None if no input is provided.
    +40    """
     41    if not sys.stdin.isatty():
     42        return sys.stdin.read()
     43    return None
     
    -

    TODO

    +

    Read input from the standard input (stdin).

    + +
    Returns
    + +
    +

    The input read from stdin as a string, or None if no input is provided.

    +
    @@ -355,18 +335,18 @@

    def - display_text( text: Any, prefix: Any = '', markdown: bool = True, erase_last=False) -> None: + display_text( text: ~AnyStr, prefix: ~AnyStr = '', markdown: bool = True, erase_last=False) -> None:
    -
    46def display_text(text: Any, prefix: Any = "", markdown: bool = True, erase_last=False) -> None:
    -47    """Display the provided text ina proper way.
    +            
    46def display_text(text: AnyStr, prefix: AnyStr = "", markdown: bool = True, erase_last=False) -> None:
    +47    """Display the provided text in a formatted way.
     48    :param text: The text to be displayed.
    -49    :param prefix: the text prefix.
    -50    :param markdown: Whether to enable markdown rendering.
    -51    :param erase_last: Whether to erase the last displayed line.
    +49    :param prefix: A prefix to prepend to the text (optional).
    +50    :param markdown: Whether to render the text using markdown formatting (default is True).
    +51    :param erase_last: Whether to erase the last displayed line before displaying the new text (default is False).
     52    """
     53    if erase_last:
     54        Cursor.INSTANCE.erase_line()
    @@ -377,206 +357,190 @@ 

    -

    Display the provided text ina proper way.

    +

    Display the provided text in a formatted way.

    Parameters
    • text: The text to be displayed.
    • -
    • prefix: the text prefix.
    • -
    • markdown: Whether to enable markdown rendering.
    • -
    • erase_last: Whether to erase the last displayed line.
    • +
    • prefix: A prefix to prepend to the text (optional).
    • +
    • markdown: Whether to render the text using markdown formatting (default is True).
    • +
    • erase_last: Whether to erase the last displayed line before displaying the new text (default is False).
    -
    - +
    +
    def - stream_text( text: Any, prefix: Any = '', tempo: int = 1, language: askai.language.language.Language = English) -> None: + find_file(filename: Union[pathlib.Path, str, NoneType]) -> Optional[pathlib.Path]: - +
    - -
     61def stream_text(text: Any, prefix: Any = "", tempo: int = 1, language: Language = Language.EN_US) -> None:
    - 62    """Stream the text on the screen. Simulates a typewriter effect. The following presets were
    - 63    benchmarked according to the selected language.
    - 64    :param text: the text to stream.
    - 65    :param prefix: the streaming prefix.
    - 66    :param tempo: the speed multiplier of the typewriter effect. Defaults to 1.
    - 67    :param language: the language used to stream the text. Defaults to en_US.
    - 68    """
    - 69    text: str = text_formatter.beautify(text)
    - 70    presets: Presets = Presets.get(language.language, tempo=tempo)
    - 71    word_count: int = 0
    - 72    ln: str = os.linesep
    - 73    hide: bool = False
    - 74    idx: int = 0
    - 75
    - 76    # The following algorithm was created based on the whisper voice.
    - 77    sysout(f"{str(prefix)}", end="")
    - 78    for i, char in enumerate(text):
    - 79        if char == "%" and (i + 1) < len(text):
    - 80            try:
    - 81                if (color := text[i + 1 : text.index("%", i + 1)]) in VtColor.names():
    - 82                    hide, idx = True, text.index("%", i + 1)
    - 83                    sysout(f"%{color}%", end="")
    - 84                    continue
    - 85            except ValueError:
    - 86                pass  # this means that this '%' is not a VtColor specification
    - 87        if hide and idx is not None and i <= idx:
    - 88            continue
    - 89        sysout(char, end="")
    - 90        if char.isalpha():
    - 91            pause.seconds(presets.base_speed)
    - 92        elif char.isnumeric():
    - 93            pause.seconds(
    - 94                presets.breath_interval if i + 1 < len(text) and text[i + 1] == "." else presets.number_interval
    - 95            )
    - 96        elif char.isspace():
    - 97            if i - 1 >= 0 and not text[i - 1].isspace():
    - 98                word_count += 1
    - 99                pause.seconds(
    -100                    presets.breath_interval if word_count % presets.words_per_breath == 0 else presets.words_interval
    -101                )
    -102            elif i - 1 >= 0 and not text[i - 1] in [".", "?", "!"]:
    -103                word_count += 1
    -104                pause.seconds(
    -105                    presets.period_interval if word_count % presets.words_per_breath == 0 else presets.punct_interval
    -106                )
    -107        elif char == "/":
    -108            pause.seconds(
    -109                presets.base_speed if i + 1 < len(text) and text[i + 1].isnumeric() else presets.punct_interval
    -110            )
    -111        elif char == ln:
    -112            pause.seconds(
    -113                presets.period_interval if i + 1 < len(text) and text[i + 1] == ln else presets.punct_interval
    -114            )
    -115            word_count = 0
    -116        elif char in [":", "-"]:
    -117            pause.seconds(
    -118                presets.enum_interval
    -119                if i + 1 < len(text) and (text[i + 1].isnumeric() or text[i + 1] in [" ", ln, "-"])
    -120                else presets.base_speed
    -121            )
    -122        elif char in [",", ";"]:
    -123            pause.seconds(presets.comma_interval if i + 1 < len(text) and text[i + 1].isspace() else presets.base_speed)
    -124        elif char in [".", "?", "!", ln]:
    -125            pause.seconds(presets.punct_interval)
    -126            word_count = 0
    -127        pause.seconds(presets.base_speed)
    -128    sysout("%NC%")
    +    
    +            
    61def find_file(filename: AnyPath) -> Optional[Path]:
    +62    """Find the specified file by name in the most common locations.
    +63    :param filename: The name or path of the file to find.
    +64    :return: The full path to the file if found, otherwise None.
    +65    """
    +66    prompt_path: Path = Path(filename) if filename else None
    +67    if prompt_path and not prompt_path.exists():
    +68        prompt_path = Path(os.path.expandvars(os.path.expanduser(filename)))
    +69        if not prompt_path.exists():
    +70            prompt_path = Path(os.path.join(os.getcwd(), filename))
    +71            if not prompt_path.exists():
    +72                prompt_path = Path(os.path.join(Path.home(), filename))
    +73    return prompt_path if prompt_path and prompt_path.exists() else None
     
    -

    Stream the text on the screen. Simulates a typewriter effect. The following presets were -benchmarked according to the selected language.

    +

    Find the specified file by name in the most common locations.

    Parameters
      -
    • text: the text to stream.
    • -
    • prefix: the streaming prefix.
    • -
    • tempo: the speed multiplier of the typewriter effect. Defaults to 1.
    • -
    • language: the language used to stream the text. Defaults to en_US.
    • +
    • filename: The name or path of the file to find.
    + +
    Returns
    + +
    +

    The full path to the file if found, otherwise None.

    +
    -
    - +
    +
    def - find_file(filename: str | None) -> Optional[pathlib.Path]: + copy_file( srcfile: Union[pathlib.Path, str, NoneType], destfile: Union[pathlib.Path, str, NoneType]) -> str: - +
    - -
    131def find_file(filename: str | None) -> Optional[Path]:
    -132    """Find the specified file, specified by name, from the most common locations."""
    -133    prompt_path: Path = Path(filename) if filename else None
    -134    if prompt_path and not prompt_path.exists():
    -135        prompt_path = Path(os.path.expandvars(os.path.expanduser(filename)))
    -136        if not prompt_path.exists():
    -137            prompt_path = Path(os.path.join(os.getcwd(), filename))
    -138            if not prompt_path.exists():
    -139                prompt_path = Path(os.path.join(Path.home(), filename))
    -140    return prompt_path if prompt_path and prompt_path.exists() else None
    +    
    +            
    76def copy_file(srcfile: AnyPath | Path, destfile: AnyPath | Path) -> str:
    +77    """Copy the specified file to the given destination path.
    +78    :param srcfile: The path of the source file to be copied.
    +79    :param destfile: The destination path where the file should be copied.
    +80    :return: The path of the copied file as a string.
    +81    """
    +82    filepath: PathObject = PathObject.of(srcfile)
    +83    dest_dir: PathObject = PathObject.of(destfile)
    +84    check_argument(filepath.exists and filepath.is_file and dest_dir.exists and dest_dir.is_dir)
    +85    dest_file: str = os.path.join(dest_dir.abs_dir, filepath.filename)
    +86    shutil.copy(srcfile, dest_file)
    +87    return dest_file
     
    -

    Find the specified file, specified by name, from the most common locations.

    +

    Copy the specified file to the given destination path.

    + +
    Parameters
    + +
      +
    • srcfile: The path of the source file to be copied.
    • +
    • destfile: The destination path where the file should be copied.
    • +
    + +
    Returns
    + +
    +

    The path of the copied file as a string.

    +
    -
    - +
    +
    def - copy_file(filename: str | pathlib.Path, destfile: str | pathlib.Path) -> str: + build_img_path(base_dir: pathlib.Path, filename: str, suffix: str) -> Optional[str]: - +
    - -
    143def copy_file(filename: str | Path, destfile: str | Path) -> str:
    -144    """Copy the specified file to another folder path.
    -145    :param filename: The file name to be moved.
    -146    :param destfile: The destination path to move to.
    -147    """
    -148    filepath: PathObject = PathObject.of(filename)
    -149    dest_dir: PathObject = PathObject.of(destfile)
    -150    check_argument(filepath.exists and filepath.is_file and dest_dir.exists and dest_dir.is_dir)
    -151    dest_file: str = os.path.join(dest_dir.abs_dir, filepath.filename)
    -152    shutil.copy(filename, dest_file)
    -153    return dest_file
    +    
    +            
     90def build_img_path(base_dir: Path, filename: str, suffix: str) -> Optional[str]:
    + 91    """Construct the full path for an image file based on the base directory, filename, and suffix.
    + 92    :param base_dir: The base directory where the image file is located.
    + 93    :param filename: The name of the image file (without the suffix).
    + 94    :param suffix: The suffix or extension to append to the filename (e.g., ".jpg", ".png").
    + 95    :return: The full path to the image file as a string, or None if the path cannot be constructed.
    + 96    """
    + 97    if not filename:
    + 98        return None
    + 99    img_path: str = str(Path.joinpath(base_dir, basename(filename or f"ASKAI-{now_ms()}"))).strip()
    +100    img_path = re.sub(r"\s+", "-", ensure_endswith(img_path, suffix))
    +101    img_path = re.sub(r"-+", "-", img_path)
    +102    return strip_escapes(img_path)
     
    -

    Copy the specified file to another folder path.

    +

    Construct the full path for an image file based on the base directory, filename, and suffix.

    Parameters
      -
    • filename: The file name to be moved.
    • -
    • destfile: The destination path to move to.
    • +
    • base_dir: The base directory where the image file is located.
    • +
    • filename: The name of the image file (without the suffix).
    • +
    • suffix: The suffix or extension to append to the filename (e.g., ".jpg", ".png").
    + +
    Returns
    + +
    +

    The full path to the image file as a string, or None if the path cannot be constructed.

    +
    -
    - +
    +
    def - build_img_path(base_dir: pathlib.Path, filename: str, suffix: str) -> Optional[str]: + join_path( dir_name: Union[pathlib.Path, str, NoneType], path_name: Union[pathlib.Path, str, NoneType]) -> str: - +
    - -
    156def build_img_path(base_dir: Path, filename: str, suffix: str) -> Optional[str]:
    -157    """TODO"""
    -158    if not filename:
    -159        return None
    -160    img_path: str = str(Path.joinpath(base_dir, basename(filename or f"ASKAI-{now_ms()}"))).strip()
    -161    img_path = re.sub(r'\s+', '-', ensure_endswith(img_path, suffix))
    -162    img_path = re.sub(r'-+', '-', img_path)
    -163    return strip_escapes(img_path)
    +    
    +            
    105def join_path(dir_name: AnyPath, path_name: AnyPath) -> str:
    +106    """Join the the given path name with the specified directory name.
    +107    :param dir_name: The new directory name to replace the existing one.
    +108    :param path_name: The original path where the directory will be replaced.
    +109    :return: The modified path with the new directory name.
    +110    """
    +111
    +112    return os.path.join(str(dir_name), str(path_name))
     
    -

    TODO

    +

    Join the the given path name with the specified directory name.

    + +
    Parameters
    + +
      +
    • dir_name: The new directory name to replace the existing one.
    • +
    • path_name: The original path where the directory will be replaced.
    • +
    + +
    Returns
    + +
    +

    The modified path with the new directory name.

    +
    @@ -586,33 +550,119 @@
    Parameters
    def - read_resource(base_dir: str, filename: str, file_ext: str = '.txt') -> str: + read_resource( base_dir: Union[pathlib.Path, str, NoneType], filename: Union[pathlib.Path, str, NoneType], file_ext: str = '.txt') -> str:
    -
    166def read_resource(base_dir: str, filename: str, file_ext: str = ".txt") -> str:
    -167    """Read the prompt template specified by the filename.
    -168    :param base_dir: The base directory, relative to the resources folder.
    -169    :param filename: The filename of the prompt.
    -170    :param file_ext: The file extension of.
    -171    """
    -172    filename = f"{base_dir}/{ensure_endswith(basename(filename), file_ext)}"
    -173    check_argument(file_is_not_empty(filename), f"Resource file is empty does not exist: {filename}")
    -174    return Path(filename).read_text(encoding=Charset.UTF_8.val)
    +            
    115def read_resource(base_dir: AnyPath, filename: AnyPath, file_ext: str = ".txt") -> str:
    +116    """Read the resource file specified by the filename and return its content.
    +117    :param base_dir: The base directory, relative to the resources folder.
    +118    :param filename: The name of the file to read.
    +119    :param file_ext: The file extension of the file (default is ".txt").
    +120    :return: The content of the file as a string.
    +121    """
    +122    _, ext = os.path.splitext(filename)
    +123    filename = f"{str(base_dir)}/{ensure_endswith(basename(str(filename)), ext or file_ext)}"
    +124    check_argument(file_is_not_empty(str(filename)), f"Resource file is empty or does not exist: {filename}")
    +125    return Path(filename).read_text(encoding=Charset.UTF_8.val)
     
    -

    Read the prompt template specified by the filename.

    +

    Read the resource file specified by the filename and return its content.

    Parameters
    • base_dir: The base directory, relative to the resources folder.
    • -
    • filename: The filename of the prompt.
    • -
    • file_ext: The file extension of.
    • +
    • filename: The name of the file to read.
    • +
    • file_ext: The file extension of the file (default is ".txt").
    • +
    + +
    Returns
    + +
    +

    The content of the file as a string.

    +
    +
    + + +
    +
    + +
    + + def + read_file( load_dir: Union[pathlib.Path, str, NoneType], path_name: str) -> Optional[str]: + + + +
    + +
    128def read_file(load_dir: AnyPath, path_name: str) -> Optional[str]:
    +129    """Reads the contents of a file from the specified directory.
    +130    :param load_dir: The directory where the file is located.
    +131    :param path_name: The path name of the file to read.
    +132    :return: The contents of the file as a string, or None if the file cannot be read.
    +133    """
    +134    file: Path = find_file(Path(join_path(load_dir, path_name)))
    +135    return file.read_text(encoding=Charset.UTF_8.val) if file_is_not_empty(str(file)) else ""
    +
    + + +

    Reads the contents of a file from the specified directory.

    + +
    Parameters
    + +
      +
    • load_dir: The directory where the file is located.
    • +
    • path_name: The path name of the file to read.
    • +
    + +
    Returns
    + +
    +

    The contents of the file as a string, or None if the file cannot be read.

    +
    +
    + + +
    +
    + +
    + + def + encode_image(file_path: str): + + + +
    + +
    138def encode_image(file_path: str):
    +139    """Encode an image file to a base64 string.
    +140    :param file_path: Path to the image file to be encoded.
    +141    :return: Base64 encoded string of the image file.
    +142    """
    +143    with open(file_path, "rb") as f_image:
    +144        return base64.b64encode(f_image.read()).decode(Charset.UTF_8.val)
    +
    + + +

    Encode an image file to a base64 string.

    + +
    Parameters
    + +
      +
    • file_path: Path to the image file to be encoded.
    + +
    Returns
    + +
    +

    Base64 encoded string of the image file.

    +
    @@ -628,29 +678,36 @@
    Parameters
    -
    177def extract_path(command_line: str, flags: int = re.IGNORECASE | re.MULTILINE) -> Optional[str]:
    -178    """Extract the first identifiable path of the executed command line.
    -179    :param command_line: The command line text.
    -180    :param flags: Regex match flags.
    -181    """
    -182    command_line = re.sub("([12&]>|2>&1|1>&2).+", "", command_line.split("|")[0])
    -183    re_path = r'(?:\w)\s+(?:-[\w\d]+\s)*(?:([\/\w\d\s"-]+)|(".*?"))'
    -184    if command_line and (cmd_path := re.search(re_path, command_line, flags)):
    -185        if (extracted := cmd_path.group(1).strip().replace("\\ ", " ")) and (_path_ := Path(extracted)).exists():
    -186            if _path_.is_dir() or (extracted := dirname(extracted)):
    -187                return extracted if extracted and Path(extracted).is_dir() else None
    -188    return None
    +            
    147def extract_path(command_line: str, flags: int = re.IGNORECASE | re.MULTILINE) -> Optional[str]:
    +148    """Extract the first identifiable path from the provided command line text.
    +149    :param command_line: The command line text from which to extract the path.
    +150    :param flags: Regex match flags to control the extraction process (default is re.IGNORECASE | re.MULTILINE).
    +151    :return: The first identified path as a string, or None if no path could be extracted.
    +152    """
    +153    command_line = re.sub("([12&]>|2>&1|1>&2).+", "", command_line.split("|")[0])
    +154    re_path = r'(?:\w)\s+(?:-[\w\d]+\s)*(?:([\/\w\d\s"-]+)|(".*?"))'
    +155    if command_line and (cmd_path := re.search(re_path, command_line, flags)):
    +156        if (extracted := cmd_path.group(1).strip().replace("\\ ", " ")) and (_path_ := Path(extracted)).exists():
    +157            if _path_.is_dir() or (extracted := dirname(extracted)):
    +158                return extracted if extracted and Path(extracted).is_dir() else None
    +159    return None
     
    -

    Extract the first identifiable path of the executed command line.

    +

    Extract the first identifiable path from the provided command line text.

    Parameters
      -
    • command_line: The command line text.
    • -
    • flags: Regex match flags.
    • +
    • command_line: The command line text from which to extract the path.
    • +
    • flags: Regex match flags to control the extraction process (default is re.IGNORECASE | re.MULTILINE).
    + +
    Returns
    + +
    +

    The first identified path as a string, or None if no path could be extracted.

    +
    @@ -660,34 +717,44 @@
    Parameters
    def - extract_codeblock(text: str) -> Tuple[Optional[str], str]: + extract_codeblock( text: str, flags: int = re.IGNORECASE|re.MULTILINE) -> tuple[typing.Optional[str], str]:
    -
    191def extract_codeblock(text: str) -> Tuple[Optional[str], str]:
    -192    """Extract language and actual code from a markdown multi-line code block.
    -193    :param text: The markdown formatted text.
    -194    """
    -195    # Match a terminal command formatted in a markdown code block.
    -196    re_command = r".*```((?:\w+)?\s*)\n(.*?)(?=^```)\s*\n?```.*"
    -197    if text and (mat := re.search(re_command, text, flags=re.DOTALL | re.MULTILINE)):
    -198        if mat and len(mat.groups()) == 2:
    -199            lang, code = mat.group(1) or "", mat.group(2) or ""
    -200            return lang.strip(), code.strip()
    -201
    -202    return None, text
    +            
    162def extract_codeblock(text: str, flags: int = re.IGNORECASE | re.MULTILINE) -> tuple[Optional[str], str]:
    +163    """Extract the programming language and actual code from a markdown multi-line code block.
    +164    :param text: The markdown-formatted text containing the code block.
    +165    :param flags: Regex match flags to control the extraction process (default is re.IGNORECASE | re.MULTILINE).
    +166    :return: A tuple where the first element is the programming language (or None if not specified),
    +167             and the second element is the extracted code as a string.
    +168    """
    +169    # Match a terminal command formatted in a markdown code block.
    +170    re_command = r".*```((?:\w+)?\s*)\n(.*?)(?=^```)\s*\n?```.*"
    +171    if text and (mat := re.search(re_command, text, flags=flags)):
    +172        if mat and len(mat.groups()) == 2:
    +173            lang, code = mat.group(1) or "", mat.group(2) or ""
    +174            return lang, code
    +175    return None, text
     
    -

    Extract language and actual code from a markdown multi-line code block.

    +

    Extract the programming language and actual code from a markdown multi-line code block.

    Parameters
      -
    • text: The markdown formatted text.
    • +
    • text: The markdown-formatted text containing the code block.
    • +
    • flags: Regex match flags to control the extraction process (default is re.IGNORECASE | re.MULTILINE).
    + +
    Returns
    + +
    +

    A tuple where the first element is the programming language (or None if not specified), + and the second element is the extracted code as a string.

    +
    @@ -703,27 +770,32 @@
    Parameters
    -
    205def media_type_of(pathname: str) -> Optional[tuple[str, ...]] | None:
    -206    """Return the file media type, or none is guessing was not possible.
    -207    :param pathname: The file path to check.
    -208    """
    -209    mimetypes.init()
    -210    mtype, _ = mimetypes.guess_type(basename(pathname))
    -211
    -212    if mtype is not None:
    -213        return tuple(mtype.split("/"))
    -214
    -215    return None
    +            
    178def media_type_of(pathname: str) -> Optional[Optional[tuple[str, ...]]]:
    +179    """Return the media type of the specified file, or None if the type could not be determined.
    +180    :param pathname: The file path to check.
    +181    :return: A tuple representing the media type (e.g., ("image", "jpeg")), or None if guessing was not possible.
    +182    """
    +183    mimetypes.init()
    +184    mtype, _ = mimetypes.guess_type(basename(pathname))
    +185    if mtype is not None:
    +186        return tuple(mtype.split("/"))
    +187    return None
     
    -

    Return the file media type, or none is guessing was not possible.

    +

    Return the media type of the specified file, or None if the type could not be determined.

    Parameters
    • pathname: The file path to check.
    + +
    Returns
    + +
    +

    A tuple representing the media type (e.g., ("image", "jpeg")), or None if guessing was not possible.

    +
    @@ -739,36 +811,28 @@
    Parameters
    -
    218def seconds(millis: int) -> float:
    -219    """Return the amount of second from milliseconds."""
    -220    return millis / 1000
    +            
    190def seconds(millis: int) -> float:
    +191    """Convert milliseconds to seconds.
    +192    :param millis: The time in milliseconds.
    +193    :return: The equivalent time in seconds as a float.
    +194    """
    +195    return millis / 1000
     
    -

    Return the amount of second from milliseconds.

    -
    - - - -
    - -
    - - def - ensure_ln(text: str) -> str: +

    Convert milliseconds to seconds.

    - +
    Parameters
    -
    - -
    223def ensure_ln(text: str) -> str:
    -224    """Ensure text starts and ends with new line."""
    -225    ln: str = os.linesep
    -226    return ensure_endswith(ensure_startswith(text, ln), ln)
    -
    +
      +
    • millis: The time in milliseconds.
    • +
    +
    Returns
    -

    Ensure text starts and ends with new line.

    +
    +

    The equivalent time in seconds as a float.

    +
    diff --git a/docs/api/askai/main/askai/exception.html b/docs/api/askai/main/askai/exception.html index 0a86fa64..14ade58b 100644 --- a/docs/api/askai/main/askai/exception.html +++ b/docs/api/askai/main/askai/exception.html @@ -3,7 +3,7 @@ - + main.askai.exception API documentation @@ -55,7 +55,7 @@

     1# _*_ coding: utf-8 _*_
      2#
    - 3# hspylib-askai v1.0.11
    + 3# hspylib-askai v1.0.13
      4#
      5# Package: main.askai.exception
      6"""Package initialization."""
    @@ -63,7 +63,7 @@ 

    8__all__ = [ 9 'exceptions' 10] -11__version__ = '1.0.11' +11__version__ = '1.0.13'

    diff --git a/docs/api/askai/main/askai/exception/exceptions.html b/docs/api/askai/main/askai/exception/exceptions.html index d67f04c5..2bcc2ea2 100644 --- a/docs/api/askai/main/askai/exception/exceptions.html +++ b/docs/api/askai/main/askai/exception/exceptions.html @@ -3,7 +3,7 @@ - + main.askai.exception.exceptions API documentation @@ -126,6 +126,12 @@

    API Documentation

    +
  • + InterruptionRequest +
      +
    + +
  • @@ -237,6 +243,10 @@

    78 79class CameraAccessFailure(HSBaseException): 80 """Raised when failed to operate with the computer Webcam.""" +81 +82 +83class InterruptionRequest(HSBaseException): +84 """Raised when the AI flags to interrupt the execution of the action plan."""

    @@ -817,6 +827,42 @@
    Inherited Members
    +
    + +
    + + class + InterruptionRequest(hspylib.core.exception.exceptions.HSBaseException): + + + +
    + +
    84class InterruptionRequest(HSBaseException):
    +85    """Raised when the AI flags to interrupt the execution of the action plan."""
    +
    + + +

    Raised when the AI flags to interrupt the execution of the action plan.

    +
    + + +
    +
    Inherited Members
    +
    +
    hspylib.core.exception.exceptions.HSBaseException
    +
    HSBaseException
    + +
    +
    builtins.BaseException
    +
    with_traceback
    +
    add_note
    +
    args
    + +
    +
    +
    +
    - \ No newline at end of file diff --git a/docs/api/askai/main/askai/language/argos_translator.html b/docs/api/askai/main/askai/language/translators/argos_translator.html similarity index 75% rename from docs/api/askai/main/askai/language/argos_translator.html rename to docs/api/askai/main/askai/language/translators/argos_translator.html index 3dabdd0c..646de92f 100644 --- a/docs/api/askai/main/askai/language/argos_translator.html +++ b/docs/api/askai/main/askai/language/translators/argos_translator.html @@ -3,24 +3,24 @@ - - main.askai.language.argos_translator API documentation + + main.askai.language.translators.argos_translator API documentation - +

  • translate
  • +
  • + name +
  • @@ -56,13 +59,13 @@

    API Documentation

    -main.askai.language.argos_translator

    +main.askai.language.translators.argos_translator

    @project: HsPyLib-AskAI @package: askai.language.argos_translator @file: argos_translator.py @created: Fri, 5 Jan 2024 - @author: Hugo Saporetti Junior" + @author: Hugo Saporetti Junior @site: https://github.com/yorevs/askai @license: MIT - Please refer to https://opensource.org/licenses/MIT

    @@ -81,7 +84,7 @@

    6 @package: askai.language.argos_translator 7 @file: argos_translator.py 8 @created: Fri, 5 Jan 2024 - 9 @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior" + 9 @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior 10 @site: https://github.com/yorevs/askai 11 @license: MIT - Please refer to <https://opensource.org/licenses/MIT> 12 @@ -91,81 +94,83 @@

    16from argostranslate import package, translate 17from argostranslate.translate import ITranslation 18from askai.exception.exceptions import TranslationPackageError -19from askai.language.language import Language -20from functools import lru_cache -21from typing import Optional -22 -23import logging as log -24import os -25import sys -26 +19from askai.language.ai_translator import AITranslator +20from askai.language.language import Language +21from functools import lru_cache +22from typing import Optional +23 +24import logging as log +25import os +26import sys 27 -28class ArgosTranslator: -29 """Provides a multilingual offline translation engine. -30 Language packages are downloaded at: ~/.local/share/argos-translate/packages -31 """ -32 -33 @staticmethod -34 def _get_argos_model(source: Language, target: Language) -> Optional[ITranslation]: -35 """Retrieve ARGOS model from source to target languages. -36 :param source: The source language. -37 :param target: The target language. -38 """ -39 lang = f"{source.name} -> {target.name}" -40 log.debug(f"Translating from: {source} to: {target}") -41 source_lang = [ -42 model for model in translate.get_installed_languages() if lang in map(repr, model.translations_from) -43 ] -44 target_lang = [ -45 model for model in translate.get_installed_languages() if lang in map(repr, model.translations_to) -46 ] -47 if len(source_lang) <= 0 or len(target_lang) <= 0: -48 log.info('Translation "%s" is not installed!') -49 return None -50 -51 return source_lang[0].get_translation(target_lang[0]) +28 +29class ArgosTranslator(AITranslator): +30 """Provides a multilingual offline translation engine using ARGOS translate. +31 Reference: https://github.com/argosopentech/argos-translate +32 Language packages are downloaded at: ~/.local/share/argos-translate/packages +33 """ +34 +35 @staticmethod +36 def _get_argos_model(source: Language, target: Language) -> Optional[ITranslation]: +37 """Retrieve the ARGOS model for translating from the source language to the target language. +38 :param source: The source language. +39 :param target: The target language. +40 :return: An ITranslation model for the specified languages if found; None otherwise. +41 """ +42 lang = f"{source.name} -> {target.name}" +43 source_lang = [ +44 model for model in translate.get_installed_languages() if lang in map(repr, model.translations_from) +45 ] +46 target_lang = [ +47 model for model in translate.get_installed_languages() if lang in map(repr, model.translations_to) +48 ] +49 if len(source_lang) <= 0 or len(target_lang) <= 0: +50 log.info('Translation "%s" is not installed!') +51 return None 52 -53 def __init__(self, from_idiom: Language, to_idiom: Language): -54 self._from_idiom: Language = from_idiom -55 self._to_idiom: Language = to_idiom -56 if argos_model := self._get_argos_model(from_idiom, to_idiom): -57 log.debug(f"Argos translator found for: {from_idiom.language} -> {to_idiom.language}") -58 elif not self._install_translator(): -59 raise TranslationPackageError( -60 f"Could not install Argos translator: {from_idiom.language} -> {to_idiom.language}" -61 ) -62 else: -63 argos_model = self._get_argos_model(from_idiom, to_idiom) -64 self._argos_model = argos_model -65 -66 @lru_cache -67 def translate(self, text: str) -> str: -68 """Translate text using Argos translator. -69 :param text: Text to translate. -70 """ -71 return text if self._from_idiom == self._to_idiom else self._argos_model.translate(text) -72 -73 def _install_translator(self) -> bool: -74 """Install the Argos translator if it's not yet installed on the system.""" -75 old_stdout = sys.stdout -76 with open(os.devnull, "w") as dev_null: -77 sys.stdout = dev_null -78 package.update_package_index() -79 required_package = next( -80 filter( -81 lambda x: x.from_code == self._from_idiom.language and x.to_code == self._to_idiom.language, -82 package.get_available_packages(), -83 ) -84 ) -85 log.debug("Downloading and installing translator package: %s", required_package) -86 package.install_from_path(required_package.download()) -87 sys.stdout = old_stdout -88 return required_package is not None -89 -90 -91if __name__ == "__main__": -92 t = ArgosTranslator(Language.EN_US, Language.PT_BR) -93 print(t.translate("Hello \"initialization\" how ARE you doing 'what is up'")) +53 return source_lang[0].get_translation(target_lang[0]) +54 +55 def __init__(self, from_idiom: Language, to_idiom: Language): +56 super().__init__(from_idiom, to_idiom) +57 if argos_model := self._get_argos_model(from_idiom, to_idiom): +58 log.debug(f"Argos translator found for: {from_idiom.language} -> {to_idiom.language}") +59 elif not self._install_translator(): +60 raise TranslationPackageError( +61 f"Could not install Argos translator: {from_idiom.language} -> {to_idiom.language}" +62 ) +63 else: +64 argos_model = self._get_argos_model(from_idiom, to_idiom) +65 self._argos_model = argos_model +66 +67 @lru_cache +68 def translate(self, text: str, **kwargs) -> str: +69 """Translate text from the source language to the target language. +70 :param text: Text to translate. +71 :return: The translated text. +72 """ +73 return text if self._source_lang == self._target_lang else self._argos_model.translate(text) +74 +75 def name(self) -> str: +76 return "Argos" +77 +78 def _install_translator(self) -> bool: +79 """Install the Argos translator if it's not yet installed on the system. +80 :return: True if the installation was successful, False otherwise. +81 """ +82 old_stdout = sys.stdout +83 with open(os.devnull, "w") as dev_null: +84 sys.stdout = dev_null +85 package.update_package_index() +86 required_package = next( +87 filter( +88 lambda x: x.from_code == self._source_lang.language and x.to_code == self._target_lang.language, +89 package.get_available_packages(), +90 ) +91 ) +92 log.debug("Downloading and installing translator package: %s", required_package) +93 package.install_from_path(required_package.download()) +94 sys.stdout = old_stdout +95 return required_package is not None

    @@ -175,77 +180,84 @@

    class - ArgosTranslator: + ArgosTranslator(askai.language.ai_translator.AITranslator):
    -
    29class ArgosTranslator:
    -30    """Provides a multilingual offline translation engine.
    -31    Language packages are downloaded at: ~/.local/share/argos-translate/packages
    -32    """
    -33
    -34    @staticmethod
    -35    def _get_argos_model(source: Language, target: Language) -> Optional[ITranslation]:
    -36        """Retrieve ARGOS model from source to target languages.
    -37        :param source: The source language.
    -38        :param target: The target language.
    -39        """
    -40        lang = f"{source.name} -> {target.name}"
    -41        log.debug(f"Translating from: {source} to: {target}")
    -42        source_lang = [
    -43            model for model in translate.get_installed_languages() if lang in map(repr, model.translations_from)
    -44        ]
    -45        target_lang = [
    -46            model for model in translate.get_installed_languages() if lang in map(repr, model.translations_to)
    -47        ]
    -48        if len(source_lang) <= 0 or len(target_lang) <= 0:
    -49            log.info('Translation "%s" is not installed!')
    -50            return None
    -51
    -52        return source_lang[0].get_translation(target_lang[0])
    +            
    30class ArgosTranslator(AITranslator):
    +31    """Provides a multilingual offline translation engine using ARGOS translate.
    +32    Reference: https://github.com/argosopentech/argos-translate
    +33    Language packages are downloaded at: ~/.local/share/argos-translate/packages
    +34    """
    +35
    +36    @staticmethod
    +37    def _get_argos_model(source: Language, target: Language) -> Optional[ITranslation]:
    +38        """Retrieve the ARGOS model for translating from the source language to the target language.
    +39        :param source: The source language.
    +40        :param target: The target language.
    +41        :return: An ITranslation model for the specified languages if found; None otherwise.
    +42        """
    +43        lang = f"{source.name} -> {target.name}"
    +44        source_lang = [
    +45            model for model in translate.get_installed_languages() if lang in map(repr, model.translations_from)
    +46        ]
    +47        target_lang = [
    +48            model for model in translate.get_installed_languages() if lang in map(repr, model.translations_to)
    +49        ]
    +50        if len(source_lang) <= 0 or len(target_lang) <= 0:
    +51            log.info('Translation "%s" is not installed!')
    +52            return None
     53
    -54    def __init__(self, from_idiom: Language, to_idiom: Language):
    -55        self._from_idiom: Language = from_idiom
    -56        self._to_idiom: Language = to_idiom
    -57        if argos_model := self._get_argos_model(from_idiom, to_idiom):
    -58            log.debug(f"Argos translator found for: {from_idiom.language} -> {to_idiom.language}")
    -59        elif not self._install_translator():
    -60            raise TranslationPackageError(
    -61                f"Could not install Argos translator: {from_idiom.language} -> {to_idiom.language}"
    -62            )
    -63        else:
    -64            argos_model = self._get_argos_model(from_idiom, to_idiom)
    -65        self._argos_model = argos_model
    -66
    -67    @lru_cache
    -68    def translate(self, text: str) -> str:
    -69        """Translate text using Argos translator.
    -70        :param text: Text to translate.
    -71        """
    -72        return text if self._from_idiom == self._to_idiom else self._argos_model.translate(text)
    -73
    -74    def _install_translator(self) -> bool:
    -75        """Install the Argos translator if it's not yet installed on the system."""
    -76        old_stdout = sys.stdout
    -77        with open(os.devnull, "w") as dev_null:
    -78            sys.stdout = dev_null
    -79            package.update_package_index()
    -80            required_package = next(
    -81                filter(
    -82                    lambda x: x.from_code == self._from_idiom.language and x.to_code == self._to_idiom.language,
    -83                    package.get_available_packages(),
    -84                )
    -85            )
    -86            log.debug("Downloading and installing translator package: %s", required_package)
    -87            package.install_from_path(required_package.download())
    -88            sys.stdout = old_stdout
    -89        return required_package is not None
    +54        return source_lang[0].get_translation(target_lang[0])
    +55
    +56    def __init__(self, from_idiom: Language, to_idiom: Language):
    +57        super().__init__(from_idiom, to_idiom)
    +58        if argos_model := self._get_argos_model(from_idiom, to_idiom):
    +59            log.debug(f"Argos translator found for: {from_idiom.language} -> {to_idiom.language}")
    +60        elif not self._install_translator():
    +61            raise TranslationPackageError(
    +62                f"Could not install Argos translator: {from_idiom.language} -> {to_idiom.language}"
    +63            )
    +64        else:
    +65            argos_model = self._get_argos_model(from_idiom, to_idiom)
    +66        self._argos_model = argos_model
    +67
    +68    @lru_cache
    +69    def translate(self, text: str, **kwargs) -> str:
    +70        """Translate text from the source language to the target language.
    +71        :param text: Text to translate.
    +72        :return: The translated text.
    +73        """
    +74        return text if self._source_lang == self._target_lang else self._argos_model.translate(text)
    +75
    +76    def name(self) -> str:
    +77        return "Argos"
    +78
    +79    def _install_translator(self) -> bool:
    +80        """Install the Argos translator if it's not yet installed on the system.
    +81        :return: True if the installation was successful, False otherwise.
    +82        """
    +83        old_stdout = sys.stdout
    +84        with open(os.devnull, "w") as dev_null:
    +85            sys.stdout = dev_null
    +86            package.update_package_index()
    +87            required_package = next(
    +88                filter(
    +89                    lambda x: x.from_code == self._source_lang.language and x.to_code == self._target_lang.language,
    +90                    package.get_available_packages(),
    +91                )
    +92            )
    +93            log.debug("Downloading and installing translator package: %s", required_package)
    +94            package.install_from_path(required_package.download())
    +95            sys.stdout = old_stdout
    +96        return required_package is not None
     
    -

    Provides a multilingual offline translation engine. +

    Provides a multilingual offline translation engine using ARGOS translate. +Reference: https://github.com/argosopentech/argos-translate Language packages are downloaded at: ~/.local/share/argos-translate/packages

    @@ -260,18 +272,17 @@

    -
    54    def __init__(self, from_idiom: Language, to_idiom: Language):
    -55        self._from_idiom: Language = from_idiom
    -56        self._to_idiom: Language = to_idiom
    -57        if argos_model := self._get_argos_model(from_idiom, to_idiom):
    -58            log.debug(f"Argos translator found for: {from_idiom.language} -> {to_idiom.language}")
    -59        elif not self._install_translator():
    -60            raise TranslationPackageError(
    -61                f"Could not install Argos translator: {from_idiom.language} -> {to_idiom.language}"
    -62            )
    -63        else:
    -64            argos_model = self._get_argos_model(from_idiom, to_idiom)
    -65        self._argos_model = argos_model
    +            
    56    def __init__(self, from_idiom: Language, to_idiom: Language):
    +57        super().__init__(from_idiom, to_idiom)
    +58        if argos_model := self._get_argos_model(from_idiom, to_idiom):
    +59            log.debug(f"Argos translator found for: {from_idiom.language} -> {to_idiom.language}")
    +60        elif not self._install_translator():
    +61            raise TranslationPackageError(
    +62                f"Could not install Argos translator: {from_idiom.language} -> {to_idiom.language}"
    +63            )
    +64        else:
    +65            argos_model = self._get_argos_model(from_idiom, to_idiom)
    +66        self._argos_model = argos_model
     
    @@ -284,28 +295,62 @@

    @lru_cache
    def - translate(self, text: str) -> str: + translate(self, text: str, **kwargs) -> str:

    -
    67    @lru_cache
    -68    def translate(self, text: str) -> str:
    -69        """Translate text using Argos translator.
    -70        :param text: Text to translate.
    -71        """
    -72        return text if self._from_idiom == self._to_idiom else self._argos_model.translate(text)
    +            
    68    @lru_cache
    +69    def translate(self, text: str, **kwargs) -> str:
    +70        """Translate text from the source language to the target language.
    +71        :param text: Text to translate.
    +72        :return: The translated text.
    +73        """
    +74        return text if self._source_lang == self._target_lang else self._argos_model.translate(text)
     
    -

    Translate text using Argos translator.

    +

    Translate text from the source language to the target language.

    Parameters
    • text: Text to translate.
    + +
    Returns
    + +
    +

    The translated text.

    +
    +
    + + +
    +
    + +
    + + def + name(self) -> str: + + + +
    + +
    76    def name(self) -> str:
    +77        return "Argos"
    +
    + + +

    Return the translator name or model.

    + +
    Returns
    + +
    +

    The name or model of the translator.

    +
    @@ -364,7 +409,7 @@
    Parameters
    script.async = true; script.onload = () => resolve(window.pdocSearch); script.onerror = (e) => reject(e); - script.src = "../../../search.js"; + script.src = "../../../../search.js"; document.getElementsByTagName("head")[0].appendChild(script); }); } catch (e) { @@ -406,7 +451,7 @@
    Parameters
    } for (let result of results.slice(0, 10)) { let doc = result.doc; - let url = `../../../${doc.modulename.replaceAll(".", "/")}.html`; + let url = `../../../../${doc.modulename.replaceAll(".", "/")}.html`; if (doc.qualname) { url += `#${doc.qualname}`; } diff --git a/docs/api/askai/main/askai/language/translators/deep.html b/docs/api/askai/main/askai/language/translators/deepl_translator.html similarity index 79% rename from docs/api/askai/main/askai/language/translators/deep.html rename to docs/api/askai/main/askai/language/translators/deepl_translator.html index 689332ca..4335ca35 100644 --- a/docs/api/askai/main/askai/language/translators/deep.html +++ b/docs/api/askai/main/askai/language/translators/deepl_translator.html @@ -3,8 +3,8 @@ - - main.askai.language.translators.deep API documentation + + main.askai.language.translators.deepl_translator API documentation @@ -59,7 +59,7 @@

    API Documentation

    -main.askai.language.translators.deep

    +main.askai.language.translators.deepl_translator

    @project: HsPyLib-AskAI @package: askai.language.argos_translator @@ -72,9 +72,9 @@

    Copyright (c) 2024, HomeSetup

    - + - +
     1#!/usr/bin/env python3
      2# -*- coding: utf-8 -*-
    @@ -90,65 +90,62 @@ 

    12 13 Copyright (c) 2024, HomeSetup 14""" -15from askai.exception.exceptions import MissingApiKeyError +15from askai.__classpath__ import API_KEYS 16from askai.language.ai_translator import AITranslator 17from askai.language.language import Language 18from functools import lru_cache 19 20import deepl -21import os +21 22 -23 -24class DeepLTranslator(AITranslator): -25 """Provides a multilingual online translation engine.""" -26 -27 def __init__(self, from_idiom: Language, to_idiom: Language): -28 super().__init__(from_idiom, to_idiom) -29 if not (auth_key := os.environ.get("DEEPL_API_KEY")): -30 raise MissingApiKeyError("ApiKey: 'DEEPL_API_KEY' is required to use DeepL translator!") -31 self._translator = deepl.Translator(auth_key) +23class DeepLTranslator(AITranslator): +24 """Provides a multilingual online translation engine using DeepL translator. +25 Reference: https://hnd.www.deepl.com/en/translator +26 """ +27 +28 def __init__(self, source_lang: Language, target_lang: Language): +29 super().__init__(source_lang, target_lang) +30 API_KEYS.ensure("DEEPL_API_KEY", "DeepLTranslator") +31 self._translator = deepl.Translator(API_KEYS.DEEPL_API_KEY) 32 -33 @lru_cache(maxsize=256) -34 def translate(self, text: str) -> str: -35 """Translate text using te default translator. +33 @lru_cache +34 def translate(self, text: str, **kwargs) -> str: +35 """Translate text from the source language to the target language. 36 :param text: Text to translate. -37 """ -38 if self._from_idiom == self._to_idiom: -39 return text -40 lang = self._from_locale() -41 result: deepl.TextResult = self._translator.translate_text( -42 text, preserve_formatting=True, source_lang=lang[0], target_lang=lang[1] -43 ) -44 return str(result) -45 -46 def name(self) -> str: -47 return "DeepL" -48 -49 def _from_locale(self) -> tuple[str, ...]: -50 """Convert from locale string to DeepL language. -51 Ref:.https://developers.deepl.com/docs/resources/supported-languages -52 """ -53 from_lang: str = self._from_idiom.language.upper() -54 match self._to_idiom: -55 case Language.PT_BR: -56 to_lang: str = "PT-BR" -57 case Language.PT_PT: -58 to_lang: str = "PT-PT" -59 case Language.EN_GB: -60 to_lang: str = "EN-GB" -61 case _: -62 if self._to_idiom.language.casefold() == "en": -63 to_lang: str = "en_US" -64 elif self._to_idiom.language.casefold() == "zh": -65 to_lang: str = "ZH-HANS" -66 else: -67 to_lang: str = self._to_idiom.language.upper() -68 return from_lang, to_lang.upper() -69 -70 -71if __name__ == "__main__": -72 t = DeepLTranslator(Language.EN_US, Language.PT_BR) -73 print(t.translate("--- \n Hello \"initialization\"\n How ARE you doing 'what is up'?")) +37 :return: The translated text. +38 """ +39 if self._source_lang != self._target_lang: +40 kwargs["preserve_formatting"] = True +41 lang = self._from_locale() +42 result: deepl.TextResult = self._translator.translate_text( +43 text, source_lang=lang[0], target_lang=lang[1], **kwargs +44 ) +45 return str(result) +46 return text +47 +48 def name(self) -> str: +49 return "DeepL" +50 +51 def _from_locale(self) -> tuple[str, ...]: +52 """Convert from locale string to DeepL language. +53 Ref:.https://developers.deepl.com/docs/resources/supported-languages +54 """ +55 from_lang: str = self._source_lang.language.upper() +56 match self._target_lang: +57 case Language.PT_BR: +58 to_lang: str = "PT-BR" +59 case Language.PT_PT: +60 to_lang: str = "PT-PT" +61 case Language.EN_GB: +62 to_lang: str = "EN-GB" +63 case _: +64 if self._target_lang.language.casefold() == "en": +65 to_lang: str = "en_US" +66 elif self._target_lang.language.casefold() == "zh": +67 to_lang: str = "ZH-HANS" +68 else: +69 to_lang: str = self._target_lang.language.upper() +70 return from_lang, to_lang.upper()

    @@ -164,55 +161,59 @@

    -
    25class DeepLTranslator(AITranslator):
    -26    """Provides a multilingual online translation engine."""
    -27
    -28    def __init__(self, from_idiom: Language, to_idiom: Language):
    -29        super().__init__(from_idiom, to_idiom)
    -30        if not (auth_key := os.environ.get("DEEPL_API_KEY")):
    -31            raise MissingApiKeyError("ApiKey: 'DEEPL_API_KEY' is required to use DeepL translator!")
    -32        self._translator = deepl.Translator(auth_key)
    +            
    24class DeepLTranslator(AITranslator):
    +25    """Provides a multilingual online translation engine using DeepL translator.
    +26    Reference: https://hnd.www.deepl.com/en/translator
    +27    """
    +28
    +29    def __init__(self, source_lang: Language, target_lang: Language):
    +30        super().__init__(source_lang, target_lang)
    +31        API_KEYS.ensure("DEEPL_API_KEY", "DeepLTranslator")
    +32        self._translator = deepl.Translator(API_KEYS.DEEPL_API_KEY)
     33
    -34    @lru_cache(maxsize=256)
    -35    def translate(self, text: str) -> str:
    -36        """Translate text using te default translator.
    +34    @lru_cache
    +35    def translate(self, text: str, **kwargs) -> str:
    +36        """Translate text from the source language to the target language.
     37        :param text: Text to translate.
    -38        """
    -39        if self._from_idiom == self._to_idiom:
    -40            return text
    -41        lang = self._from_locale()
    -42        result: deepl.TextResult = self._translator.translate_text(
    -43            text, preserve_formatting=True, source_lang=lang[0], target_lang=lang[1]
    -44        )
    -45        return str(result)
    -46
    -47    def name(self) -> str:
    -48        return "DeepL"
    -49
    -50    def _from_locale(self) -> tuple[str, ...]:
    -51        """Convert from locale string to DeepL language.
    -52        Ref:.https://developers.deepl.com/docs/resources/supported-languages
    -53        """
    -54        from_lang: str = self._from_idiom.language.upper()
    -55        match self._to_idiom:
    -56            case Language.PT_BR:
    -57                to_lang: str = "PT-BR"
    -58            case Language.PT_PT:
    -59                to_lang: str = "PT-PT"
    -60            case Language.EN_GB:
    -61                to_lang: str = "EN-GB"
    -62            case _:
    -63                if self._to_idiom.language.casefold() == "en":
    -64                    to_lang: str = "en_US"
    -65                elif self._to_idiom.language.casefold() == "zh":
    -66                    to_lang: str = "ZH-HANS"
    -67                else:
    -68                    to_lang: str = self._to_idiom.language.upper()
    -69        return from_lang, to_lang.upper()
    +38        :return: The translated text.
    +39        """
    +40        if self._source_lang != self._target_lang:
    +41            kwargs["preserve_formatting"] = True
    +42            lang = self._from_locale()
    +43            result: deepl.TextResult = self._translator.translate_text(
    +44                text, source_lang=lang[0], target_lang=lang[1], **kwargs
    +45            )
    +46            return str(result)
    +47        return text
    +48
    +49    def name(self) -> str:
    +50        return "DeepL"
    +51
    +52    def _from_locale(self) -> tuple[str, ...]:
    +53        """Convert from locale string to DeepL language.
    +54        Ref:.https://developers.deepl.com/docs/resources/supported-languages
    +55        """
    +56        from_lang: str = self._source_lang.language.upper()
    +57        match self._target_lang:
    +58            case Language.PT_BR:
    +59                to_lang: str = "PT-BR"
    +60            case Language.PT_PT:
    +61                to_lang: str = "PT-PT"
    +62            case Language.EN_GB:
    +63                to_lang: str = "EN-GB"
    +64            case _:
    +65                if self._target_lang.language.casefold() == "en":
    +66                    to_lang: str = "en_US"
    +67                elif self._target_lang.language.casefold() == "zh":
    +68                    to_lang: str = "ZH-HANS"
    +69                else:
    +70                    to_lang: str = self._target_lang.language.upper()
    +71        return from_lang, to_lang.upper()
     
    -

    Provides a multilingual online translation engine.

    +

    Provides a multilingual online translation engine using DeepL translator. +Reference: https://hnd.www.deepl.com/en/translator

    @@ -220,17 +221,16 @@

    - DeepLTranslator( from_idiom: askai.language.language.Language, to_idiom: askai.language.language.Language) + DeepLTranslator( source_lang: askai.language.language.Language, target_lang: askai.language.language.Language)
    -
    28    def __init__(self, from_idiom: Language, to_idiom: Language):
    -29        super().__init__(from_idiom, to_idiom)
    -30        if not (auth_key := os.environ.get("DEEPL_API_KEY")):
    -31            raise MissingApiKeyError("ApiKey: 'DEEPL_API_KEY' is required to use DeepL translator!")
    -32        self._translator = deepl.Translator(auth_key)
    +            
    29    def __init__(self, source_lang: Language, target_lang: Language):
    +30        super().__init__(source_lang, target_lang)
    +31        API_KEYS.ensure("DEEPL_API_KEY", "DeepLTranslator")
    +32        self._translator = deepl.Translator(API_KEYS.DEEPL_API_KEY)
     
    @@ -240,37 +240,45 @@

    -
    @lru_cache(maxsize=256)
    +
    @lru_cache
    def - translate(self, text: str) -> str: + translate(self, text: str, **kwargs) -> str:
    -
    34    @lru_cache(maxsize=256)
    -35    def translate(self, text: str) -> str:
    -36        """Translate text using te default translator.
    +            
    34    @lru_cache
    +35    def translate(self, text: str, **kwargs) -> str:
    +36        """Translate text from the source language to the target language.
     37        :param text: Text to translate.
    -38        """
    -39        if self._from_idiom == self._to_idiom:
    -40            return text
    -41        lang = self._from_locale()
    -42        result: deepl.TextResult = self._translator.translate_text(
    -43            text, preserve_formatting=True, source_lang=lang[0], target_lang=lang[1]
    -44        )
    -45        return str(result)
    +38        :return: The translated text.
    +39        """
    +40        if self._source_lang != self._target_lang:
    +41            kwargs["preserve_formatting"] = True
    +42            lang = self._from_locale()
    +43            result: deepl.TextResult = self._translator.translate_text(
    +44                text, source_lang=lang[0], target_lang=lang[1], **kwargs
    +45            )
    +46            return str(result)
    +47        return text
     
    -

    Translate text using te default translator.

    +

    Translate text from the source language to the target language.

    Parameters
    • text: Text to translate.
    + +
    Returns
    + +
    +

    The translated text.

    +
    @@ -286,12 +294,18 @@
    Parameters
    -
    47    def name(self) -> str:
    -48        return "DeepL"
    +            
    49    def name(self) -> str:
    +50        return "DeepL"
     

    Return the translator name or model.

    + +
    Returns
    + +
    +

    The name or model of the translator.

    +
    diff --git a/docs/api/askai/main/askai/language/translators/marian.html b/docs/api/askai/main/askai/language/translators/marian_translator.html similarity index 72% rename from docs/api/askai/main/askai/language/translators/marian.html rename to docs/api/askai/main/askai/language/translators/marian_translator.html index e2d6e481..8cc9939d 100644 --- a/docs/api/askai/main/askai/language/translators/marian.html +++ b/docs/api/askai/main/askai/language/translators/marian_translator.html @@ -3,8 +3,8 @@ - - main.askai.language.translators.marian API documentation + + main.askai.language.translators.marian_translator API documentation @@ -62,63 +62,74 @@

    API Documentation

    -main.askai.language.translators.marian

    +main.askai.language.translators.marian_translator

    - + - +
     1from askai.language.ai_translator import AITranslator
      2from askai.language.language import Language
      3from functools import lru_cache
      4from transformers import MarianMTModel, MarianTokenizer
      5
    - 6
    - 7class MarianTranslator(AITranslator):
    - 8    """Provides a multilingual offline translation engine.
    - 9    """
    -10
    -11    # Specify the model name
    -12    MODEL_NAME = 'Helsinki-NLP/opus-mt-en-ROMANCE'
    + 6import re
    + 7
    + 8
    + 9class MarianTranslator(AITranslator):
    +10    """Provides a multilingual offline translation engine using Marian translator.
    +11    Reference: https://marian-nmt.github.io/
    +12    """
     13
    -14    def __init__(self, from_idiom: Language, to_idiom: Language):
    -15        super().__init__(from_idiom, to_idiom)
    -16        # Load the model and tokenizer
    -17        self._model = MarianMTModel.from_pretrained(self.MODEL_NAME)
    -18        self._tokenizer = MarianTokenizer.from_pretrained(self.MODEL_NAME)
    -19
    -20    @lru_cache(maxsize=256)
    -21    def translate(self, text: str) -> str:
    -22        """Translate text using te default translator.
    -23        :param text: Text to translate.
    -24        """
    -25        if self._from_idiom == self._to_idiom:
    -26            return text
    -27
    -28        return self._translate(
    -29            f">>{self._to_idiom.idiom}<<{text}",
    -30        )
    +14    # Specify the model name
    +15    MODEL_NAME = "Helsinki-NLP/opus-mt-en-ROMANCE"
    +16
    +17    def __init__(self, from_idiom: Language, to_idiom: Language):
    +18        super().__init__(from_idiom, to_idiom)
    +19        # Load the model and tokenizer
    +20        self._model = MarianMTModel.from_pretrained(self.MODEL_NAME)
    +21        self._tokenizer = MarianTokenizer.from_pretrained(self.MODEL_NAME)
    +22
    +23    @lru_cache
    +24    def translate(self, text: str, **kwargs) -> str:
    +25        """Translate text from the source language to the target language.
    +26        :param text: Text to translate.
    +27        :return: The translated text.
    +28        """
    +29        if self._source_lang == self._target_lang:
    +30            return text
     31
    -32    def _translate(self, text) -> str:
    -33        """TODO"""
    -34        # Prepare the text for the model
    -35        inputs = self._tokenizer.encode(text, return_tensors="pt", padding=True)
    -36        # Perform the translation
    -37        translated = self._model.generate(inputs)
    -38        # Decode the translated text
    -39        translated_text = self._tokenizer.decode(
    -40            translated[0], skip_special_tokens=True
    -41        )
    -42        return translated_text
    -43
    -44    def name(self) -> str:
    -45        return "Marian"
    +32        kwargs["return_tensors"] = "pt"
    +33        kwargs["padding"] = True
    +34
    +35        return self._translate(f">>{self._target_lang.idiom}<<{text}", **kwargs)
    +36
    +37    def _translate(self, text, **kwargs) -> str:
    +38        """Wrapper function that is going to provide the translation of the text.
    +39        :param text: The text to be translated.
    +40        :return: The translated text.
    +41        """
    +42        # Replace formatting with placeholders
    +43        text_with_placeholders = re.sub(r"\*\*(.*?)\*\*", r"%BOLD%\1%BOLD%", text)
    +44        text_with_placeholders = re.sub(r"_(.*?)_", r"%ITALIC%\1%ITALIC%", text_with_placeholders)
    +45        text_with_placeholders = re.sub(r"\n", r"%LN%", text_with_placeholders)
     46
    -47
    -48if __name__ == "__main__":
    -49    t = MarianTranslator(Language.EN_US, Language.PT_BR)
    -50    print(t.translate("--- \n Hello \"initialization\"\n How ARE you doing 'what is up'?"))
    +47        # Prepare the text for the model
    +48        inputs = self._tokenizer.encode(text_with_placeholders, **kwargs)
    +49
    +50        # Perform the translation
    +51        translated_text = [self._tokenizer.decode(t) for t in self._model.generate(inputs)][0]
    +52
    +53        # Reapply formatting
    +54        translated_text = re.sub(r"% ?BOLD%(.*?)% ?BOLD%", r"**\1**", translated_text)
    +55        translated_text = re.sub(r"% ?ITALIC%(.*?)% ?ITALIC%", r"_\1_", translated_text)
    +56        translated_text = re.sub(r"% ?LN%", r"\n", translated_text)
    +57
    +58        return translated_text
    +59
    +60    def name(self) -> str:
    +61        return "Marian"
     
    @@ -134,49 +145,64 @@

    -
     8class MarianTranslator(AITranslator):
    - 9    """Provides a multilingual offline translation engine.
    -10    """
    -11
    -12    # Specify the model name
    -13    MODEL_NAME = 'Helsinki-NLP/opus-mt-en-ROMANCE'
    +            
    10class MarianTranslator(AITranslator):
    +11    """Provides a multilingual offline translation engine using Marian translator.
    +12    Reference: https://marian-nmt.github.io/
    +13    """
     14
    -15    def __init__(self, from_idiom: Language, to_idiom: Language):
    -16        super().__init__(from_idiom, to_idiom)
    -17        # Load the model and tokenizer
    -18        self._model = MarianMTModel.from_pretrained(self.MODEL_NAME)
    -19        self._tokenizer = MarianTokenizer.from_pretrained(self.MODEL_NAME)
    -20
    -21    @lru_cache(maxsize=256)
    -22    def translate(self, text: str) -> str:
    -23        """Translate text using te default translator.
    -24        :param text: Text to translate.
    -25        """
    -26        if self._from_idiom == self._to_idiom:
    -27            return text
    -28
    -29        return self._translate(
    -30            f">>{self._to_idiom.idiom}<<{text}",
    -31        )
    +15    # Specify the model name
    +16    MODEL_NAME = "Helsinki-NLP/opus-mt-en-ROMANCE"
    +17
    +18    def __init__(self, from_idiom: Language, to_idiom: Language):
    +19        super().__init__(from_idiom, to_idiom)
    +20        # Load the model and tokenizer
    +21        self._model = MarianMTModel.from_pretrained(self.MODEL_NAME)
    +22        self._tokenizer = MarianTokenizer.from_pretrained(self.MODEL_NAME)
    +23
    +24    @lru_cache
    +25    def translate(self, text: str, **kwargs) -> str:
    +26        """Translate text from the source language to the target language.
    +27        :param text: Text to translate.
    +28        :return: The translated text.
    +29        """
    +30        if self._source_lang == self._target_lang:
    +31            return text
     32
    -33    def _translate(self, text) -> str:
    -34        """TODO"""
    -35        # Prepare the text for the model
    -36        inputs = self._tokenizer.encode(text, return_tensors="pt", padding=True)
    -37        # Perform the translation
    -38        translated = self._model.generate(inputs)
    -39        # Decode the translated text
    -40        translated_text = self._tokenizer.decode(
    -41            translated[0], skip_special_tokens=True
    -42        )
    -43        return translated_text
    -44
    -45    def name(self) -> str:
    -46        return "Marian"
    +33        kwargs["return_tensors"] = "pt"
    +34        kwargs["padding"] = True
    +35
    +36        return self._translate(f">>{self._target_lang.idiom}<<{text}", **kwargs)
    +37
    +38    def _translate(self, text, **kwargs) -> str:
    +39        """Wrapper function that is going to provide the translation of the text.
    +40        :param text: The text to be translated.
    +41        :return: The translated text.
    +42        """
    +43        # Replace formatting with placeholders
    +44        text_with_placeholders = re.sub(r"\*\*(.*?)\*\*", r"%BOLD%\1%BOLD%", text)
    +45        text_with_placeholders = re.sub(r"_(.*?)_", r"%ITALIC%\1%ITALIC%", text_with_placeholders)
    +46        text_with_placeholders = re.sub(r"\n", r"%LN%", text_with_placeholders)
    +47
    +48        # Prepare the text for the model
    +49        inputs = self._tokenizer.encode(text_with_placeholders, **kwargs)
    +50
    +51        # Perform the translation
    +52        translated_text = [self._tokenizer.decode(t) for t in self._model.generate(inputs)][0]
    +53
    +54        # Reapply formatting
    +55        translated_text = re.sub(r"% ?BOLD%(.*?)% ?BOLD%", r"**\1**", translated_text)
    +56        translated_text = re.sub(r"% ?ITALIC%(.*?)% ?ITALIC%", r"_\1_", translated_text)
    +57        translated_text = re.sub(r"% ?LN%", r"\n", translated_text)
    +58
    +59        return translated_text
    +60
    +61    def name(self) -> str:
    +62        return "Marian"
     
    -

    Provides a multilingual offline translation engine.

    +

    Provides a multilingual offline translation engine using Marian translator. +Reference: https://marian-nmt.github.io/

    @@ -190,11 +216,11 @@

    -
    15    def __init__(self, from_idiom: Language, to_idiom: Language):
    -16        super().__init__(from_idiom, to_idiom)
    -17        # Load the model and tokenizer
    -18        self._model = MarianMTModel.from_pretrained(self.MODEL_NAME)
    -19        self._tokenizer = MarianTokenizer.from_pretrained(self.MODEL_NAME)
    +            
    18    def __init__(self, from_idiom: Language, to_idiom: Language):
    +19        super().__init__(from_idiom, to_idiom)
    +20        # Load the model and tokenizer
    +21        self._model = MarianMTModel.from_pretrained(self.MODEL_NAME)
    +22        self._tokenizer = MarianTokenizer.from_pretrained(self.MODEL_NAME)
     
    @@ -216,36 +242,44 @@

    -
    @lru_cache(maxsize=256)
    +
    @lru_cache
    def - translate(self, text: str) -> str: + translate(self, text: str, **kwargs) -> str:
    -
    21    @lru_cache(maxsize=256)
    -22    def translate(self, text: str) -> str:
    -23        """Translate text using te default translator.
    -24        :param text: Text to translate.
    -25        """
    -26        if self._from_idiom == self._to_idiom:
    -27            return text
    -28
    -29        return self._translate(
    -30            f">>{self._to_idiom.idiom}<<{text}",
    -31        )
    +            
    24    @lru_cache
    +25    def translate(self, text: str, **kwargs) -> str:
    +26        """Translate text from the source language to the target language.
    +27        :param text: Text to translate.
    +28        :return: The translated text.
    +29        """
    +30        if self._source_lang == self._target_lang:
    +31            return text
    +32
    +33        kwargs["return_tensors"] = "pt"
    +34        kwargs["padding"] = True
    +35
    +36        return self._translate(f">>{self._target_lang.idiom}<<{text}", **kwargs)
     
    -

    Translate text using te default translator.

    +

    Translate text from the source language to the target language.

    Parameters
    • text: Text to translate.
    + +
    Returns
    + +
    +

    The translated text.

    +
    @@ -261,12 +295,18 @@
    Parameters
    -
    45    def name(self) -> str:
    -46        return "Marian"
    +            
    61    def name(self) -> str:
    +62        return "Marian"
     

    Return the translator name or model.

    + +
    Returns
    + +
    +

    The name or model of the translator.

    +
    diff --git a/docs/api/askai/main/askai/tui.html b/docs/api/askai/main/askai/tui.html index 34ed4e52..2bfaca15 100644 --- a/docs/api/askai/main/askai/tui.html +++ b/docs/api/askai/main/askai/tui.html @@ -3,7 +3,7 @@ - + main.askai.tui API documentation @@ -59,7 +59,7 @@

     1# _*_ coding: utf-8 _*_
      2#
    - 3# hspylib-askai v1.0.11
    + 3# hspylib-askai v1.0.13
      4#
      5# Package: main.askai.tui
      6"""Package initialization."""
    @@ -71,7 +71,7 @@ 

    12 'app_widgets', 13 'askai_app' 14] -15__version__ = '1.0.11' +15__version__ = '1.0.13'

    diff --git a/docs/api/askai/main/askai/tui/app_header.html b/docs/api/askai/main/askai/tui/app_header.html index 3ea6ccf1..d8f897bc 100644 --- a/docs/api/askai/main/askai/tui/app_header.html +++ b/docs/api/askai/main/askai/tui/app_header.html @@ -3,7 +3,7 @@ - + main.askai.tui.app_header API documentation @@ -624,6 +624,7 @@

    Inherited Members
    COMPONENT_CLASSES
    BORDER_TITLE
    BORDER_SUBTITLE
    +
    ALLOW_MAXIMIZE
    expand
    shrink
    @@ -650,10 +651,12 @@
    Inherited Members
    visible_siblings
    allow_vertical_scroll
    allow_horizontal_scroll
    +
    allow_maximize
    offset
    opacity
    is_anchored
    is_mouse_over
    +
    is_maximized
    anchor
    clear_anchor
    tooltip
    @@ -781,8 +784,10 @@
    Inherited Members
    textual.dom.DOMNode
    DEFAULT_CLASSES
    +
    BINDING_GROUP_TITLE
    BINDINGS
    SCOPED_CSS
    +
    HELP
    styles
    set_reactive
    mutate_reactive
    @@ -810,6 +815,7 @@
    Inherited Members
    css_tree
    text_style
    rich_style
    +
    check_consume_key
    background_colors
    colors
    ancestors_with_self
    @@ -821,6 +827,7 @@
    Inherited Members
    query
    query_children
    query_one
    +
    query_exactly_one
    set_styles
    has_class
    set_class
    @@ -1003,6 +1010,7 @@
    Inherited Members
    COMPONENT_CLASSES
    BORDER_TITLE
    BORDER_SUBTITLE
    +
    ALLOW_MAXIMIZE
    expand
    shrink
    @@ -1029,10 +1037,12 @@
    Inherited Members
    visible_siblings
    allow_vertical_scroll
    allow_horizontal_scroll
    +
    allow_maximize
    offset
    opacity
    is_anchored
    is_mouse_over
    +
    is_maximized
    anchor
    clear_anchor
    tooltip
    @@ -1160,8 +1170,10 @@
    Inherited Members
    textual.dom.DOMNode
    DEFAULT_CLASSES
    +
    BINDING_GROUP_TITLE
    BINDINGS
    SCOPED_CSS
    +
    HELP
    styles
    set_reactive
    mutate_reactive
    @@ -1189,6 +1201,7 @@
    Inherited Members
    css_tree
    text_style
    rich_style
    +
    check_consume_key
    background_colors
    colors
    ancestors_with_self
    @@ -1200,6 +1213,7 @@
    Inherited Members
    query
    query_children
    query_one
    +
    query_exactly_one
    set_styles
    has_class
    set_class
    @@ -1508,10 +1522,10 @@
    Inherited Members
    -
    600    @property
    -601    def tooltip(self) -> RenderableType | None:
    -602        """Tooltip for the widget, or `None` for no tooltip."""
    -603        return self._tooltip
    +            
    625    @property
    +626    def tooltip(self) -> RenderableType | None:
    +627        """Tooltip for the widget, or `None` for no tooltip."""
    +628        return self._tooltip
     
    @@ -1730,6 +1744,7 @@
    Inherited Members
    COMPONENT_CLASSES
    BORDER_TITLE
    BORDER_SUBTITLE
    +
    ALLOW_MAXIMIZE
    expand
    shrink
    @@ -1756,10 +1771,12 @@
    Inherited Members
    visible_siblings
    allow_vertical_scroll
    allow_horizontal_scroll
    +
    allow_maximize
    offset
    opacity
    is_anchored
    is_mouse_over
    +
    is_maximized
    anchor
    clear_anchor
    allow_focus
    @@ -1886,8 +1903,10 @@
    Inherited Members
    textual.dom.DOMNode
    DEFAULT_CLASSES
    +
    BINDING_GROUP_TITLE
    BINDINGS
    SCOPED_CSS
    +
    HELP
    styles
    set_reactive
    mutate_reactive
    @@ -1915,6 +1934,7 @@
    Inherited Members
    css_tree
    text_style
    rich_style
    +
    check_consume_key
    background_colors
    colors
    ancestors_with_self
    @@ -1926,6 +1946,7 @@
    Inherited Members
    query
    query_children
    query_one
    +
    query_exactly_one
    set_styles
    has_class
    set_class
    diff --git a/docs/api/askai/main/askai/tui/app_icons.html b/docs/api/askai/main/askai/tui/app_icons.html index cfbe0c8b..43f4cbad 100644 --- a/docs/api/askai/main/askai/tui/app_icons.html +++ b/docs/api/askai/main/askai/tui/app_icons.html @@ -3,7 +3,7 @@ - + main.askai.tui.app_icons API documentation @@ -154,7 +154,7 @@

    19class AppIcons(Enumeration): 20 """Enumerated icons of the new AskAI UI application.""" 21 -22 # icons:                      鬒  穀    +22 # icons:                      鬒  穀        23 24 DEFAULT = "" 25 STARTED = "" @@ -196,7 +196,7 @@

    20class AppIcons(Enumeration):
     21    """Enumerated icons of the new AskAI UI application."""
     22
    -23    # icons:                      鬒  穀   
    +23    # icons:                      鬒  穀       
     24
     25    DEFAULT = ""
     26    STARTED = ""
    diff --git a/docs/api/askai/main/askai/tui/app_suggester.html b/docs/api/askai/main/askai/tui/app_suggester.html
    index 0a44ff5c..1bceccab 100644
    --- a/docs/api/askai/main/askai/tui/app_suggester.html
    +++ b/docs/api/askai/main/askai/tui/app_suggester.html
    @@ -3,7 +3,7 @@
     
         
         
    -    
    +    
         main.askai.tui.app_suggester API documentation
     
         
    diff --git a/docs/api/askai/main/askai/tui/app_widgets.html b/docs/api/askai/main/askai/tui/app_widgets.html
    index 9dfd990c..d673d3c0 100644
    --- a/docs/api/askai/main/askai/tui/app_widgets.html
    +++ b/docs/api/askai/main/askai/tui/app_widgets.html
    @@ -3,7 +3,7 @@
     
         
         
    -    
    +    
         main.askai.tui.app_widgets API documentation
     
         
    @@ -465,10 +465,10 @@ 

    -
    600    @property
    -601    def tooltip(self) -> RenderableType | None:
    -602        """Tooltip for the widget, or `None` for no tooltip."""
    -603        return self._tooltip
    +            
    625    @property
    +626    def tooltip(self) -> RenderableType | None:
    +627        """Tooltip for the widget, or `None` for no tooltip."""
    +628        return self._tooltip
     
    @@ -573,6 +573,7 @@
    Inherited Members
    + @@ -599,10 +600,12 @@
    Inherited Members
    + + @@ -729,8 +732,10 @@
    Inherited Members
    textual.dom.DOMNode
    + + @@ -758,6 +763,7 @@
    Inherited Members
    + @@ -769,6 +775,7 @@
    Inherited Members
    + @@ -990,6 +997,7 @@
    Inherited Members
    COMPONENT_CLASSES
    BORDER_TITLE
    BORDER_SUBTITLE
    +
    ALLOW_MAXIMIZE
    expand
    shrink
    @@ -1016,10 +1024,12 @@
    Inherited Members
    visible_siblings
    allow_vertical_scroll
    allow_horizontal_scroll
    +
    allow_maximize
    offset
    opacity
    is_anchored
    is_mouse_over
    +
    is_maximized
    anchor
    clear_anchor
    tooltip
    @@ -1147,8 +1157,10 @@
    Inherited Members
    textual.dom.DOMNode
    DEFAULT_CLASSES
    +
    BINDING_GROUP_TITLE
    BINDINGS
    SCOPED_CSS
    +
    HELP
    styles
    set_reactive
    mutate_reactive
    @@ -1176,6 +1188,7 @@
    Inherited Members
    css_tree
    text_style
    rich_style
    +
    check_consume_key
    background_colors
    colors
    ancestors_with_self
    @@ -1187,6 +1200,7 @@
    Inherited Members
    query
    query_children
    query_one
    +
    query_exactly_one
    set_styles
    has_class
    set_class
    @@ -1366,6 +1380,7 @@
    Inherited Members
    textual.widget.Widget
    BORDER_TITLE
    BORDER_SUBTITLE
    +
    ALLOW_MAXIMIZE
    expand
    shrink
    @@ -1392,10 +1407,12 @@
    Inherited Members
    visible_siblings
    allow_vertical_scroll
    allow_horizontal_scroll
    +
    allow_maximize
    offset
    opacity
    is_anchored
    is_mouse_over
    +
    is_maximized
    anchor
    clear_anchor
    tooltip
    @@ -1524,8 +1541,10 @@
    Inherited Members
    textual.dom.DOMNode
    DEFAULT_CLASSES
    +
    BINDING_GROUP_TITLE
    BINDINGS
    SCOPED_CSS
    +
    HELP
    styles
    set_reactive
    mutate_reactive
    @@ -1553,6 +1572,7 @@
    Inherited Members
    css_tree
    text_style
    rich_style
    +
    check_consume_key
    background_colors
    colors
    ancestors_with_self
    @@ -1564,6 +1584,7 @@
    Inherited Members
    query
    query_children
    query_one
    +
    query_exactly_one
    set_styles
    has_class
    set_class
    @@ -1864,6 +1885,7 @@
    Inherited Members
    COMPONENT_CLASSES
    BORDER_TITLE
    BORDER_SUBTITLE
    +
    ALLOW_MAXIMIZE
    disabled
    hover_style
    @@ -1888,10 +1910,12 @@
    Inherited Members
    visible_siblings
    allow_vertical_scroll
    allow_horizontal_scroll
    +
    allow_maximize
    offset
    opacity
    is_anchored
    is_mouse_over
    +
    is_maximized
    anchor
    clear_anchor
    tooltip
    @@ -2018,8 +2042,10 @@
    Inherited Members
    textual.dom.DOMNode
    DEFAULT_CLASSES
    +
    BINDING_GROUP_TITLE
    BINDINGS
    SCOPED_CSS
    +
    HELP
    styles
    set_reactive
    mutate_reactive
    @@ -2047,6 +2073,7 @@
    Inherited Members
    css_tree
    text_style
    rich_style
    +
    check_consume_key
    background_colors
    colors
    ancestors_with_self
    @@ -2058,6 +2085,7 @@
    Inherited Members
    query
    query_children
    query_one
    +
    query_exactly_one
    set_styles
    has_class
    set_class
    @@ -2332,6 +2360,7 @@
    Inherited Members
    COMPONENT_CLASSES
    BORDER_TITLE
    BORDER_SUBTITLE
    +
    ALLOW_MAXIMIZE
    disabled
    hover_style
    @@ -2356,10 +2385,12 @@
    Inherited Members
    visible_siblings
    allow_vertical_scroll
    allow_horizontal_scroll
    +
    allow_maximize
    offset
    opacity
    is_anchored
    is_mouse_over
    +
    is_maximized
    anchor
    clear_anchor
    tooltip
    @@ -2486,8 +2517,10 @@
    Inherited Members
    textual.dom.DOMNode
    DEFAULT_CLASSES
    +
    BINDING_GROUP_TITLE
    BINDINGS
    SCOPED_CSS
    +
    HELP
    styles
    set_reactive
    mutate_reactive
    @@ -2515,6 +2548,7 @@
    Inherited Members
    css_tree
    text_style
    rich_style
    +
    check_consume_key
    background_colors
    colors
    ancestors_with_self
    @@ -2526,6 +2560,7 @@
    Inherited Members
    query
    query_children
    query_one
    +
    query_exactly_one
    set_styles
    has_class
    set_class
    diff --git a/docs/api/askai/main/askai/tui/askai_app.html b/docs/api/askai/main/askai/tui/askai_app.html index d074e83c..592892d9 100644 --- a/docs/api/askai/main/askai/tui/askai_app.html +++ b/docs/api/askai/main/askai/tui/askai_app.html @@ -3,7 +3,7 @@ - + main.askai.tui.askai_app API documentation @@ -189,43 +189,43 @@

    12 13 Copyright (c) 2024, HomeSetup 14""" - 15import logging as log - 16import os - 17from functools import partial - 18from pathlib import Path - 19 - 20import nltk - 21from hspylib.core.enums.charset import Charset - 22from hspylib.core.tools.commons import file_is_not_empty - 23from hspylib.core.tools.text_tools import ensure_endswith - 24from hspylib.core.zoned_datetime import DATE_FORMAT, now, TIME_FORMAT - 25from hspylib.modules.application.version import Version - 26from hspylib.modules.cli.vt100.vt_color import VtColor - 27from hspylib.modules.eventbus.event import Event - 28from textual import on, work - 29from textual.app import App, ComposeResult - 30from textual.containers import ScrollableContainer - 31from textual.widgets import Footer, Input, MarkdownViewer - 32 - 33from askai.__classpath__ import classpath - 34from askai.core.askai import AskAi - 35from askai.core.askai_configs import configs - 36from askai.core.askai_events import * - 37from askai.core.askai_messages import msg - 38from askai.core.askai_prompt import prompt - 39from askai.core.commander.commander import commander_help - 40from askai.core.component.audio_player import player - 41from askai.core.component.cache_service import cache, CACHE_DIR - 42from askai.core.component.recorder import recorder - 43from askai.core.component.scheduler import scheduler - 44from askai.core.engine.ai_engine import AIEngine - 45from askai.core.enums.router_mode import RouterMode - 46from askai.core.support.shared_instances import shared - 47from askai.core.support.text_formatter import text_formatter - 48from askai.tui.app_header import Header - 49from askai.tui.app_icons import AppIcons - 50from askai.tui.app_suggester import InputSuggester - 51from askai.tui.app_widgets import AppHelp, AppInfo, AppSettings, Splash + 15from askai.__classpath__ import classpath + 16from askai.core.askai import AskAi + 17from askai.core.askai_configs import configs + 18from askai.core.askai_events import * + 19from askai.core.askai_messages import msg + 20from askai.core.askai_prompt import prompt + 21from askai.core.commander.commander import commander_help + 22from askai.core.component.audio_player import player + 23from askai.core.component.cache_service import cache, CACHE_DIR + 24from askai.core.component.recorder import recorder + 25from askai.core.component.scheduler import scheduler + 26from askai.core.engine.ai_engine import AIEngine + 27from askai.core.enums.router_mode import RouterMode + 28from askai.core.model.ai_reply import AIReply + 29from askai.core.support.shared_instances import shared + 30from askai.core.support.text_formatter import text_formatter + 31from askai.tui.app_header import Header + 32from askai.tui.app_icons import AppIcons + 33from askai.tui.app_suggester import InputSuggester + 34from askai.tui.app_widgets import AppHelp, AppInfo, AppSettings, Splash + 35from hspylib.core.enums.charset import Charset + 36from hspylib.core.tools.commons import file_is_not_empty + 37from hspylib.core.tools.text_tools import ensure_endswith + 38from hspylib.core.zoned_datetime import DATE_FORMAT, now, TIME_FORMAT + 39from hspylib.modules.application.version import Version + 40from hspylib.modules.cli.vt100.vt_color import VtColor + 41from hspylib.modules.eventbus.event import Event + 42from pathlib import Path + 43from textual import on, work + 44from textual.app import App, ComposeResult + 45from textual.containers import ScrollableContainer + 46from textual.widgets import Footer, Input, MarkdownViewer + 47from typing import Optional + 48 + 49import logging as log + 50import nltk + 51import os 52 53SOURCE_DIR: Path = classpath.source_path() 54 @@ -365,197 +365,214 @@

    188 self.md_console.show_table_of_contents = not self.md_console.show_table_of_contents 189 190 def check_action(self, action: str, _) -> Optional[bool]: -191 """Check if certain actions can be performed.""" -192 if action == "forward" and self.md_console.navigator.end: -193 # Disable footer link if we can't go forward -194 return None -195 if action == "back" and self.md_console.navigator.start: -196 # Disable footer link if we can't go backward -197 return None -198 # All other keys display as normal -199 return True -200 -201 def enable_controls(self, enable: bool = True) -> None: -202 """Enable all UI controls (header, input and footer).""" -203 self.header.disabled = not enable -204 self.line_input.loading = not enable -205 self.footer.disabled = not enable -206 -207 def activate_markdown(self) -> None: -208 """Activate the Markdown console.""" -209 self.md_console.go(self.console_path) -210 self.md_console.set_class(False, "-hidden") -211 self.md_console.scroll_end(animate=False) +191 """Check if a specific action can be performed. +192 :param action: The name of the action to check. +193 :param _: Unused parameter, typically a placeholder for additional context. +194 :return: True if the action can be performed, None if it cannot. +195 """ +196 if action == "forward" and self.md_console.navigator.end: +197 # Disable footer link if we can't go forward +198 return None +199 if action == "back" and self.md_console.navigator.start: +200 # Disable footer link if we can't go backward +201 return None +202 # All other keys display as normal +203 return True +204 +205 def enable_controls(self, enable: bool = True) -> None: +206 """Enable or disable all UI controls, including the header, input, and footer. +207 :param enable: Whether to enable (True) or disable (False) the UI controls (default is True). +208 """ +209 self.header.disabled = not enable +210 self.line_input.loading = not enable +211 self.footer.disabled = not enable 212 -213 def action_clear(self, overwrite: bool = True) -> None: -214 """Clear the output console.""" -215 is_new: bool = not file_is_not_empty(str(self.console_path)) or overwrite -216 with open(self.console_path, "w" if overwrite else "a", encoding=Charset.UTF_8.val) as f_console: -217 f_console.write( -218 f"{'---' + os.linesep * 2 if not is_new else ''}" -219 f"{'# ' + now(DATE_FORMAT) + os.linesep * 2 if is_new else ''}" -220 f"## {AppIcons.STARTED} {now(TIME_FORMAT)}\n\n" -221 ) -222 f_console.flush() -223 self._re_render = True -224 self._reply(f"{msg.welcome(prompt.user.title())}") -225 -226 async def action_speaking(self) -> None: -227 """Toggle Speaking ON/OFF.""" -228 self.ask_and_reply("/speak") -229 -230 async def action_debugging(self) -> None: -231 """Toggle Debugging ON/OFF.""" -232 self.ask_and_reply("/debug") +213 def activate_markdown(self) -> None: +214 """Activate the markdown console widget.""" +215 self.md_console.go(self.console_path) +216 self.md_console.set_class(False, "-hidden") +217 self.md_console.scroll_end(animate=False) +218 +219 def action_clear(self, overwrite: bool = True) -> None: +220 """Clear the output console. +221 :param overwrite: Whether to overwrite the existing content in the console (default is True). +222 """ +223 is_new: bool = not file_is_not_empty(str(self.console_path)) or overwrite +224 with open(self.console_path, "w" if overwrite else "a", encoding=Charset.UTF_8.val) as f_console: +225 f_console.write( +226 f"{'---' + os.linesep * 2 if not is_new else ''}" +227 f"{'# ' + now(DATE_FORMAT) + os.linesep * 2 if is_new else ''}" +228 f"## {AppIcons.STARTED} {now(TIME_FORMAT)}\n\n" +229 ) +230 f_console.flush() +231 self._re_render = True +232 self._reply(AIReply.info(f"{msg.welcome(prompt.user.title())}")) 233 -234 @work(thread=True, exclusive=True) -235 async def action_ptt(self) -> None: -236 """Push-To-Talk STT as input method.""" -237 self.enable_controls(False) -238 if spoken_text := self.engine.speech_to_text(): -239 self.display_text(f"{shared.username_md}: {spoken_text}") -240 if self.ask_and_reply(spoken_text): -241 await self.suggester.add_suggestion(spoken_text) -242 suggestions = await self.suggester.suggestions() -243 cache.save_input_history(suggestions) -244 self.enable_controls() -245 -246 @on(Input.Submitted) -247 async def on_submit(self, submitted: Input.Submitted) -> None: -248 """A coroutine to handle a input submission.""" -249 question: str = submitted.value -250 self.line_input.clear() -251 self.display_text(f"{shared.username_md}: {question}") -252 if self.ask_and_reply(question): -253 await self.suggester.add_suggestion(question) -254 suggestions = await self.suggester.suggestions() -255 cache.save_input_history(suggestions) -256 -257 async def _write_markdown(self) -> None: -258 """Write buffered text to the markdown file.""" -259 if len(self._display_buffer) > 0: -260 with open(self.console_path, "a", encoding=Charset.UTF_8.val) as f_console: -261 prev_text: str | None = None -262 while len(self._display_buffer) > 0: -263 if (text := self._display_buffer.pop(0)) == prev_text: -264 continue -265 prev_text = text -266 final_text: str = text_formatter.beautify(f"{ensure_endswith(text, os.linesep * 2)}") -267 f_console.write(final_text) -268 f_console.flush() -269 self._re_render = True -270 -271 async def _cb_refresh_console(self) -> None: -272 """Callback to handle markdown console updates.""" -273 if not self.console_path.exists(): -274 self.action_clear() -275 await self._write_markdown() -276 if self._re_render: -277 self._re_render = False -278 await self.md_console.go(self.console_path) -279 self.md_console.scroll_end(animate=False) -280 -281 def _reply(self, message: str) -> None: -282 """Reply to the user with the AI response. -283 :param message: The reply message to be displayed. -284 """ -285 prev_msg: str = self._display_buffer[-1] if self._display_buffer else "" -286 if message and prev_msg != message: -287 log.debug(message) -288 self.display_text(f"{shared.nickname_md} {message}") -289 if configs.is_speak: -290 self.engine.text_to_speech(message, f"{shared.nickname_md} ") -291 -292 def _reply_error(self, message: str) -> None: -293 """Reply API or system errors. -294 :param message: The error message to be displayed. -295 """ -296 prev_msg: str = self._display_buffer[-1] if self._display_buffer else "" -297 if message and prev_msg != message: -298 log.error(message) -299 self.display_text(f"{shared.nickname_md} Error: {message}") -300 if configs.is_speak: -301 self.engine.text_to_speech(f"Error: {message}", f"{shared.nickname_md} ") -302 -303 def display_text(self, markdown_text: str) -> None: -304 """Send the text to the Markdown console. -305 :param markdown_text: the text to be displayed. -306 """ -307 self._display_buffer.append(msg.translate(markdown_text)) -308 -309 def _cb_reply_event(self, ev: Event, error: bool = False) -> None: -310 """Callback to handle reply events. -311 :param ev: The reply event. -312 :param error: Whether the event is an error not not. -313 """ -314 if message := ev.args.message: -315 if error: -316 self._reply_error(message) -317 else: -318 if ev.args.verbosity.casefold() == "normal" or configs.is_debug: -319 self._reply(message) -320 -321 def _cb_mic_listening_event(self, ev: Event) -> None: -322 """Callback to handle microphone listening events. -323 :param ev: The microphone listening event. -324 """ -325 self.header.notifications.listening = ev.args.listening -326 if ev.args.listening: -327 self._reply(msg.listening()) -328 -329 def _cb_device_changed_event(self, ev: Event) -> None: -330 """Callback to handle audio input device changed events. -331 :param ev: The device changed event. -332 """ -333 self._reply(msg.device_switch(str(ev.args.device))) -334 -335 def _cb_mode_changed_event(self, ev: Event) -> None: -336 """Callback to handle mode changed events. -337 :param ev: The mode changed event. -338 """ -339 self._mode: RouterMode = RouterMode.of_name(ev.args.mode) -340 if not self._mode.is_default: -341 self._reply( -342 f"{msg.enter_qna()} \n" -343 f"```\nContext:  {ev.args.sum_path},  {ev.args.glob} \n```\n" -344 f"`{msg.press_esc_enter()}` \n\n" -345 f"> {msg.qna_welcome()}" -346 ) -347 -348 @work(thread=True, exclusive=True) -349 def ask_and_reply(self, question: str) -> tuple[bool, Optional[str]]: -350 """Ask the question to the AI, and provide the reply. -351 :param question: The question to ask to the AI engine. -352 """ -353 self.enable_controls(False) -354 status, reply = self.askai.ask_and_reply(question) -355 self.enable_controls() -356 -357 return status, reply +234 async def action_speaking(self) -> None: +235 """Toggle Speaking ON/OFF.""" +236 self.ask_and_reply("/speak") +237 +238 async def action_debugging(self) -> None: +239 """Toggle Debugging ON/OFF.""" +240 self.ask_and_reply("/debug") +241 +242 @work(thread=True, exclusive=True) +243 async def action_ptt(self) -> None: +244 """Handle the Push-To-Talk (PTT) action for Speech-To-Text (STT) input. This method allows the user to use +245 Push-To-Talk as an input method, converting spoken words into text. +246 """ +247 self.enable_controls(False) +248 if spoken_text := self.engine.speech_to_text(): +249 self.display_text(f"{shared.username_md}: {spoken_text}") +250 if self.ask_and_reply(spoken_text): +251 await self.suggester.add_suggestion(spoken_text) +252 suggestions = await self.suggester.suggestions() +253 cache.save_input_history(suggestions) +254 self.enable_controls() +255 +256 @on(Input.Submitted) +257 async def on_submit(self, submitted: Input.Submitted) -> None: +258 """A coroutine to handle input submission events. +259 :param submitted: The event that contains the submitted data. +260 """ +261 question: str = submitted.value +262 self.line_input.clear() +263 self.display_text(f"{shared.username_md}: {question}") +264 if self.ask_and_reply(question): +265 await self.suggester.add_suggestion(question) +266 suggestions = await self.suggester.suggestions() +267 cache.save_input_history(suggestions) +268 +269 async def _write_markdown(self) -> None: +270 """Write buffered text to the markdown file. +271 This method handles writing any accumulated or buffered text to a markdown file. +272 """ +273 if len(self._display_buffer) > 0: +274 with open(self.console_path, "a", encoding=Charset.UTF_8.val) as f_console: +275 prev_text: str | None = None +276 while len(self._display_buffer) > 0: +277 if (text := self._display_buffer.pop(0)) == prev_text: +278 continue +279 prev_text = text +280 final_text: str = text_formatter.beautify(f"{ensure_endswith(text, os.linesep * 2)}") +281 f_console.write(final_text) +282 f_console.flush() +283 self._re_render = True +284 +285 async def _cb_refresh_console(self) -> None: +286 """Callback to handle markdown console updates. +287 This method is responsible for refreshing or updating the console display in markdown format. +288 """ +289 if not self.console_path.exists(): +290 self.action_clear() +291 await self._write_markdown() +292 if self._re_render: +293 self._re_render = False +294 await self.md_console.go(self.console_path) +295 self.md_console.scroll_end(animate=False) +296 +297 def _reply(self, reply: AIReply) -> None: +298 """Reply to the user with the AI-generated response. +299 :param reply: The reply message to send as a reply to the user. +300 """ +301 prev_msg: str = self._display_buffer[-1] if self._display_buffer else "" +302 if reply and prev_msg != reply.message: +303 log.debug(reply.message) +304 self.display_text(f"{shared.nickname_md} {reply.message}") +305 if configs.is_speak and reply.is_speakable: +306 self.engine.text_to_speech(reply.message, f"{shared.nickname_md} ") +307 +308 def _reply_error(self, reply: AIReply) -> None: +309 """Reply to the user with an AI-generated error message or system error. +310 :param reply: The error reply message to be displayed to the user. +311 """ +312 prev_msg: str = self._display_buffer[-1] if self._display_buffer else "" +313 if reply and prev_msg != reply.message: +314 log.error(reply.message) +315 self.display_text(f"{shared.nickname_md} Error: {reply.message}") +316 if configs.is_speak and reply.is_speakable: +317 self.engine.text_to_speech(f"Error: {reply.message}", f"{shared.nickname_md} ") +318 +319 def display_text(self, markdown_text: str) -> None: +320 """Send the text to the Markdown console. +321 :param markdown_text: the text to be displayed. +322 """ +323 self._display_buffer.append(msg.translate(markdown_text)) +324 +325 def _cb_reply_event(self, ev: Event) -> None: +326 """Callback to handle reply events. +327 :param ev: The event object representing the reply event. +328 """ +329 reply: AIReply +330 if reply := ev.args.reply: +331 if reply.is_error: +332 self._reply_error(reply) +333 else: +334 if ev.args.reply.verbosity.match(configs.verbosity) or configs.is_debug: +335 self._reply(reply) +336 +337 def _cb_mode_changed_event(self, ev: Event) -> None: +338 """Callback to handle mode change events. +339 :param ev: The event object representing the mode change. +340 """ +341 self._mode: RouterMode = RouterMode.of_name(ev.args.mode) +342 if not self._mode.is_default: +343 sum_msg: str = ( +344 f"{msg.enter_qna()} \n" +345 f"```\nContext:  {ev.args.sum_path},  {ev.args.glob} \n```\n" +346 f"`{msg.press_esc_enter()}` \n\n" +347 f"> {msg.qna_welcome()}" +348 ) +349 events.reply.emit(reply=AIReply.info(sum_msg)) +350 +351 def _cb_mic_listening_event(self, ev: Event) -> None: +352 """Callback to handle microphone listening events. +353 :param ev: The event object representing the microphone listening event. +354 """ +355 self.header.notifications.listening = ev.args.listening +356 if ev.args.listening: +357 self._reply(AIReply.info(msg.listening())) 358 -359 def _startup(self) -> None: -360 """Initialize the application.""" -361 os.chdir(Path.home()) -362 askai_bus = AskAiEvents.bus(ASKAI_BUS_NAME) -363 askai_bus.subscribe(REPLY_EVENT, self._cb_reply_event) -364 askai_bus.subscribe(REPLY_ERROR_EVENT, partial(self._cb_reply_event, error=True)) -365 nltk.download("averaged_perceptron_tagger", quiet=True, download_dir=CACHE_DIR) -366 recorder.setup() -367 scheduler.start() -368 askai_bus.subscribe(MIC_LISTENING_EVENT, self._cb_mic_listening_event) -369 askai_bus.subscribe(DEVICE_CHANGED_EVENT, self._cb_device_changed_event) -370 askai_bus.subscribe(MODE_CHANGED_EVENT, self._cb_mode_changed_event) -371 log.info("AskAI is ready to use!") -372 -373 @work(thread=True, exclusive=True) -374 def _setup(self) -> None: -375 """Setup the TUI controls.""" -376 player.start_delay() -377 self.splash.set_class(True, "-hidden") -378 self.activate_markdown() -379 self.action_clear(overwrite=False) -380 self.enable_controls() -381 self.line_input.focus(False) +359 def _cb_device_changed_event(self, ev: Event) -> None: +360 """Callback to handle audio input device change events. +361 :param ev: The event object representing the device change. +362 """ +363 self._reply(AIReply.info(msg.device_switch(str(ev.args.device)))) +364 +365 @work(thread=True, exclusive=True) +366 def ask_and_reply(self, question: str) -> tuple[bool, Optional[str]]: +367 """Ask the specified question to the AI and provide the reply. +368 :param question: The question to ask the AI engine. +369 :return: A tuple containing a boolean indicating success or failure, and the AI's reply as an optional string. +370 """ +371 self.enable_controls(False) +372 status, reply = self.askai.ask_and_reply(question) +373 self.enable_controls() +374 +375 return status, reply +376 +377 def _startup(self) -> None: +378 """Initialize the application components.""" +379 os.chdir(Path.home()) +380 askai_bus = AskAiEvents.bus(ASKAI_BUS_NAME) +381 askai_bus.subscribe(REPLY_EVENT, self._cb_reply_event) +382 nltk.download("averaged_perceptron_tagger", quiet=True, download_dir=CACHE_DIR) +383 recorder.setup() +384 scheduler.start() +385 askai_bus.subscribe(MIC_LISTENING_EVENT, self._cb_mic_listening_event) +386 askai_bus.subscribe(DEVICE_CHANGED_EVENT, self._cb_device_changed_event) +387 askai_bus.subscribe(MODE_CHANGED_EVENT, self._cb_mode_changed_event) +388 log.info("AskAI is ready to use!") +389 +390 @work(thread=True, exclusive=True) +391 def _setup(self) -> None: +392 """Setup the TUI controls.""" +393 player.start_delay() +394 self.splash.set_class(True, "-hidden") +395 self.activate_markdown() +396 self.action_clear(overwrite=False) +397 self.enable_controls() +398 self.line_input.focus(False)

    @@ -728,197 +745,214 @@

    189 self.md_console.show_table_of_contents = not self.md_console.show_table_of_contents 190 191 def check_action(self, action: str, _) -> Optional[bool]: -192 """Check if certain actions can be performed.""" -193 if action == "forward" and self.md_console.navigator.end: -194 # Disable footer link if we can't go forward -195 return None -196 if action == "back" and self.md_console.navigator.start: -197 # Disable footer link if we can't go backward -198 return None -199 # All other keys display as normal -200 return True -201 -202 def enable_controls(self, enable: bool = True) -> None: -203 """Enable all UI controls (header, input and footer).""" -204 self.header.disabled = not enable -205 self.line_input.loading = not enable -206 self.footer.disabled = not enable -207 -208 def activate_markdown(self) -> None: -209 """Activate the Markdown console.""" -210 self.md_console.go(self.console_path) -211 self.md_console.set_class(False, "-hidden") -212 self.md_console.scroll_end(animate=False) +192 """Check if a specific action can be performed. +193 :param action: The name of the action to check. +194 :param _: Unused parameter, typically a placeholder for additional context. +195 :return: True if the action can be performed, None if it cannot. +196 """ +197 if action == "forward" and self.md_console.navigator.end: +198 # Disable footer link if we can't go forward +199 return None +200 if action == "back" and self.md_console.navigator.start: +201 # Disable footer link if we can't go backward +202 return None +203 # All other keys display as normal +204 return True +205 +206 def enable_controls(self, enable: bool = True) -> None: +207 """Enable or disable all UI controls, including the header, input, and footer. +208 :param enable: Whether to enable (True) or disable (False) the UI controls (default is True). +209 """ +210 self.header.disabled = not enable +211 self.line_input.loading = not enable +212 self.footer.disabled = not enable 213 -214 def action_clear(self, overwrite: bool = True) -> None: -215 """Clear the output console.""" -216 is_new: bool = not file_is_not_empty(str(self.console_path)) or overwrite -217 with open(self.console_path, "w" if overwrite else "a", encoding=Charset.UTF_8.val) as f_console: -218 f_console.write( -219 f"{'---' + os.linesep * 2 if not is_new else ''}" -220 f"{'# ' + now(DATE_FORMAT) + os.linesep * 2 if is_new else ''}" -221 f"## {AppIcons.STARTED} {now(TIME_FORMAT)}\n\n" -222 ) -223 f_console.flush() -224 self._re_render = True -225 self._reply(f"{msg.welcome(prompt.user.title())}") -226 -227 async def action_speaking(self) -> None: -228 """Toggle Speaking ON/OFF.""" -229 self.ask_and_reply("/speak") -230 -231 async def action_debugging(self) -> None: -232 """Toggle Debugging ON/OFF.""" -233 self.ask_and_reply("/debug") +214 def activate_markdown(self) -> None: +215 """Activate the markdown console widget.""" +216 self.md_console.go(self.console_path) +217 self.md_console.set_class(False, "-hidden") +218 self.md_console.scroll_end(animate=False) +219 +220 def action_clear(self, overwrite: bool = True) -> None: +221 """Clear the output console. +222 :param overwrite: Whether to overwrite the existing content in the console (default is True). +223 """ +224 is_new: bool = not file_is_not_empty(str(self.console_path)) or overwrite +225 with open(self.console_path, "w" if overwrite else "a", encoding=Charset.UTF_8.val) as f_console: +226 f_console.write( +227 f"{'---' + os.linesep * 2 if not is_new else ''}" +228 f"{'# ' + now(DATE_FORMAT) + os.linesep * 2 if is_new else ''}" +229 f"## {AppIcons.STARTED} {now(TIME_FORMAT)}\n\n" +230 ) +231 f_console.flush() +232 self._re_render = True +233 self._reply(AIReply.info(f"{msg.welcome(prompt.user.title())}")) 234 -235 @work(thread=True, exclusive=True) -236 async def action_ptt(self) -> None: -237 """Push-To-Talk STT as input method.""" -238 self.enable_controls(False) -239 if spoken_text := self.engine.speech_to_text(): -240 self.display_text(f"{shared.username_md}: {spoken_text}") -241 if self.ask_and_reply(spoken_text): -242 await self.suggester.add_suggestion(spoken_text) -243 suggestions = await self.suggester.suggestions() -244 cache.save_input_history(suggestions) -245 self.enable_controls() -246 -247 @on(Input.Submitted) -248 async def on_submit(self, submitted: Input.Submitted) -> None: -249 """A coroutine to handle a input submission.""" -250 question: str = submitted.value -251 self.line_input.clear() -252 self.display_text(f"{shared.username_md}: {question}") -253 if self.ask_and_reply(question): -254 await self.suggester.add_suggestion(question) -255 suggestions = await self.suggester.suggestions() -256 cache.save_input_history(suggestions) -257 -258 async def _write_markdown(self) -> None: -259 """Write buffered text to the markdown file.""" -260 if len(self._display_buffer) > 0: -261 with open(self.console_path, "a", encoding=Charset.UTF_8.val) as f_console: -262 prev_text: str | None = None -263 while len(self._display_buffer) > 0: -264 if (text := self._display_buffer.pop(0)) == prev_text: -265 continue -266 prev_text = text -267 final_text: str = text_formatter.beautify(f"{ensure_endswith(text, os.linesep * 2)}") -268 f_console.write(final_text) -269 f_console.flush() -270 self._re_render = True -271 -272 async def _cb_refresh_console(self) -> None: -273 """Callback to handle markdown console updates.""" -274 if not self.console_path.exists(): -275 self.action_clear() -276 await self._write_markdown() -277 if self._re_render: -278 self._re_render = False -279 await self.md_console.go(self.console_path) -280 self.md_console.scroll_end(animate=False) -281 -282 def _reply(self, message: str) -> None: -283 """Reply to the user with the AI response. -284 :param message: The reply message to be displayed. -285 """ -286 prev_msg: str = self._display_buffer[-1] if self._display_buffer else "" -287 if message and prev_msg != message: -288 log.debug(message) -289 self.display_text(f"{shared.nickname_md} {message}") -290 if configs.is_speak: -291 self.engine.text_to_speech(message, f"{shared.nickname_md} ") -292 -293 def _reply_error(self, message: str) -> None: -294 """Reply API or system errors. -295 :param message: The error message to be displayed. -296 """ -297 prev_msg: str = self._display_buffer[-1] if self._display_buffer else "" -298 if message and prev_msg != message: -299 log.error(message) -300 self.display_text(f"{shared.nickname_md} Error: {message}") -301 if configs.is_speak: -302 self.engine.text_to_speech(f"Error: {message}", f"{shared.nickname_md} ") -303 -304 def display_text(self, markdown_text: str) -> None: -305 """Send the text to the Markdown console. -306 :param markdown_text: the text to be displayed. -307 """ -308 self._display_buffer.append(msg.translate(markdown_text)) -309 -310 def _cb_reply_event(self, ev: Event, error: bool = False) -> None: -311 """Callback to handle reply events. -312 :param ev: The reply event. -313 :param error: Whether the event is an error not not. -314 """ -315 if message := ev.args.message: -316 if error: -317 self._reply_error(message) -318 else: -319 if ev.args.verbosity.casefold() == "normal" or configs.is_debug: -320 self._reply(message) -321 -322 def _cb_mic_listening_event(self, ev: Event) -> None: -323 """Callback to handle microphone listening events. -324 :param ev: The microphone listening event. -325 """ -326 self.header.notifications.listening = ev.args.listening -327 if ev.args.listening: -328 self._reply(msg.listening()) -329 -330 def _cb_device_changed_event(self, ev: Event) -> None: -331 """Callback to handle audio input device changed events. -332 :param ev: The device changed event. -333 """ -334 self._reply(msg.device_switch(str(ev.args.device))) -335 -336 def _cb_mode_changed_event(self, ev: Event) -> None: -337 """Callback to handle mode changed events. -338 :param ev: The mode changed event. -339 """ -340 self._mode: RouterMode = RouterMode.of_name(ev.args.mode) -341 if not self._mode.is_default: -342 self._reply( -343 f"{msg.enter_qna()} \n" -344 f"```\nContext:  {ev.args.sum_path},  {ev.args.glob} \n```\n" -345 f"`{msg.press_esc_enter()}` \n\n" -346 f"> {msg.qna_welcome()}" -347 ) -348 -349 @work(thread=True, exclusive=True) -350 def ask_and_reply(self, question: str) -> tuple[bool, Optional[str]]: -351 """Ask the question to the AI, and provide the reply. -352 :param question: The question to ask to the AI engine. -353 """ -354 self.enable_controls(False) -355 status, reply = self.askai.ask_and_reply(question) -356 self.enable_controls() -357 -358 return status, reply +235 async def action_speaking(self) -> None: +236 """Toggle Speaking ON/OFF.""" +237 self.ask_and_reply("/speak") +238 +239 async def action_debugging(self) -> None: +240 """Toggle Debugging ON/OFF.""" +241 self.ask_and_reply("/debug") +242 +243 @work(thread=True, exclusive=True) +244 async def action_ptt(self) -> None: +245 """Handle the Push-To-Talk (PTT) action for Speech-To-Text (STT) input. This method allows the user to use +246 Push-To-Talk as an input method, converting spoken words into text. +247 """ +248 self.enable_controls(False) +249 if spoken_text := self.engine.speech_to_text(): +250 self.display_text(f"{shared.username_md}: {spoken_text}") +251 if self.ask_and_reply(spoken_text): +252 await self.suggester.add_suggestion(spoken_text) +253 suggestions = await self.suggester.suggestions() +254 cache.save_input_history(suggestions) +255 self.enable_controls() +256 +257 @on(Input.Submitted) +258 async def on_submit(self, submitted: Input.Submitted) -> None: +259 """A coroutine to handle input submission events. +260 :param submitted: The event that contains the submitted data. +261 """ +262 question: str = submitted.value +263 self.line_input.clear() +264 self.display_text(f"{shared.username_md}: {question}") +265 if self.ask_and_reply(question): +266 await self.suggester.add_suggestion(question) +267 suggestions = await self.suggester.suggestions() +268 cache.save_input_history(suggestions) +269 +270 async def _write_markdown(self) -> None: +271 """Write buffered text to the markdown file. +272 This method handles writing any accumulated or buffered text to a markdown file. +273 """ +274 if len(self._display_buffer) > 0: +275 with open(self.console_path, "a", encoding=Charset.UTF_8.val) as f_console: +276 prev_text: str | None = None +277 while len(self._display_buffer) > 0: +278 if (text := self._display_buffer.pop(0)) == prev_text: +279 continue +280 prev_text = text +281 final_text: str = text_formatter.beautify(f"{ensure_endswith(text, os.linesep * 2)}") +282 f_console.write(final_text) +283 f_console.flush() +284 self._re_render = True +285 +286 async def _cb_refresh_console(self) -> None: +287 """Callback to handle markdown console updates. +288 This method is responsible for refreshing or updating the console display in markdown format. +289 """ +290 if not self.console_path.exists(): +291 self.action_clear() +292 await self._write_markdown() +293 if self._re_render: +294 self._re_render = False +295 await self.md_console.go(self.console_path) +296 self.md_console.scroll_end(animate=False) +297 +298 def _reply(self, reply: AIReply) -> None: +299 """Reply to the user with the AI-generated response. +300 :param reply: The reply message to send as a reply to the user. +301 """ +302 prev_msg: str = self._display_buffer[-1] if self._display_buffer else "" +303 if reply and prev_msg != reply.message: +304 log.debug(reply.message) +305 self.display_text(f"{shared.nickname_md} {reply.message}") +306 if configs.is_speak and reply.is_speakable: +307 self.engine.text_to_speech(reply.message, f"{shared.nickname_md} ") +308 +309 def _reply_error(self, reply: AIReply) -> None: +310 """Reply to the user with an AI-generated error message or system error. +311 :param reply: The error reply message to be displayed to the user. +312 """ +313 prev_msg: str = self._display_buffer[-1] if self._display_buffer else "" +314 if reply and prev_msg != reply.message: +315 log.error(reply.message) +316 self.display_text(f"{shared.nickname_md} Error: {reply.message}") +317 if configs.is_speak and reply.is_speakable: +318 self.engine.text_to_speech(f"Error: {reply.message}", f"{shared.nickname_md} ") +319 +320 def display_text(self, markdown_text: str) -> None: +321 """Send the text to the Markdown console. +322 :param markdown_text: the text to be displayed. +323 """ +324 self._display_buffer.append(msg.translate(markdown_text)) +325 +326 def _cb_reply_event(self, ev: Event) -> None: +327 """Callback to handle reply events. +328 :param ev: The event object representing the reply event. +329 """ +330 reply: AIReply +331 if reply := ev.args.reply: +332 if reply.is_error: +333 self._reply_error(reply) +334 else: +335 if ev.args.reply.verbosity.match(configs.verbosity) or configs.is_debug: +336 self._reply(reply) +337 +338 def _cb_mode_changed_event(self, ev: Event) -> None: +339 """Callback to handle mode change events. +340 :param ev: The event object representing the mode change. +341 """ +342 self._mode: RouterMode = RouterMode.of_name(ev.args.mode) +343 if not self._mode.is_default: +344 sum_msg: str = ( +345 f"{msg.enter_qna()} \n" +346 f"```\nContext:  {ev.args.sum_path},  {ev.args.glob} \n```\n" +347 f"`{msg.press_esc_enter()}` \n\n" +348 f"> {msg.qna_welcome()}" +349 ) +350 events.reply.emit(reply=AIReply.info(sum_msg)) +351 +352 def _cb_mic_listening_event(self, ev: Event) -> None: +353 """Callback to handle microphone listening events. +354 :param ev: The event object representing the microphone listening event. +355 """ +356 self.header.notifications.listening = ev.args.listening +357 if ev.args.listening: +358 self._reply(AIReply.info(msg.listening())) 359 -360 def _startup(self) -> None: -361 """Initialize the application.""" -362 os.chdir(Path.home()) -363 askai_bus = AskAiEvents.bus(ASKAI_BUS_NAME) -364 askai_bus.subscribe(REPLY_EVENT, self._cb_reply_event) -365 askai_bus.subscribe(REPLY_ERROR_EVENT, partial(self._cb_reply_event, error=True)) -366 nltk.download("averaged_perceptron_tagger", quiet=True, download_dir=CACHE_DIR) -367 recorder.setup() -368 scheduler.start() -369 askai_bus.subscribe(MIC_LISTENING_EVENT, self._cb_mic_listening_event) -370 askai_bus.subscribe(DEVICE_CHANGED_EVENT, self._cb_device_changed_event) -371 askai_bus.subscribe(MODE_CHANGED_EVENT, self._cb_mode_changed_event) -372 log.info("AskAI is ready to use!") -373 -374 @work(thread=True, exclusive=True) -375 def _setup(self) -> None: -376 """Setup the TUI controls.""" -377 player.start_delay() -378 self.splash.set_class(True, "-hidden") -379 self.activate_markdown() -380 self.action_clear(overwrite=False) -381 self.enable_controls() -382 self.line_input.focus(False) +360 def _cb_device_changed_event(self, ev: Event) -> None: +361 """Callback to handle audio input device change events. +362 :param ev: The event object representing the device change. +363 """ +364 self._reply(AIReply.info(msg.device_switch(str(ev.args.device)))) +365 +366 @work(thread=True, exclusive=True) +367 def ask_and_reply(self, question: str) -> tuple[bool, Optional[str]]: +368 """Ask the specified question to the AI and provide the reply. +369 :param question: The question to ask the AI engine. +370 :return: A tuple containing a boolean indicating success or failure, and the AI's reply as an optional string. +371 """ +372 self.enable_controls(False) +373 status, reply = self.askai.ask_and_reply(question) +374 self.enable_controls() +375 +376 return status, reply +377 +378 def _startup(self) -> None: +379 """Initialize the application components.""" +380 os.chdir(Path.home()) +381 askai_bus = AskAiEvents.bus(ASKAI_BUS_NAME) +382 askai_bus.subscribe(REPLY_EVENT, self._cb_reply_event) +383 nltk.download("averaged_perceptron_tagger", quiet=True, download_dir=CACHE_DIR) +384 recorder.setup() +385 scheduler.start() +386 askai_bus.subscribe(MIC_LISTENING_EVENT, self._cb_mic_listening_event) +387 askai_bus.subscribe(DEVICE_CHANGED_EVENT, self._cb_device_changed_event) +388 askai_bus.subscribe(MODE_CHANGED_EVENT, self._cb_mode_changed_event) +389 log.info("AskAI is ready to use!") +390 +391 @work(thread=True, exclusive=True) +392 def _setup(self) -> None: +393 """Setup the TUI controls.""" +394 player.start_delay() +395 self.splash.set_class(True, "-hidden") +396 self.activate_markdown() +397 self.action_clear(overwrite=False) +398 self.enable_controls() +399 self.line_input.focus(False)

    @@ -965,7 +999,7 @@

    APP_TITLE: str = -'AskAI v1.0.11' +'AskAI v1.0.13'
    @@ -998,7 +1032,9 @@

    - +

    The default key bindings.

    +
    +

    @@ -1438,19 +1474,36 @@

    191    def check_action(self, action: str, _) -> Optional[bool]:
    -192        """Check if certain actions can be performed."""
    -193        if action == "forward" and self.md_console.navigator.end:
    -194            # Disable footer link if we can't go forward
    -195            return None
    -196        if action == "back" and self.md_console.navigator.start:
    -197            # Disable footer link if we can't go backward
    -198            return None
    -199        # All other keys display as normal
    -200        return True
    +192        """Check if a specific action can be performed.
    +193        :param action: The name of the action to check.
    +194        :param _: Unused parameter, typically a placeholder for additional context.
    +195        :return: True if the action can be performed, None if it cannot.
    +196        """
    +197        if action == "forward" and self.md_console.navigator.end:
    +198            # Disable footer link if we can't go forward
    +199            return None
    +200        if action == "back" and self.md_console.navigator.start:
    +201            # Disable footer link if we can't go backward
    +202            return None
    +203        # All other keys display as normal
    +204        return True
     
    -

    Check if certain actions can be performed.

    +

    Check if a specific action can be performed.

    + +
    Parameters
    + +
      +
    • action: The name of the action to check.
    • +
    • _: Unused parameter, typically a placeholder for additional context.
    • +
    + +
    Returns
    + +
    +

    True if the action can be performed, None if it cannot.

    +
    @@ -1466,15 +1519,23 @@

    -
    202    def enable_controls(self, enable: bool = True) -> None:
    -203        """Enable all UI controls (header, input and footer)."""
    -204        self.header.disabled = not enable
    -205        self.line_input.loading = not enable
    -206        self.footer.disabled = not enable
    +            
    206    def enable_controls(self, enable: bool = True) -> None:
    +207        """Enable or disable all UI controls, including the header, input, and footer.
    +208        :param enable: Whether to enable (True) or disable (False) the UI controls (default is True).
    +209        """
    +210        self.header.disabled = not enable
    +211        self.line_input.loading = not enable
    +212        self.footer.disabled = not enable
     
    -

    Enable all UI controls (header, input and footer).

    +

    Enable or disable all UI controls, including the header, input, and footer.

    + +
    Parameters
    + +
      +
    • enable: Whether to enable (True) or disable (False) the UI controls (default is True).
    • +
    @@ -1490,15 +1551,15 @@

    -
    208    def activate_markdown(self) -> None:
    -209        """Activate the Markdown console."""
    -210        self.md_console.go(self.console_path)
    -211        self.md_console.set_class(False, "-hidden")
    -212        self.md_console.scroll_end(animate=False)
    +            
    214    def activate_markdown(self) -> None:
    +215        """Activate the markdown console widget."""
    +216        self.md_console.go(self.console_path)
    +217        self.md_console.set_class(False, "-hidden")
    +218        self.md_console.scroll_end(animate=False)
     
    -

    Activate the Markdown console.

    +

    Activate the markdown console widget.

    @@ -1514,22 +1575,30 @@

    -
    214    def action_clear(self, overwrite: bool = True) -> None:
    -215        """Clear the output console."""
    -216        is_new: bool = not file_is_not_empty(str(self.console_path)) or overwrite
    -217        with open(self.console_path, "w" if overwrite else "a", encoding=Charset.UTF_8.val) as f_console:
    -218            f_console.write(
    -219                f"{'---' + os.linesep * 2 if not is_new else ''}"
    -220                f"{'# ' + now(DATE_FORMAT) + os.linesep * 2 if is_new else ''}"
    -221                f"## {AppIcons.STARTED} {now(TIME_FORMAT)}\n\n"
    -222            )
    -223            f_console.flush()
    -224            self._re_render = True
    -225        self._reply(f"{msg.welcome(prompt.user.title())}")
    +            
    220    def action_clear(self, overwrite: bool = True) -> None:
    +221        """Clear the output console.
    +222        :param overwrite: Whether to overwrite the existing content in the console (default is True).
    +223        """
    +224        is_new: bool = not file_is_not_empty(str(self.console_path)) or overwrite
    +225        with open(self.console_path, "w" if overwrite else "a", encoding=Charset.UTF_8.val) as f_console:
    +226            f_console.write(
    +227                f"{'---' + os.linesep * 2 if not is_new else ''}"
    +228                f"{'# ' + now(DATE_FORMAT) + os.linesep * 2 if is_new else ''}"
    +229                f"## {AppIcons.STARTED} {now(TIME_FORMAT)}\n\n"
    +230            )
    +231            f_console.flush()
    +232            self._re_render = True
    +233        self._reply(AIReply.info(f"{msg.welcome(prompt.user.title())}"))
     

    Clear the output console.

    + +
    Parameters
    + +
      +
    • overwrite: Whether to overwrite the existing content in the console (default is True).
    • +
    @@ -1545,9 +1614,9 @@

    -
    227    async def action_speaking(self) -> None:
    -228        """Toggle Speaking ON/OFF."""
    -229        self.ask_and_reply("/speak")
    +            
    235    async def action_speaking(self) -> None:
    +236        """Toggle Speaking ON/OFF."""
    +237        self.ask_and_reply("/speak")
     
    @@ -1567,9 +1636,9 @@

    -
    231    async def action_debugging(self) -> None:
    -232        """Toggle Debugging ON/OFF."""
    -233        self.ask_and_reply("/debug")
    +            
    239    async def action_debugging(self) -> None:
    +240        """Toggle Debugging ON/OFF."""
    +241        self.ask_and_reply("/debug")
     
    @@ -1590,21 +1659,24 @@

    -
    235    @work(thread=True, exclusive=True)
    -236    async def action_ptt(self) -> None:
    -237        """Push-To-Talk STT as input method."""
    -238        self.enable_controls(False)
    -239        if spoken_text := self.engine.speech_to_text():
    -240            self.display_text(f"{shared.username_md}: {spoken_text}")
    -241            if self.ask_and_reply(spoken_text):
    -242                await self.suggester.add_suggestion(spoken_text)
    -243                suggestions = await self.suggester.suggestions()
    -244                cache.save_input_history(suggestions)
    -245        self.enable_controls()
    +            
    243    @work(thread=True, exclusive=True)
    +244    async def action_ptt(self) -> None:
    +245        """Handle the Push-To-Talk (PTT) action for Speech-To-Text (STT) input. This method allows the user to use
    +246        Push-To-Talk as an input method, converting spoken words into text.
    +247        """
    +248        self.enable_controls(False)
    +249        if spoken_text := self.engine.speech_to_text():
    +250            self.display_text(f"{shared.username_md}: {spoken_text}")
    +251            if self.ask_and_reply(spoken_text):
    +252                await self.suggester.add_suggestion(spoken_text)
    +253                suggestions = await self.suggester.suggestions()
    +254                cache.save_input_history(suggestions)
    +255        self.enable_controls()
     
    -

    Push-To-Talk STT as input method.

    +

    Handle the Push-To-Talk (PTT) action for Speech-To-Text (STT) input. This method allows the user to use +Push-To-Talk as an input method, converting spoken words into text.

    @@ -1621,20 +1693,28 @@

    -
    247    @on(Input.Submitted)
    -248    async def on_submit(self, submitted: Input.Submitted) -> None:
    -249        """A coroutine to handle a input submission."""
    -250        question: str = submitted.value
    -251        self.line_input.clear()
    -252        self.display_text(f"{shared.username_md}: {question}")
    -253        if self.ask_and_reply(question):
    -254            await self.suggester.add_suggestion(question)
    -255            suggestions = await self.suggester.suggestions()
    -256            cache.save_input_history(suggestions)
    +            
    257    @on(Input.Submitted)
    +258    async def on_submit(self, submitted: Input.Submitted) -> None:
    +259        """A coroutine to handle input submission events.
    +260        :param submitted: The event that contains the submitted data.
    +261        """
    +262        question: str = submitted.value
    +263        self.line_input.clear()
    +264        self.display_text(f"{shared.username_md}: {question}")
    +265        if self.ask_and_reply(question):
    +266            await self.suggester.add_suggestion(question)
    +267            suggestions = await self.suggester.suggestions()
    +268            cache.save_input_history(suggestions)
     
    -

    A coroutine to handle a input submission.

    +

    A coroutine to handle input submission events.

    + +
    Parameters
    + +
      +
    • submitted: The event that contains the submitted data.
    • +
    @@ -1650,11 +1730,11 @@

    -
    304    def display_text(self, markdown_text: str) -> None:
    -305        """Send the text to the Markdown console.
    -306        :param markdown_text: the text to be displayed.
    -307        """
    -308        self._display_buffer.append(msg.translate(markdown_text))
    +            
    320    def display_text(self, markdown_text: str) -> None:
    +321        """Send the text to the Markdown console.
    +322        :param markdown_text: the text to be displayed.
    +323        """
    +324        self._display_buffer.append(msg.translate(markdown_text))
     
    @@ -1681,26 +1761,33 @@
    Parameters
    -
    349    @work(thread=True, exclusive=True)
    -350    def ask_and_reply(self, question: str) -> tuple[bool, Optional[str]]:
    -351        """Ask the question to the AI, and provide the reply.
    -352        :param question: The question to ask to the AI engine.
    -353        """
    -354        self.enable_controls(False)
    -355        status, reply = self.askai.ask_and_reply(question)
    -356        self.enable_controls()
    -357
    -358        return status, reply
    +            
    366    @work(thread=True, exclusive=True)
    +367    def ask_and_reply(self, question: str) -> tuple[bool, Optional[str]]:
    +368        """Ask the specified question to the AI and provide the reply.
    +369        :param question: The question to ask the AI engine.
    +370        :return: A tuple containing a boolean indicating success or failure, and the AI's reply as an optional string.
    +371        """
    +372        self.enable_controls(False)
    +373        status, reply = self.askai.ask_and_reply(question)
    +374        self.enable_controls()
    +375
    +376        return status, reply
     
    -

    Ask the question to the AI, and provide the reply.

    +

    Ask the specified question to the AI and provide the reply.

    Parameters
      -
    • question: The question to ask to the AI engine.
    • +
    • question: The question to ask the AI engine.
    + +
    Returns
    + +
    +

    A tuple containing a boolean indicating success or failure, and the AI's reply as an optional string.

    +
    @@ -1718,7 +1805,12 @@
    Inherited Members
    SUB_TITLE
    NOTIFICATION_TIMEOUT
    COMMANDS
    +
    COMMAND_PALETTE_BINDING
    +
    COMMAND_PALETTE_DISPLAY
    CLOSE_TIMEOUT
    +
    TOOLTIP_DELAY
    +
    BINDING_GROUP_TITLE
    +
    ESCAPE_TO_MINIMIZE
    title
    sub_title
    dark
    @@ -1763,6 +1855,7 @@
    Inherited Members
    exit
    focused
    active_bindings
    +
    get_system_commands
    get_default_screen
    get_css_variables
    watch_dark
    @@ -1781,6 +1874,7 @@
    Inherited Members
    action_screenshot
    export_screenshot
    save_screenshot
    +
    deliver_screenshot
    bind
    get_key_display
    begin_capture_print
    @@ -1818,6 +1912,7 @@
    Inherited Members
    bell
    simulate_key
    on_event
    +
    escape_to_minimize
    run_action
    action_simulate_key
    action_quit
    @@ -1832,18 +1927,23 @@
    Inherited Members
    action_toggle_class
    action_focus_next
    action_focus_previous
    +
    action_hide_help_panel
    +
    action_show_help_panel
    notify
    clear_notifications
    action_command_palette
    suspend
    action_suspend_process
    open_url
    +
    deliver_text
    +
    deliver_binary
    textual.dom.DOMNode
    DEFAULT_CLASSES
    COMPONENT_CLASSES
    SCOPED_CSS
    +
    HELP
    styles
    set_reactive
    mutate_reactive
    @@ -1870,6 +1970,7 @@
    Inherited Members
    css_tree
    text_style
    rich_style
    +
    check_consume_key
    background_colors
    colors
    ancestors_with_self
    @@ -1882,6 +1983,7 @@
    Inherited Members
    query
    query_children
    query_one
    +
    query_exactly_one
    set_styles
    has_class
    set_class
    diff --git a/docs/api/askai/search.js b/docs/api/askai/search.js index 2cefb083..6701bfa9 100644 --- a/docs/api/askai/search.js +++ b/docs/api/askai/search.js @@ -1,6 +1,6 @@ window.pdocSearch = (function(){ /** elasticlunr - http://weixsong.github.io * Copyright (C) 2017 Oliver Nightingale * Copyright (C) 2017 Wei Song * MIT Licensed */!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();oPackage initialization.

    \n"}, {"fullname": "main.askai.core", "modulename": "main.askai.core", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.askai", "modulename": "main.askai.core.askai", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core\n @file: askai.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.askai.QueryString", "modulename": "main.askai.core.askai", "qualname": "QueryString", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "Union[str, List[str], NoneType]"}, {"fullname": "main.askai.core.askai.AskAi", "modulename": "main.askai.core.askai", "qualname": "AskAi", "kind": "class", "doc": "

    The AskAI core functionalities.

    \n"}, {"fullname": "main.askai.core.askai.AskAi.__init__", "modulename": "main.askai.core.askai", "qualname": "AskAi.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tinteractive: bool,\tspeak: bool,\tdebug: bool,\tcacheable: bool,\ttempo: int,\tengine_name: str,\tmodel_name: str)"}, {"fullname": "main.askai.core.askai.AskAi.SOURCE_DIR", "modulename": "main.askai.core.askai", "qualname": "AskAi.SOURCE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai')"}, {"fullname": "main.askai.core.askai.AskAi.RESOURCE_DIR", "modulename": "main.askai.core.askai", "qualname": "AskAi.RESOURCE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources')"}, {"fullname": "main.askai.core.askai.AskAi.SPLASH", "modulename": "main.askai.core.askai", "qualname": "AskAi.SPLASH", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "" ################\\n ### ###\\n ## ##### ### ##\\n # ####### # ######## ###\\n ### # #### # ###### ## #\\n ### # ## ( ) #### ## ### #\\n ## ( ) # ## ########## # #\\n _ _ _\\n | | ___ __ _ __| (_)_ __ __ _\\n | | / _ \\\\ / _` |/ _` | | '_ \\\\ / _` |\\n | |__| (_) | (_| | (_| | | | | | (_| |_ _ _\\n |_____\\\\___/ \\\\__,_|\\\\__,_|_|_| |_|\\\\__, (_|_|_)\\n |___/\\n\\n # # ## ## ### ################ ####\\n( ) ( ) ### ### ############## #\\n ### # ### ############ #\\n # ### ######## #\\n # #### #\\n ( ) #####\\n""}, {"fullname": "main.askai.core.askai.AskAi.RunModes", "modulename": "main.askai.core.askai", "qualname": "AskAi.RunModes", "kind": "class", "doc": "

    AskAI run modes

    \n", "bases": "enum.Enum"}, {"fullname": "main.askai.core.askai.AskAi.RunModes.ASKAI_TUI", "modulename": "main.askai.core.askai", "qualname": "AskAi.RunModes.ASKAI_TUI", "kind": "variable", "doc": "

    \n", "default_value": "<RunModes.ASKAI_TUI: 'ASKAI_TUI'>"}, {"fullname": "main.askai.core.askai.AskAi.RunModes.ASKAI_CLI", "modulename": "main.askai.core.askai", "qualname": "AskAi.RunModes.ASKAI_CLI", "kind": "variable", "doc": "

    \n", "default_value": "<RunModes.ASKAI_CLI: 'ASKAI_CLI'>"}, {"fullname": "main.askai.core.askai.AskAi.RunModes.ASKAI_CMD", "modulename": "main.askai.core.askai", "qualname": "AskAi.RunModes.ASKAI_CMD", "kind": "variable", "doc": "

    \n", "default_value": "<RunModes.ASKAI_CMD: 'ASKAI_CMD'>"}, {"fullname": "main.askai.core.askai.AskAi.engine", "modulename": "main.askai.core.askai", "qualname": "AskAi.engine", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.engine.ai_engine.AIEngine"}, {"fullname": "main.askai.core.askai.AskAi.context", "modulename": "main.askai.core.askai", "qualname": "AskAi.context", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.support.chat_context.ChatContext"}, {"fullname": "main.askai.core.askai.AskAi.mode", "modulename": "main.askai.core.askai", "qualname": "AskAi.mode", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.enums.router_mode.RouterMode"}, {"fullname": "main.askai.core.askai.AskAi.query_prompt", "modulename": "main.askai.core.askai", "qualname": "AskAi.query_prompt", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.askai.AskAi.console_path", "modulename": "main.askai.core.askai", "qualname": "AskAi.console_path", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path"}, {"fullname": "main.askai.core.askai.AskAi.session_id", "modulename": "main.askai.core.askai", "qualname": "AskAi.session_id", "kind": "variable", "doc": "

    Get the Session id.

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.askai.AskAi.app_settings", "modulename": "main.askai.core.askai", "qualname": "AskAi.app_settings", "kind": "variable", "doc": "

    \n", "annotation": ": list[tuple[str, ...]]"}, {"fullname": "main.askai.core.askai.AskAi.run", "modulename": "main.askai.core.askai", "qualname": "AskAi.run", "kind": "function", "doc": "

    Run the application.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.askai.AskAi.ask_and_reply", "modulename": "main.askai.core.askai", "qualname": "AskAi.ask_and_reply", "kind": "function", "doc": "

    Ask the question to the AI, and provide the reply.

    \n\n
    Parameters
    \n\n
      \n
    • question: The question to ask to the AI engine.
    • \n
    \n", "signature": "(self, question: str) -> tuple[bool, typing.Optional[str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_cli", "modulename": "main.askai.core.askai_cli", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core\n @file: askai_cli.py\n@created: Fri, 9 Aug 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.askai_cli.QueryString", "modulename": "main.askai.core.askai_cli", "qualname": "QueryString", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "Union[str, List[str], NoneType]"}, {"fullname": "main.askai.core.askai_cli.AskAiCli", "modulename": "main.askai.core.askai_cli", "qualname": "AskAiCli", "kind": "class", "doc": "

    The AskAI CLI application.

    \n", "bases": "askai.core.askai.AskAi"}, {"fullname": "main.askai.core.askai_cli.AskAiCli.__init__", "modulename": "main.askai.core.askai_cli", "qualname": "AskAiCli.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tinteractive: bool,\tspeak: bool,\tdebug: bool,\tcacheable: bool,\ttempo: int,\tquery_prompt: str,\tengine_name: str,\tmodel_name: str,\tquery_string: Union[str, List[str], NoneType])"}, {"fullname": "main.askai.core.askai_cli.AskAiCli.run", "modulename": "main.askai.core.askai_cli", "qualname": "AskAiCli.run", "kind": "function", "doc": "

    Run the application.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_configs", "modulename": "main.askai.core.askai_configs", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.askai_configs\n @file: askai_configs.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs", "kind": "class", "doc": "

    Provides access to AskAI configurations.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.__init__", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.INSTANCE", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.askai_configs.AskAiConfigs"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.RESOURCE_DIR", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.RESOURCE_DIR", "kind": "variable", "doc": "

    \n", "default_value": "'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources'"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.engine", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.engine", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.model", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.model", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.is_interactive", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.is_interactive", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.is_speak", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.is_speak", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.is_debug", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.is_debug", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.is_cache", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.is_cache", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.tempo", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.tempo", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.ttl", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.ttl", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.chunk_size", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.chunk_size", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.chunk_overlap", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.chunk_overlap", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.language", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.language", "kind": "variable", "doc": "

    \n", "annotation": ": askai.language.language.Language"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.encoding", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.encoding", "kind": "variable", "doc": "

    \n", "annotation": ": hspylib.core.enums.charset.Charset"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.max_iteractions", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.max_iteractions", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.max_short_memory_size", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.max_short_memory_size", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.max_router_retries", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.max_router_retries", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.max_agent_execution_time_seconds", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.max_agent_execution_time_seconds", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.face_detect_alg", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.face_detect_alg", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.scale_factor", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.scale_factor", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.min_neighbors", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.min_neighbors", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.min_max_size", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.min_max_size", "kind": "variable", "doc": "

    \n", "annotation": ": tuple[int, ...]"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.max_id_distance", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.max_id_distance", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.recorder_phrase_limit_millis", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.recorder_phrase_limit_millis", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.recorder_silence_timeout_millis", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.recorder_silence_timeout_millis", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.recorder_noise_detection_duration_millis", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.recorder_noise_detection_duration_millis", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.recorder_input_device_auto_swap", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.recorder_input_device_auto_swap", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.recorder_devices", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.recorder_devices", "kind": "variable", "doc": "

    \n", "annotation": ": set[str]"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.add_device", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.add_device", "kind": "function", "doc": "

    Add a new device to the configuration list.

    \n", "signature": "(self, device_name: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.remove_device", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.remove_device", "kind": "function", "doc": "

    Remove a new device from the configuration list.

    \n", "signature": "(self, device_name: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_events", "modulename": "main.askai.core.askai_events", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.askai_events\n @file: askai_events.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.askai_events.ASKAI_BUS_NAME", "modulename": "main.askai.core.askai_events", "qualname": "ASKAI_BUS_NAME", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-reply-bus'"}, {"fullname": "main.askai.core.askai_events.REPLY_EVENT", "modulename": "main.askai.core.askai_events", "qualname": "REPLY_EVENT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-reply-event'"}, {"fullname": "main.askai.core.askai_events.REPLY_ERROR_EVENT", "modulename": "main.askai.core.askai_events", "qualname": "REPLY_ERROR_EVENT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-reply-error-event'"}, {"fullname": "main.askai.core.askai_events.MIC_LISTENING_EVENT", "modulename": "main.askai.core.askai_events", "qualname": "MIC_LISTENING_EVENT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-mic-listening-event'"}, {"fullname": "main.askai.core.askai_events.DEVICE_CHANGED_EVENT", "modulename": "main.askai.core.askai_events", "qualname": "DEVICE_CHANGED_EVENT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-input-device-changed-event'"}, {"fullname": "main.askai.core.askai_events.MODE_CHANGED_EVENT", "modulename": "main.askai.core.askai_events", "qualname": "MODE_CHANGED_EVENT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-routing-mode-changed-event'"}, {"fullname": "main.askai.core.askai_events.AskAiEvents", "modulename": "main.askai.core.askai_events", "qualname": "AskAiEvents", "kind": "class", "doc": "

    Facility class to provide easy access to AskAI events.

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.askai_events.AskAiEvents.ASKAI_BUS", "modulename": "main.askai.core.askai_events", "qualname": "AskAiEvents.ASKAI_BUS", "kind": "variable", "doc": "

    \n", "default_value": "ASKAI_BUS"}, {"fullname": "main.askai.core.askai_events.AskAiEvents.bus", "modulename": "main.askai.core.askai_events", "qualname": "AskAiEvents.bus", "kind": "function", "doc": "

    Return an eventbus instance for the given name.

    \n", "signature": "(bus_name: str) -> Optional[hspylib.modules.eventbus.fluid.FluidEventBus]:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_events.AskAiEvents.events", "modulename": "main.askai.core.askai_events", "qualname": "AskAiEvents.events", "kind": "variable", "doc": "

    \n", "annotation": ": hspylib.core.namespace.Namespace"}, {"fullname": "main.askai.core.askai_events.events", "modulename": "main.askai.core.askai_events", "qualname": "events", "kind": "variable", "doc": "

    \n", "annotation": ": hspylib.core.namespace.Namespace", "default_value": "FluidEventBus::askai-reply-bus(reply=FluidEvent-askai-reply-event::(name, verbosity, erase_last, cb_event_handler, emit, subscribe), reply_error=FluidEvent-askai-reply-error-event::(name, cb_event_handler, emit, subscribe), listening=FluidEvent-askai-mic-listening-event::(name, listening, cb_event_handler, emit, subscribe), device_changed=FluidEvent-askai-input-device-changed-event::(name, device, cb_event_handler, emit, subscribe), mode_changed=FluidEvent-askai-routing-mode-changed-event::(name, mode, sum_path, glob, cb_event_handler, emit, subscribe))"}, {"fullname": "main.askai.core.askai_messages", "modulename": "main.askai.core.askai_messages", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.askai_messages\n @file: askai_messages.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages", "kind": "class", "doc": "

    Provide access to static 'translated' messages.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.__init__", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.INSTANCE", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.askai_messages.AskAiMessages"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.TRANSLATOR", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.TRANSLATOR", "kind": "variable", "doc": "

    \n", "annotation": ": askai.language.ai_translator.AITranslator", "default_value": "<class 'askai.language.translators.marian.MarianTranslator'>"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.get_translator", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.get_translator", "kind": "function", "doc": "

    \n", "signature": "(\tfrom_lang: askai.language.language.Language,\tto_lang: askai.language.language.Language) -> askai.language.ai_translator.AITranslator:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.accurate_responses", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.accurate_responses", "kind": "variable", "doc": "

    \n", "annotation": ": list[str]"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.translator", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.translator", "kind": "variable", "doc": "

    \n", "annotation": ": askai.language.ai_translator.AITranslator"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.translate", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.translate", "kind": "function", "doc": "

    Translate text using the configured language.

    \n", "signature": "(self, text: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.welcome", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.welcome", "kind": "function", "doc": "

    \n", "signature": "(self, username: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.wait", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.wait", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.welcome_back", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.welcome_back", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.listening", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.listening", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.transcribing", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.transcribing", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.goodbye", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.goodbye", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.smile", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.smile", "kind": "function", "doc": "

    \n", "signature": "(self, countdown: int) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.cmd_success", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.cmd_success", "kind": "function", "doc": "

    \n", "signature": "(self, command_line: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.searching", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.searching", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.scrapping", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.scrapping", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.summarizing", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.summarizing", "kind": "function", "doc": "

    \n", "signature": "(self, path: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.summary_succeeded", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.summary_succeeded", "kind": "function", "doc": "

    \n", "signature": "(self, path: str, glob: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.enter_qna", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.enter_qna", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.leave_qna", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.leave_qna", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.leave_rag", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.leave_rag", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.qna_welcome", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.qna_welcome", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.press_esc_enter", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.press_esc_enter", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.device_switch", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.device_switch", "kind": "function", "doc": "

    \n", "signature": "(self, device_info: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.photo_captured", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.photo_captured", "kind": "function", "doc": "

    \n", "signature": "(self, photo: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.executing", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.executing", "kind": "function", "doc": "

    \n", "signature": "(self, command_line: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.analysis", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.analysis", "kind": "function", "doc": "

    \n", "signature": "(self, result: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.assert_acc", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.assert_acc", "kind": "function", "doc": "

    \n", "signature": "(self, status: str, details: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.action_plan", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.action_plan", "kind": "function", "doc": "

    \n", "signature": "(self, plan_text: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.x_reference", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.x_reference", "kind": "function", "doc": "

    \n", "signature": "(self, pathname: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.describe_image", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.describe_image", "kind": "function", "doc": "

    \n", "signature": "(self, image_path: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.model_select", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.model_select", "kind": "function", "doc": "

    \n", "signature": "(self, model: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.task", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.task", "kind": "function", "doc": "

    \n", "signature": "(self, task: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.final_query", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.final_query", "kind": "function", "doc": "

    \n", "signature": "(self, query: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.no_caption", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.no_caption", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.no_output", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.no_output", "kind": "function", "doc": "

    \n", "signature": "(self, source: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.access_grant", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.access_grant", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.no_query_string", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.no_query_string", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.invalid_response", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.invalid_response", "kind": "function", "doc": "

    \n", "signature": "(self, response_text: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.invalid_command", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.invalid_command", "kind": "function", "doc": "

    \n", "signature": "(self, response_text: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.cmd_no_exist", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.cmd_no_exist", "kind": "function", "doc": "

    \n", "signature": "(self, command: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.cmd_failed", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.cmd_failed", "kind": "function", "doc": "

    \n", "signature": "(self, cmd_line: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.camera_not_open", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.camera_not_open", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.missing_package", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.missing_package", "kind": "function", "doc": "

    \n", "signature": "(self, err: ImportError) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.summary_not_possible", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.summary_not_possible", "kind": "function", "doc": "

    \n", "signature": "(self, err: BaseException = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.intelligible", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.intelligible", "kind": "function", "doc": "

    \n", "signature": "(self, reason: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.impossible", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.impossible", "kind": "function", "doc": "

    \n", "signature": "(self, reason: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.timeout", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.timeout", "kind": "function", "doc": "

    \n", "signature": "(self, reason: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.llm_error", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.llm_error", "kind": "function", "doc": "

    \n", "signature": "(self, error: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.fail_to_search", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.fail_to_search", "kind": "function", "doc": "

    \n", "signature": "(self, error: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.too_many_actions", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.too_many_actions", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.unprocessable", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.unprocessable", "kind": "function", "doc": "

    \n", "signature": "(self, reason: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.quote_exceeded", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.quote_exceeded", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_prompt", "modulename": "main.askai.core.askai_prompt", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.askai_prompt\n @file: askai_prompt.py\n@created: Mon, 22 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt", "kind": "class", "doc": "

    Provide the prompts used by the AskAi.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.__init__", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.INSTANCE", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.askai_prompt.AskAiPrompt"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.PROMPT_DIR", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.PROMPT_DIR", "kind": "variable", "doc": "

    \n", "default_value": "'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources/prompts'"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.os_type", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.os_type", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[Literal['linux', 'windows', 'darwin']]"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.shell", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.shell", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[Literal['bash', 'csh', 'dash', 'ksh', 'tcsh', 'zsh', 'sh']]"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.user", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.user", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.idiom", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.idiom", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.read_prompt", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.read_prompt", "kind": "function", "doc": "

    Read a processor prompt template and set its persona.

    \n", "signature": "(self, template_file: str, prompt_dir: str = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.append_path", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.append_path", "kind": "function", "doc": "

    Return the PROMPT_DIR with the extra path appended.

    \n", "signature": "(self, path: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings", "modulename": "main.askai.core.askai_settings", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.support\n @file: askai_settings.py\n@created: Tue, 23 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.askai_settings.ASKAI_DIR", "modulename": "main.askai.core.askai_settings", "qualname": "ASKAI_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/askai')"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings", "kind": "class", "doc": "

    The Setman Settings.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.__init__", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.INSTANCE", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.askai_settings.AskAiSettings"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.RESOURCE_DIR", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.RESOURCE_DIR", "kind": "variable", "doc": "

    \n", "default_value": "'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources'"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.settings", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.settings", "kind": "variable", "doc": "

    \n", "annotation": ": setman.settings.settings.Settings"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.search", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.search", "kind": "function", "doc": "

    Search setting using the specified filters.

    \n\n
    Parameters
    \n\n
      \n
    • filters: Filters used on the search.
    • \n
    \n", "signature": "(self, filters: str | None = None) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.defaults", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.defaults", "kind": "function", "doc": "

    Create the default settings database if they doesn't exist.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.get", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.get", "kind": "function", "doc": "

    \n", "signature": "(self, key: str, default_value: str | None = '') -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.put", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.put", "kind": "function", "doc": "

    \n", "signature": "(self, key: str, value: Any) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.get_bool", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.get_bool", "kind": "function", "doc": "

    \n", "signature": "(self, key: str, default_value: bool | None = False) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.get_int", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.get_int", "kind": "function", "doc": "

    \n", "signature": "(self, key: str, default_value: int | None = 0) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.get_float", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.get_float", "kind": "function", "doc": "

    \n", "signature": "(self, key: str, default_value: float | None = 0.0) -> float:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.get_list", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.get_list", "kind": "function", "doc": "

    \n", "signature": "(self, key: str, default_value: list | None = None) -> list:", "funcdef": "def"}, {"fullname": "main.askai.core.commander", "modulename": "main.askai.core.commander", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.commander.commander", "modulename": "main.askai.core.commander.commander", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.commander.commander\n @file: commander.py\n@created: Thu, 25 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.commander.commander.COMMANDER_HELP_TPL", "modulename": "main.askai.core.commander.commander", "qualname": "COMMANDER_HELP_TPL", "kind": "variable", "doc": "

    \n", "default_value": "<string.Template object>"}, {"fullname": "main.askai.core.commander.commander.RE_ASKAI_CMD", "modulename": "main.askai.core.commander.commander", "qualname": "RE_ASKAI_CMD", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'^(?<!\\\\\\\\)/(\\\\w+)( (.*))*$'"}, {"fullname": "main.askai.core.commander.commander.commands", "modulename": "main.askai.core.commander.commander", "qualname": "commands", "kind": "function", "doc": "

    Return the list of all commander commands.

    \n", "signature": "() -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commander.commander_help", "modulename": "main.askai.core.commander.commander", "qualname": "commander_help", "kind": "function", "doc": "

    Return the commander help string.

    \n", "signature": "() -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commander.ask_commander", "modulename": "main.askai.core.commander.commander", "qualname": "ask_commander", "kind": "variable", "doc": "

    AskAI commands group.

    \n", "default_value": "<Group ask-commander>"}, {"fullname": "main.askai.core.commander.commander.help", "modulename": "main.askai.core.commander.commander", "qualname": "help", "kind": "variable", "doc": "

    Show this help message and exit.

    \n", "default_value": "<Command help>"}, {"fullname": "main.askai.core.commander.commander.debug", "modulename": "main.askai.core.commander.commander", "qualname": "debug", "kind": "variable", "doc": "

    Toggle debug mode ON/OFF.

    \n", "default_value": "<Command debug>"}, {"fullname": "main.askai.core.commander.commander.speak", "modulename": "main.askai.core.commander.commander", "qualname": "speak", "kind": "variable", "doc": "

    Toggle speak mode ON/OFF.

    \n", "default_value": "<Command speak>"}, {"fullname": "main.askai.core.commander.commander.context", "modulename": "main.askai.core.commander.commander", "qualname": "context", "kind": "variable", "doc": "

    List/forget the current context window.\n:param operation The operation to manage contexts.\n:param name The context name.

    \n", "default_value": "<Command context>"}, {"fullname": "main.askai.core.commander.commander.history", "modulename": "main.askai.core.commander.commander", "qualname": "history", "kind": "variable", "doc": "

    List/forget the input history.\n:param operation The operation to manage history.

    \n", "default_value": "<Command history>"}, {"fullname": "main.askai.core.commander.commander.copy", "modulename": "main.askai.core.commander.commander", "qualname": "copy", "kind": "variable", "doc": "

    Copy a context entry to the clipboard

    \n\n
    Parameters
    \n\n
      \n
    • name: The context name.
    • \n
    \n", "default_value": "<Command copy>"}, {"fullname": "main.askai.core.commander.commander.devices", "modulename": "main.askai.core.commander.commander", "qualname": "devices", "kind": "variable", "doc": "

    List/set the audio input devices.\n:param operation The operation to manage devices.\n:param name The device name to set.

    \n", "default_value": "<Command devices>"}, {"fullname": "main.askai.core.commander.commander.settings", "modulename": "main.askai.core.commander.commander", "qualname": "settings", "kind": "variable", "doc": "

    List/get/set/reset AskAI settings.\n:param operation The operation to manage settings.\n:param name The settings key to operate.\n:param value The settings value to be set.

    \n", "default_value": "<Command settings>"}, {"fullname": "main.askai.core.commander.commander.cache", "modulename": "main.askai.core.commander.commander", "qualname": "cache", "kind": "variable", "doc": "

    List/get/clear/files AskAI TTL cache and files.\n:param operation The operation to manage cache.\n:param args The operation arguments operate.

    \n", "default_value": "<Command cache>"}, {"fullname": "main.askai.core.commander.commander.tempo", "modulename": "main.askai.core.commander.commander", "qualname": "tempo", "kind": "variable", "doc": "

    List/set speech-to-text tempo.\n:param speed The tempo to set.

    \n", "default_value": "<Command tempo>"}, {"fullname": "main.askai.core.commander.commander.voices", "modulename": "main.askai.core.commander.commander", "qualname": "voices", "kind": "variable", "doc": "

    List/set/play speech-to-text voices.\n:param operation The operation to manage voices.\n:param name The voice name.

    \n", "default_value": "<Command voices>"}, {"fullname": "main.askai.core.commander.commander.tts", "modulename": "main.askai.core.commander.commander", "qualname": "tts", "kind": "variable", "doc": "

    Convert a text to speech using the default AI engine.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to be converted. If the text denotes a valid file, its contents will be used instead.
    • \n
    • dest_dir: The destination directory, where to save the converted file.
    • \n
    • playback: Whether to plat the audio file or not.
    • \n
    \n", "default_value": "<Command tts>"}, {"fullname": "main.askai.core.commander.commander.summarize", "modulename": "main.askai.core.commander.commander", "qualname": "summarize", "kind": "variable", "doc": "

    Generate a summarization of the folder contents.

    \n\n
    Parameters
    \n\n
      \n
    • folder: The base folder of the summarization.
    • \n
    • glob: The glob pattern or file of the summarization.
    • \n
    \n", "default_value": "<Command summarize>"}, {"fullname": "main.askai.core.commander.commander.idiom", "modulename": "main.askai.core.commander.commander", "qualname": "idiom", "kind": "variable", "doc": "

    Set the application language.

    \n\n
    Parameters
    \n\n
      \n
    • locale_str: The locale string.
    • \n
    \n", "default_value": "<Command idiom>"}, {"fullname": "main.askai.core.commander.commander.info", "modulename": "main.askai.core.commander.commander", "qualname": "info", "kind": "variable", "doc": "

    Display some useful application information.

    \n", "default_value": "<Command info>"}, {"fullname": "main.askai.core.commander.commander.translate", "modulename": "main.askai.core.commander.commander", "qualname": "translate", "kind": "variable", "doc": "

    Translate a text from the source language to the target language.

    \n\n
    Parameters
    \n\n
      \n
    • from_locale_str: The source idiom.
    • \n
    • to_locale_str: The target idiom.
    • \n
    • texts: The texts to be translated.
    • \n
    \n", "default_value": "<Command translate>"}, {"fullname": "main.askai.core.commander.commander.camera", "modulename": "main.askai.core.commander.commander", "qualname": "camera", "kind": "variable", "doc": "

    Take photos, import images or identify a person using hte WebCam.

    \n\n
    Parameters
    \n\n
      \n
    • operation: the camera operation.\n:param args The operation arguments operate.
    • \n
    \n", "default_value": "<Command camera>"}, {"fullname": "main.askai.core.commander.commands", "modulename": "main.askai.core.commander.commands", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.commander.commands.cache_cmd", "modulename": "main.askai.core.commander.commands.cache_cmd", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.commander.cache_cmd\n @file: cache_cmd.py\n@created: Thu, 27 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.commander.commands.cache_cmd.CacheCmd", "modulename": "main.askai.core.commander.commands.cache_cmd", "qualname": "CacheCmd", "kind": "class", "doc": "

    Provides cache command functionalities.

    \n", "bases": "abc.ABC"}, {"fullname": "main.askai.core.commander.commands.cache_cmd.CacheCmd.list", "modulename": "main.askai.core.commander.commands.cache_cmd", "qualname": "CacheCmd.list", "kind": "function", "doc": "

    List all cache entries.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.cache_cmd.CacheCmd.get", "modulename": "main.askai.core.commander.commands.cache_cmd", "qualname": "CacheCmd.get", "kind": "function", "doc": "

    Get cache entry by name.

    \n", "signature": "(name: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.cache_cmd.CacheCmd.clear", "modulename": "main.askai.core.commander.commands.cache_cmd", "qualname": "CacheCmd.clear", "kind": "function", "doc": "

    Clear a specified, or all, cache entries.

    \n", "signature": "(entry: str | int | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.cache_cmd.CacheCmd.files", "modulename": "main.askai.core.commander.commands.cache_cmd", "qualname": "CacheCmd.files", "kind": "function", "doc": "

    Enlist all cached files (from askai cache dir).

    \n", "signature": "(cleanup: bool = False, *args: str | int) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.camera_cmd", "modulename": "main.askai.core.commander.commands.camera_cmd", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.commander.commands.camera_cmd.CameraCmd", "modulename": "main.askai.core.commander.commands.camera_cmd", "qualname": "CameraCmd", "kind": "class", "doc": "

    Provides camera command functionalities.

    \n", "bases": "abc.ABC"}, {"fullname": "main.askai.core.commander.commands.camera_cmd.CameraCmd.capture", "modulename": "main.askai.core.commander.commands.camera_cmd", "qualname": "CameraCmd.capture", "kind": "function", "doc": "

    Take a photo using hte Webcam.

    \n\n
    Parameters
    \n\n
      \n
    • filename: The filename of the photo taken.
    • \n
    • detect_faces: Whether to detect faces on the photo or not.
    • \n
    • countdown: A countdown to be displayed before the photo is taken.
    • \n
    \n", "signature": "(\tfilename: Union[pathlib.Path, str, NoneType] = None,\tdetect_faces: bool = True,\tcountdown: int = 3) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.camera_cmd.CameraCmd.identify", "modulename": "main.askai.core.commander.commands.camera_cmd", "qualname": "CameraCmd.identify", "kind": "function", "doc": "

    Identify the person in from of the WebCam.

    \n\n
    Parameters
    \n\n
      \n
    • max_distance: The maximum distance of the face (the lower, the closer to the real face).
    • \n
    \n", "signature": "(max_distance: int = 0.7) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.camera_cmd.CameraCmd.import_images", "modulename": "main.askai.core.commander.commands.camera_cmd", "qualname": "CameraCmd.import_images", "kind": "function", "doc": "

    Import image files into the image collection.

    \n\n
    Parameters
    \n\n
      \n
    • pathname: The pathname or glob to be imported.
    • \n
    • detect_faces: Whether to detect faces on the images or not.
    • \n
    \n", "signature": "(\tpathname: Union[pathlib.Path, str, NoneType] = None,\tdetect_faces: bool = True) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.general_cmd", "modulename": "main.askai.core.commander.commands.general_cmd", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.commander.general_cmd\n @file: general_cmd.py\n@created: Mon, 06 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.commander.commands.general_cmd.GeneralCmd", "modulename": "main.askai.core.commander.commands.general_cmd", "qualname": "GeneralCmd", "kind": "class", "doc": "

    Provides general command functionalities.

    \n", "bases": "abc.ABC"}, {"fullname": "main.askai.core.commander.commands.general_cmd.GeneralCmd.execute", "modulename": "main.askai.core.commander.commands.general_cmd", "qualname": "GeneralCmd.execute", "kind": "function", "doc": "

    Execute a terminal command.\n:param cmd_line The command line to execute.

    \n", "signature": "(cmd_line: str | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.general_cmd.GeneralCmd.summarize", "modulename": "main.askai.core.commander.commands.general_cmd", "qualname": "GeneralCmd.summarize", "kind": "function", "doc": "

    Generate a summarization of the folder contents.

    \n\n
    Parameters
    \n\n
      \n
    • folder: The base folder of the summarization.
    • \n
    • glob: The glob pattern or file of the summarization.
    • \n
    \n", "signature": "(folder: str, glob: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.general_cmd.GeneralCmd.idiom", "modulename": "main.askai.core.commander.commands.general_cmd", "qualname": "GeneralCmd.idiom", "kind": "function", "doc": "

    Set the application language.

    \n\n
    Parameters
    \n\n
      \n
    • locale_str: The locale string.
    • \n
    \n", "signature": "(locale_str: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.general_cmd.GeneralCmd.app_info", "modulename": "main.askai.core.commander.commands.general_cmd", "qualname": "GeneralCmd.app_info", "kind": "function", "doc": "

    Display some useful application information.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.general_cmd.GeneralCmd.translate", "modulename": "main.askai.core.commander.commands.general_cmd", "qualname": "GeneralCmd.translate", "kind": "function", "doc": "

    Translate a text from the source language to the target language.

    \n\n
    Parameters
    \n\n
      \n
    • from_lang: The source idiom.
    • \n
    • to_lang: The target idiom.
    • \n
    • texts: The texts to be translated.
    • \n
    \n", "signature": "(\tfrom_lang: askai.language.language.Language,\tto_lang: askai.language.language.Language,\t*texts: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.history_cmd", "modulename": "main.askai.core.commander.commands.history_cmd", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.commander.history_cmd\n @file: general_cmd.py\n@created: Sat, 22 Jun 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.commander.commands.history_cmd.HistoryCmd", "modulename": "main.askai.core.commander.commands.history_cmd", "qualname": "HistoryCmd", "kind": "class", "doc": "

    Provides history command functionalities.

    \n", "bases": "abc.ABC"}, {"fullname": "main.askai.core.commander.commands.history_cmd.HistoryCmd.context_list", "modulename": "main.askai.core.commander.commands.history_cmd", "qualname": "HistoryCmd.context_list", "kind": "function", "doc": "

    List the chat context window entries.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.history_cmd.HistoryCmd.context_forget", "modulename": "main.askai.core.commander.commands.history_cmd", "qualname": "HistoryCmd.context_forget", "kind": "function", "doc": "

    Forget entries pushed to the chat context.

    \n\n
    Parameters
    \n\n
      \n
    • context: The context key; or none to forget all context.
    • \n
    \n", "signature": "(context: str | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.history_cmd.HistoryCmd.context_copy", "modulename": "main.askai.core.commander.commands.history_cmd", "qualname": "HistoryCmd.context_copy", "kind": "function", "doc": "

    Copy a context entry to the clipboard

    \n\n
    Parameters
    \n\n
      \n
    • name: The context name.
    • \n
    \n", "signature": "(name: str | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.history_cmd.HistoryCmd.history_list", "modulename": "main.askai.core.commander.commands.history_cmd", "qualname": "HistoryCmd.history_list", "kind": "function", "doc": "

    List the input history entries.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.history_cmd.HistoryCmd.history_forget", "modulename": "main.askai.core.commander.commands.history_cmd", "qualname": "HistoryCmd.history_forget", "kind": "function", "doc": "

    Forget entries pushed to the history.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.settings_cmd", "modulename": "main.askai.core.commander.commands.settings_cmd", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.commander.settings_cmd\n @file: settings_cmd.py\n@created: Thu, 25 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.commander.commands.settings_cmd.SettingsCmd", "modulename": "main.askai.core.commander.commands.settings_cmd", "qualname": "SettingsCmd", "kind": "class", "doc": "

    Provides settings manipulation command functionalities.

    \n", "bases": "abc.ABC"}, {"fullname": "main.askai.core.commander.commands.settings_cmd.SettingsCmd.list", "modulename": "main.askai.core.commander.commands.settings_cmd", "qualname": "SettingsCmd.list", "kind": "function", "doc": "

    List all settings, optionally matching filters.

    \n", "signature": "(filters: str | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.settings_cmd.SettingsCmd.set", "modulename": "main.askai.core.commander.commands.settings_cmd", "qualname": "SettingsCmd.set", "kind": "function", "doc": "

    Set a setting value.

    \n", "signature": "(name: str, value: Any) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.settings_cmd.SettingsCmd.get", "modulename": "main.askai.core.commander.commands.settings_cmd", "qualname": "SettingsCmd.get", "kind": "function", "doc": "

    Get the setting specified by key.

    \n", "signature": "(key: str) -> Optional[setman.settings.settings_entry.SettingsEntry]:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.settings_cmd.SettingsCmd.reset", "modulename": "main.askai.core.commander.commands.settings_cmd", "qualname": "SettingsCmd.reset", "kind": "function", "doc": "

    Reset settings to the defaults.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.commander.tts_stt_cmd\n @file: tts_stt_cmd.py\n@created: Thu, 25 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd", "kind": "class", "doc": "

    Provides Speech-to-Text (STT) and Text-to-Speech (TTS) command functionalities.

    \n", "bases": "abc.ABC"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd.voice_list", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd.voice_list", "kind": "function", "doc": "

    List all available voices for the current engine/model.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd.voice_set", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd.voice_set", "kind": "function", "doc": "

    Set the specified engine's voice.

    \n", "signature": "(name: str | int | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd.voice_play", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd.voice_play", "kind": "function", "doc": "

    Play a sample using the specified voice.

    \n", "signature": "(name: str | int | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd.tempo", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd.tempo", "kind": "function", "doc": "

    Set the playing speed of the speech.

    \n", "signature": "(speed: int | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd.device_list", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd.device_list", "kind": "function", "doc": "

    List the available audio input devices.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd.device_set", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd.device_set", "kind": "function", "doc": "

    Set the current audio input device.

    \n", "signature": "(name: str | int | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd.tts", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd.tts", "kind": "function", "doc": "

    Convert a text to speech.

    \n", "signature": "(\ttext: str,\tdest_dir: str = '/Users/hjunior/GIT-Repository/GitHub/askai',\tplayback: bool = True) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component", "modulename": "main.askai.core.component", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.component.audio_player", "modulename": "main.askai.core.component.audio_player", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.component\n @file: audio_player.py\n@created: Wed, 22 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer", "kind": "class", "doc": "

    Provide an interface to play audio using the default speaker device.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer.__init__", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer.INSTANCE", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.audio_player.AudioPlayer"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer.SFX_DIR", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer.SFX_DIR", "kind": "variable", "doc": "

    \n", "default_value": "'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources/assets/sound-fx'"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer.play_audio_file", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer.play_audio_file", "kind": "function", "doc": "

    Play the specified mp3 file using ffplay (ffmpeg) application.

    \n\n
    Parameters
    \n\n
      \n
    • path_to_audio_file: the path to the mp3 file to be played.
    • \n
    • tempo: the playing speed.
    • \n
    \n", "signature": "(path_to_audio_file: str | pathlib.Path, tempo: int = 1) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer.start_delay", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer.start_delay", "kind": "function", "doc": "

    Determine the amount of delay before start streaming the text.

    \n", "signature": "(self) -> float:", "funcdef": "def"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer.audio_length", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer.audio_length", "kind": "function", "doc": "

    \n", "signature": "(self, path_to_audio_file: str) -> float:", "funcdef": "def"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer.play_sfx", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer.play_sfx", "kind": "function", "doc": "

    Play a sound effect audio file.

    \n\n
    Parameters
    \n\n
      \n
    • filename: The filename of the sound effect.
    • \n
    • file_ext: the file extension.
    • \n
    \n", "signature": "(\tself,\tfilename: str,\tfile_ext: Literal['.mp3', '.wav', '.m4a'] = '.mp3') -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service", "modulename": "main.askai.core.component.cache_service", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.component\n @file: cache_service.py\n@created: Tue, 16 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.cache_service.CACHE_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "CACHE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache')"}, {"fullname": "main.askai.core.component.cache_service.SETTINGS_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "SETTINGS_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/settings')"}, {"fullname": "main.askai.core.component.cache_service.AUDIO_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "AUDIO_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/audio')"}, {"fullname": "main.askai.core.component.cache_service.PICTURE_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "PICTURE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/pictures')"}, {"fullname": "main.askai.core.component.cache_service.PHOTO_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "PHOTO_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/pictures/photos')"}, {"fullname": "main.askai.core.component.cache_service.FACE_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "FACE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/pictures/faces')"}, {"fullname": "main.askai.core.component.cache_service.IMG_IMPORTS_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "IMG_IMPORTS_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/pictures/imports')"}, {"fullname": "main.askai.core.component.cache_service.GEN_AI_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "GEN_AI_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/generated')"}, {"fullname": "main.askai.core.component.cache_service.REC_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "REC_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/recordings')"}, {"fullname": "main.askai.core.component.cache_service.PERSIST_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "PERSIST_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/chroma')"}, {"fullname": "main.askai.core.component.cache_service.RAG_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "RAG_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/rag')"}, {"fullname": "main.askai.core.component.cache_service.CacheEntry", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheEntry", "kind": "class", "doc": "

    CacheEntry(key, expires)

    \n", "bases": "builtins.tuple"}, {"fullname": "main.askai.core.component.cache_service.CacheEntry.__init__", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheEntry.__init__", "kind": "function", "doc": "

    Create new instance of CacheEntry(key, expires)

    \n", "signature": "(key, expires)"}, {"fullname": "main.askai.core.component.cache_service.CacheEntry.key", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheEntry.key", "kind": "variable", "doc": "

    Alias for field number 0

    \n"}, {"fullname": "main.askai.core.component.cache_service.CacheEntry.expires", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheEntry.expires", "kind": "variable", "doc": "

    Alias for field number 1

    \n"}, {"fullname": "main.askai.core.component.cache_service.CacheService", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService", "kind": "class", "doc": "

    Provide a cache service for previously used queries, audio generation, etc...

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.cache_service.CacheService.__init__", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.cache_service.CacheService.INSTANCE", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.cache_service.CacheService"}, {"fullname": "main.askai.core.component.cache_service.CacheService.ASKAI_CACHE_KEYS", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.ASKAI_CACHE_KEYS", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-cache-keys'"}, {"fullname": "main.askai.core.component.cache_service.CacheService.ASKAI_INPUT_CACHE_KEY", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.ASKAI_INPUT_CACHE_KEY", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-input-history'"}, {"fullname": "main.askai.core.component.cache_service.CacheService.ASKAI_CONTEXT_KEY", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.ASKAI_CONTEXT_KEY", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-context-key'"}, {"fullname": "main.askai.core.component.cache_service.CacheService.keys", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.keys", "kind": "variable", "doc": "

    \n", "annotation": ": set[str]"}, {"fullname": "main.askai.core.component.cache_service.CacheService.audio_file_path", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.audio_file_path", "kind": "function", "doc": "

    Retrieve the hashed audio file path and whether the file exists or not.

    \n\n
    Parameters
    \n\n
      \n
    • text: Text to be cached. This is the text that the audio is speaking.
    • \n
    • voice: The AI voice used to STT.
    • \n
    • audio_format: The audio file format.
    • \n
    \n", "signature": "(\tself,\ttext: str,\tvoice: str = 'onyx',\taudio_format: str = 'mp3') -> Tuple[str, bool]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.save_reply", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.save_reply", "kind": "function", "doc": "

    Save a AI reply into the TTL cache.

    \n\n
    Parameters
    \n\n
      \n
    • text: Text to be cached.
    • \n
    • reply: The reply associated to this text.
    • \n
    \n", "signature": "(self, text: str, reply: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.read_reply", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.read_reply", "kind": "function", "doc": "

    Read AI replies from TTL cache.

    \n\n
    Parameters
    \n\n
      \n
    • text: Text to be cached.
    • \n
    \n", "signature": "(self, text: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.del_reply", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.del_reply", "kind": "function", "doc": "

    Delete AI replies from TTL cache.

    \n\n
    Parameters
    \n\n
      \n
    • text: Text to be deleted.
    • \n
    \n", "signature": "(self, text: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.clear_replies", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.clear_replies", "kind": "function", "doc": "

    Clear all cached replies.

    \n", "signature": "(self) -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.read_input_history", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.read_input_history", "kind": "function", "doc": "

    Read the input queries from TTL cache.

    \n", "signature": "(self) -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.save_input_history", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.save_input_history", "kind": "function", "doc": "

    Save the line input queries into the TTL cache.

    \n", "signature": "(self, history: list[str] = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.load_input_history", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.load_input_history", "kind": "function", "doc": "

    Load the suggester with user input history.

    \n", "signature": "(self, predefined: list[str] = None) -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.save_context", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.save_context", "kind": "function", "doc": "

    Save the Context window entries into the TTL cache.

    \n", "signature": "(self, context: list[str]) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.read_context", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.read_context", "kind": "function", "doc": "

    Read the Context window entries from the TTL cache.

    \n", "signature": "(self) -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.camera", "modulename": "main.askai.core.component.camera", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.components\n @file: recorder.py\n@created: Wed, 22 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.camera.InputDevice", "modulename": "main.askai.core.component.camera", "qualname": "InputDevice", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "tuple[int, str]"}, {"fullname": "main.askai.core.component.camera.Camera", "modulename": "main.askai.core.component.camera", "qualname": "Camera", "kind": "class", "doc": "

    Provide an interface to capture WebCam photos.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.camera.Camera.__init__", "modulename": "main.askai.core.component.camera", "qualname": "Camera.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.camera.Camera.INSTANCE", "modulename": "main.askai.core.component.camera", "qualname": "Camera.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.camera.Camera"}, {"fullname": "main.askai.core.component.camera.Camera.RESOURCE_DIR", "modulename": "main.askai.core.component.camera", "qualname": "Camera.RESOURCE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources')"}, {"fullname": "main.askai.core.component.camera.Camera.ALG", "modulename": "main.askai.core.component.camera", "qualname": "Camera.ALG", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'haarcascade_frontalface_default.xml'"}, {"fullname": "main.askai.core.component.camera.Camera.initialize", "modulename": "main.askai.core.component.camera", "qualname": "Camera.initialize", "kind": "function", "doc": "

    Initialize the camera device.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.camera.Camera.shutdown", "modulename": "main.askai.core.component.camera", "qualname": "Camera.shutdown", "kind": "function", "doc": "

    Shutdown the camera device.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.camera.Camera.capture", "modulename": "main.askai.core.component.camera", "qualname": "Camera.capture", "kind": "function", "doc": "

    Capture a WebCam frame (take a photo).

    \n", "signature": "(\tself,\tfilename: Union[pathlib.Path, str, NoneType],\tcountdown: int = 3,\twith_caption: bool = True,\tstore_image: bool = True) -> Optional[tuple[askai.core.component.image_store.ImageFile, numpy.ndarray[Any, numpy.dtype]]]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.camera.Camera.detect_faces", "modulename": "main.askai.core.component.camera", "qualname": "Camera.detect_faces", "kind": "function", "doc": "

    Detect all faces in the photo.

    \n", "signature": "(\tself,\tphoto: numpy.ndarray[typing.Any, numpy.dtype],\tfilename: Union[pathlib.Path, str, NoneType] = None,\twith_caption: bool = True,\tstore_image: bool = True) -> tuple[list[askai.core.component.image_store.ImageFile], list[numpy.ndarray[typing.Any, numpy.dtype]]]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.camera.Camera.identify", "modulename": "main.askai.core.component.camera", "qualname": "Camera.identify", "kind": "function", "doc": "

    Identify the person in front of the WebCam.

    \n", "signature": "(\tself,\tmax_distance: float = 0.7) -> Optional[askai.core.component.image_store.ImageMetadata]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.camera.Camera.import_images", "modulename": "main.askai.core.component.camera", "qualname": "Camera.import_images", "kind": "function", "doc": "

    Import image files into the image collection.

    \n", "signature": "(\tself,\tpathname: Union[pathlib.Path, str, NoneType],\tdetect_faces: bool = False,\twith_caption: bool = True,\tstore_image: bool = True) -> tuple[int, ...]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.geo_location", "modulename": "main.askai.core.component.geo_location", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.component\n @file: geo_location.py\n@created: Tue, 23 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation", "kind": "class", "doc": "

    TODO

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.__init__", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.INSTANCE", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.geo_location.GeoLocation"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.GEO_LOC_URL", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.GEO_LOC_URL", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'http://ip-api.com/json'"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.EMPTY_JSON_RESP", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.EMPTY_JSON_RESP", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'\\n{\\n "status": "failure", "country": "", "countryCode": "", "region": "", "regionName": "",\\n "city": "", "zip": "", "lat": 0.0, "lon": 0.0, "timezone": "",\\n "isp": "", "org": "", "as": "", "query": ""\\n}\\n'"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.DATE_FMT", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.DATE_FMT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'%a %d %b %-H:%M %Y'"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.get_location", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.get_location", "kind": "function", "doc": "

    TODO

    \n", "signature": "(cls, ip: str = None) -> hspylib.core.namespace.Namespace:", "funcdef": "def"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.latitude", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.latitude", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.longitude", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.longitude", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.country", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.country", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.country_code", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.country_code", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.region", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.region", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.region_name", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.region_name", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.city", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.city", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.zip", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.zip", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.timezone", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.timezone", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.location", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.location", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.datetime", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.datetime", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.image_store", "modulename": "main.askai.core.component.image_store", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.components\n @file: recorder.py\n@created: Wed, 22 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.image_store.Metadata", "modulename": "main.askai.core.component.image_store", "qualname": "Metadata", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "Mapping[str, str | int | float | bool]"}, {"fullname": "main.askai.core.component.image_store.ImageData", "modulename": "main.askai.core.component.image_store", "qualname": "ImageData", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "numpy.ndarray[typing.Any, numpy.dtype]"}, {"fullname": "main.askai.core.component.image_store.ImageFile", "modulename": "main.askai.core.component.image_store", "qualname": "ImageFile", "kind": "class", "doc": "

    ImageFile(img_id, img_path, img_category, img_caption)

    \n", "bases": "builtins.tuple"}, {"fullname": "main.askai.core.component.image_store.ImageFile.__init__", "modulename": "main.askai.core.component.image_store", "qualname": "ImageFile.__init__", "kind": "function", "doc": "

    Create new instance of ImageFile(img_id, img_path, img_category, img_caption)

    \n", "signature": "(img_id, img_path, img_category, img_caption)"}, {"fullname": "main.askai.core.component.image_store.ImageFile.img_id", "modulename": "main.askai.core.component.image_store", "qualname": "ImageFile.img_id", "kind": "variable", "doc": "

    Alias for field number 0

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageFile.img_path", "modulename": "main.askai.core.component.image_store", "qualname": "ImageFile.img_path", "kind": "variable", "doc": "

    Alias for field number 1

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageFile.img_category", "modulename": "main.askai.core.component.image_store", "qualname": "ImageFile.img_category", "kind": "variable", "doc": "

    Alias for field number 2

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageFile.img_caption", "modulename": "main.askai.core.component.image_store", "qualname": "ImageFile.img_caption", "kind": "variable", "doc": "

    Alias for field number 3

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageMetadata", "modulename": "main.askai.core.component.image_store", "qualname": "ImageMetadata", "kind": "class", "doc": "

    ImageMetadata(name, data, uri, distance)

    \n", "bases": "builtins.tuple"}, {"fullname": "main.askai.core.component.image_store.ImageMetadata.__init__", "modulename": "main.askai.core.component.image_store", "qualname": "ImageMetadata.__init__", "kind": "function", "doc": "

    Create new instance of ImageMetadata(name, data, uri, distance)

    \n", "signature": "(name, data, uri, distance)"}, {"fullname": "main.askai.core.component.image_store.ImageMetadata.name", "modulename": "main.askai.core.component.image_store", "qualname": "ImageMetadata.name", "kind": "variable", "doc": "

    Alias for field number 0

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageMetadata.data", "modulename": "main.askai.core.component.image_store", "qualname": "ImageMetadata.data", "kind": "variable", "doc": "

    Alias for field number 1

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageMetadata.uri", "modulename": "main.askai.core.component.image_store", "qualname": "ImageMetadata.uri", "kind": "variable", "doc": "

    Alias for field number 2

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageMetadata.distance", "modulename": "main.askai.core.component.image_store", "qualname": "ImageMetadata.distance", "kind": "variable", "doc": "

    Alias for field number 3

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageStore", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore", "kind": "class", "doc": "

    Provide an interface to capture WebCam photos.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.image_store.ImageStore.__init__", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.image_store.ImageStore.INSTANCE", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.image_store.ImageStore"}, {"fullname": "main.askai.core.component.image_store.ImageStore.COLLECTION_NAME", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.COLLECTION_NAME", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'image_store'"}, {"fullname": "main.askai.core.component.image_store.ImageStore.PHOTO_CATEGORY", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.PHOTO_CATEGORY", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'photos'"}, {"fullname": "main.askai.core.component.image_store.ImageStore.FACE_CATEGORY", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.FACE_CATEGORY", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'faces'"}, {"fullname": "main.askai.core.component.image_store.ImageStore.IMPORTS_CATEGORY", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.IMPORTS_CATEGORY", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'imports'"}, {"fullname": "main.askai.core.component.image_store.ImageStore.sync_folders", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.sync_folders", "kind": "function", "doc": "

    Load image files from the specified directories.

    \n", "signature": "(\twith_caption: bool = False,\t*dirs: Union[pathlib.Path, str, NoneType]) -> list[main.askai.core.component.image_store.ImageFile]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.persist_dir", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.persist_dir", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path"}, {"fullname": "main.askai.core.component.image_store.ImageStore.metadatas", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.metadatas", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[list[Mapping[str, str | int | float | bool]]]"}, {"fullname": "main.askai.core.component.image_store.ImageStore.enlist", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.enlist", "kind": "function", "doc": "

    \n", "signature": "(self) -> Optional[list[str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.store_image", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.store_image", "kind": "function", "doc": "

    Add the faces into the image store collection.

    \n", "signature": "(\tself,\t*face_files: main.askai.core.component.image_store.ImageFile) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.clear_store", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.clear_store", "kind": "function", "doc": "

    Clear the image store collection.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.sync_store", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.sync_store", "kind": "function", "doc": "

    Synchronize image store collection with the picture folder.

    \n", "signature": "(self, with_caption: bool = False) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.query_image", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.query_image", "kind": "function", "doc": "

    Query for a photo matching the query.

    \n", "signature": "(\tself,\tquery: str,\tmax_results: int = 1) -> list[main.askai.core.component.image_store.ImageMetadata]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.query_face", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.query_face", "kind": "function", "doc": "

    Query for a face matching the query.

    \n", "signature": "(\tself,\tquery: str,\tmax_results: int = 1) -> list[main.askai.core.component.image_store.ImageMetadata]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.search_image", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.search_image", "kind": "function", "doc": "

    Search for images using natural language.

    \n", "signature": "(\tself,\tquery: str,\tcategories: list[str],\tmax_results: int = 1) -> list[main.askai.core.component.image_store.ImageMetadata]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.search_face", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.search_face", "kind": "function", "doc": "

    Search for faces matching the provided photo using similarity methods.

    \n", "signature": "(\tself,\tphoto: numpy.ndarray[typing.Any, numpy.dtype],\tmax_results: int = 1) -> list[main.askai.core.component.image_store.ImageMetadata]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.internet_service", "modulename": "main.askai.core.component.internet_service", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.component\n @file: internet_service.py\n@created: Sun, 10 Mar 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.internet_service.InternetService", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService", "kind": "class", "doc": "

    Provide a internet search service to complete queries that require realtime data.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.internet_service.InternetService.__init__", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.internet_service.InternetService.INSTANCE", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.internet_service.InternetService"}, {"fullname": "main.askai.core.component.internet_service.InternetService.ASKAI_INTERNET_DATA_KEY", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.ASKAI_INTERNET_DATA_KEY", "kind": "variable", "doc": "

    \n", "default_value": "'askai-internet-data'"}, {"fullname": "main.askai.core.component.internet_service.InternetService.wrap_response", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.wrap_response", "kind": "function", "doc": "

    \n", "signature": "(terms: str, output: str, sites: list[str]) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.component.internet_service.InternetService.refine_template", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.refine_template", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.component.internet_service.InternetService.search_google", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.search_google", "kind": "function", "doc": "

    Search the web using google search API.\nGoogle search operators: https://ahrefs.com/blog/google-advanced-search-operators/

    \n\n
    Parameters
    \n\n
      \n
    • search: The AI search parameters.
    • \n
    \n", "signature": "(self, search: askai.core.model.search_result.SearchResult) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.component.internet_service.InternetService.scrap_sites", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.scrap_sites", "kind": "function", "doc": "

    Scrap a web page and summarize it's contents.

    \n\n
    Parameters
    \n\n
      \n
    • search: The AI search parameters.
    • \n
    \n", "signature": "(self, search: askai.core.model.search_result.SearchResult) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.component.internet_service.InternetService.refine_search", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.refine_search", "kind": "function", "doc": "

    Refines the text retrieved by the search engine.

    \n\n
    Parameters
    \n\n
      \n
    • question: The user question, used to refine the context.
    • \n
    • result: The search result to refine.
    • \n
    • terms: The terms used on the Google search.
    • \n
    • sites: The list of source sites used on the search.
    • \n
    \n", "signature": "(self, question: str, result: str, terms: str, sites: list[str]) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.component.recorder", "modulename": "main.askai.core.component.recorder", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.components\n @file: recorder.py\n@created: Wed, 22 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.recorder.InputDevice", "modulename": "main.askai.core.component.recorder", "qualname": "InputDevice", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "tuple[int, str]"}, {"fullname": "main.askai.core.component.recorder.Recorder", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder", "kind": "class", "doc": "

    Provide an interface to record voice using the microphone device.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.recorder.Recorder.__init__", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.recorder.Recorder.INSTANCE", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.recorder.Recorder"}, {"fullname": "main.askai.core.component.recorder.Recorder.RecognitionApi", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.RecognitionApi", "kind": "class", "doc": "

    Extended enumeration type

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.component.recorder.Recorder.RecognitionApi.OPEN_AI", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.RecognitionApi.OPEN_AI", "kind": "variable", "doc": "

    \n", "default_value": "OPEN_AI"}, {"fullname": "main.askai.core.component.recorder.Recorder.RecognitionApi.GOOGLE", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.RecognitionApi.GOOGLE", "kind": "variable", "doc": "

    \n", "default_value": "GOOGLE"}, {"fullname": "main.askai.core.component.recorder.Recorder.get_device_list", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.get_device_list", "kind": "function", "doc": "

    Return a list of available devices.

    \n", "signature": "(cls) -> list[tuple[int, str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.recorder.Recorder.setup", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.setup", "kind": "function", "doc": "

    Setup the recorder.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.recorder.Recorder.devices", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.devices", "kind": "variable", "doc": "

    \n", "annotation": ": list[tuple[int, str]]"}, {"fullname": "main.askai.core.component.recorder.Recorder.input_device", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.input_device", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[tuple[int, str]]"}, {"fullname": "main.askai.core.component.recorder.Recorder.device_index", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.device_index", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[int]"}, {"fullname": "main.askai.core.component.recorder.Recorder.is_auto_swap", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.is_auto_swap", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.component.recorder.Recorder.set_device", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.set_device", "kind": "function", "doc": "

    Test and set the specified device as current.

    \n\n
    Parameters
    \n\n
      \n
    • device: rge device to set.
    • \n
    \n", "signature": "(self, device: tuple[int, str]) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.component.recorder.Recorder.is_headphones", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.is_headphones", "kind": "function", "doc": "

    Whether the device is set is a headphone. This is a simplistic way of detecting it, bit it has been\nworking so far.

    \n", "signature": "(self) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.component.recorder.Recorder.listen", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.listen", "kind": "function", "doc": "

    listen to the microphone, save the AudioData as a wav file and then, transcribe the speech.

    \n\n
    Parameters
    \n\n
      \n
    • recognition_api: the API to be used to recognize the speech.
    • \n
    • language: the spoken language.
    • \n
    \n", "signature": "(\tself,\trecognition_api: main.askai.core.component.recorder.Recorder.RecognitionApi = GOOGLE,\tlanguage: askai.language.language.Language = English) -> tuple[pathlib.Path, typing.Optional[str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.recorder.Recorder.test_device", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.test_device", "kind": "function", "doc": "

    Test whether the input device specified by index can be used as an STT input.

    \n\n
    Parameters
    \n\n
      \n
    • idx: The index of the device to be tested.
    • \n
    \n", "signature": "(self, idx: int) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.component.scheduler", "modulename": "main.askai.core.component.scheduler", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.component.scheduler\n @file: scheduler.py\n@created: Thu, 25 Mar 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.scheduler.Scheduler", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler", "kind": "class", "doc": "

    Provide a scheduler class.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.__init__", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.INSTANCE", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.scheduler.Scheduler"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.every", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.every", "kind": "function", "doc": "

    Decorate a function to be run every interval_ms seconds. Can't be used to decorate instance\nmethods (with self). For that use the set_interval method.

    \n", "signature": "(interval_ms: int, delay_ms: int):", "funcdef": "def"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.at", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.at", "kind": "function", "doc": "

    Decorate a function to run at a specific time. Can't be used to decorate instance\nmethods (with self). For that use the schedule method.

    \n", "signature": "(hour: int, minute: int, second: int, millis: int):", "funcdef": "def"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.run", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.run", "kind": "function", "doc": "

    Method representing the thread's activity.

    \n\n

    You may override this method in a subclass. The standard run() method\ninvokes the callable object passed to the object's constructor as the\ntarget argument, if any, with sequential and keyword arguments taken\nfrom the args and kwargs arguments, respectively.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.start", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.start", "kind": "function", "doc": "

    Start the thread's activity.

    \n\n

    It must be called at most once per thread object. It arranges for the\nobject's run() method to be invoked in a separate thread of control.

    \n\n

    This method will raise a RuntimeError if called more than once on the\nsame thread object.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.schedule", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.schedule", "kind": "function", "doc": "

    Schedule a task to run at specific time in future.

    \n\n
    Parameters
    \n\n
      \n
    • hh: The hour of the day.
    • \n
    • mm: The minute of the day.
    • \n
    • ss: The seconds of the day.
    • \n
    • us: The microseconds of the day.
    • \n
    • callback: The callback function.
    • \n
    • cb_fn_args: The arguments of the callback function.
    • \n
    • cb_fn_kwargs: The keyword arguments of the callback function.
    • \n
    \n", "signature": "(\tself,\thh: int,\tmm: int,\tss: int,\tus: int,\tcallback: Callable,\tcb_fn_args: Optional[Iterable] = None,\tcb_fn_kwargs: Optional[Mapping[str, Any]] = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.set_interval", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.set_interval", "kind": "function", "doc": "

    Schedule a task to run every interval in milliseconds.

    \n\n
    Parameters
    \n\n
      \n
    • interval_ms: The interval in milliseconds, to invoke the callback function.
    • \n
    • callback: The callback function.
    • \n
    • delay_ms: The initial delay before start invoking.
    • \n
    • cb_fn_args: The arguments of the callback function.
    • \n
    • cb_fn_kwargs: The keyword arguments of the callback function.
    • \n
    \n", "signature": "(\tself,\tinterval_ms: int,\tcallback: Callable,\tdelay_ms: int = 0,\tcb_fn_args: Optional[Iterable] = None,\tcb_fn_kwargs: Optional[Mapping[str, Any]] = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.summarizer", "modulename": "main.askai.core.component.summarizer", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.component\n @file: summarizer.py\n@created: Mon, 11 Mar 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.summarizer.Summarizer", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer", "kind": "class", "doc": "

    Provide a summarization service to complete queries that require summarization.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.__init__", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.INSTANCE", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.summarizer.Summarizer"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.extract_result", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.extract_result", "kind": "function", "doc": "

    Extract the question and answer from the summarization result.

    \n", "signature": "(result: dict) -> tuple[str, str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.persist_dir", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.persist_dir", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.folder", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.folder", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.glob", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.glob", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.sum_path", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.sum_path", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.text_splitter", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.text_splitter", "kind": "variable", "doc": "

    \n", "annotation": ": langchain_text_splitters.base.TextSplitter"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.retriever", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.retriever", "kind": "variable", "doc": "

    \n", "annotation": ": langchain.chains.retrieval_qa.base.RetrievalQA"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.generate", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.generate", "kind": "function", "doc": "

    Generate a summarization of the folder contents.

    \n\n
    Parameters
    \n\n
      \n
    • folder: The base folder of the summarization.
    • \n
    • glob: The glob pattern or file of the summarization.
    • \n
    \n", "signature": "(self, folder: str | pathlib.Path, glob: str) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.exists", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.exists", "kind": "function", "doc": "

    Return whether or not the summary already exists.

    \n", "signature": "(self, folder: str | pathlib.Path, glob: str) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.query", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.query", "kind": "function", "doc": "

    Answer questions about the summarized content.

    \n\n
    Parameters
    \n\n
      \n
    • queries: The queries to ask the AI engine.
    • \n
    \n", "signature": "(\tself,\t*queries: str) -> Optional[list[askai.core.model.summary_result.SummaryResult]]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine", "modulename": "main.askai.core.engine", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.engine.ai_engine", "modulename": "main.askai.core.engine.ai_engine", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model\n @file: ai_engine.py\n@created: Fri, 5 May 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine", "kind": "class", "doc": "

    Provide an interface for AI engines.

    \n", "bases": "typing.Protocol"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.__init__", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.__init__", "kind": "function", "doc": "

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.configs", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.configs", "kind": "function", "doc": "

    Return the engine specific configurations.

    \n", "signature": "(self) -> askai.core.askai_configs.AskAiConfigs:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.lc_model", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.lc_model", "kind": "function", "doc": "

    Create a LangChain AI model instance.

    \n\n
    Parameters
    \n\n
      \n
    • temperature: The model engine temperature.
    • \n
    • top_p: The model engine top_p.
    • \n
    \n", "signature": "(\tself,\ttemperature: float,\ttop_p: float) -> langchain_core.language_models.llms.BaseLLM:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.lc_chat_model", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.lc_chat_model", "kind": "function", "doc": "

    Create a LangChain OpenAI llm chat model instance.

    \n\n
    Parameters
    \n\n
      \n
    • temperature: The model engine temperature.
    • \n
    \n", "signature": "(\tself,\ttemperature: float = 0.0) -> langchain_core.language_models.chat_models.BaseChatModel:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.lc_embeddings", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.lc_embeddings", "kind": "function", "doc": "

    Create a LangChain AI embeddings instance.

    \n", "signature": "(self, model: str) -> Any:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.ai_name", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.ai_name", "kind": "function", "doc": "

    Get the AI engine name.

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.ai_model_name", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.ai_model_name", "kind": "function", "doc": "

    Get the AI model name.

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.ai_token_limit", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.ai_token_limit", "kind": "function", "doc": "

    Get the AI model tokens limit.

    \n", "signature": "(self) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.nickname", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.nickname", "kind": "function", "doc": "

    Get the AI engine nickname.

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.models", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.models", "kind": "function", "doc": "

    Get the list of available models for the engine.

    \n", "signature": "(self) -> List[askai.core.engine.ai_model.AIModel]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.ask", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.ask", "kind": "function", "doc": "

    Ask AI assistance for the given question and expect a response.

    \n\n
    Parameters
    \n\n
      \n
    • chat_context: The chat history or context.
    • \n
    • temperature: The model engine temperature.
    • \n
    • top_p: The model engine top_p.
    • \n
    \n", "signature": "(\tself,\tchat_context: List[dict],\ttemperature: float = 0.8,\ttop_p: float = 0.0) -> askai.core.engine.ai_reply.AIReply:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.text_to_speech", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.text_to_speech", "kind": "function", "doc": "

    Text-T0-Speech the provided text.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to speech.
    • \n
    • prefix: The prefix of the streamed text.
    • \n
    • stream: Whether to stream the text into stdout.
    • \n
    • playback: Whether to playback the generated audio file.
    • \n
    \n", "signature": "(\tself,\ttext: str,\tprefix: str = '',\tstream: bool = True,\tplayback: bool = True) -> Optional[pathlib.Path]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.speech_to_text", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.speech_to_text", "kind": "function", "doc": "

    Transcribes audio input from the microphone into the text input language.

    \n", "signature": "(self) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.voices", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.voices", "kind": "function", "doc": "

    Return the available model voices for speech to text.

    \n", "signature": "(self) -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.calculate_tokens", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.calculate_tokens", "kind": "function", "doc": "

    Calculate the number of tokens for the given text.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to base the token calculation.
    • \n
    \n", "signature": "(text: str) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_model", "modulename": "main.askai.core.engine.ai_model", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model\n @file: ai_model.py\n@created: Fri, 5 May 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.ai_model.AIModel", "modulename": "main.askai.core.engine.ai_model", "qualname": "AIModel", "kind": "class", "doc": "

    Provide an interface for AI models.

    \n", "bases": "typing.Protocol"}, {"fullname": "main.askai.core.engine.ai_model.AIModel.__init__", "modulename": "main.askai.core.engine.ai_model", "qualname": "AIModel.__init__", "kind": "function", "doc": "

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.engine.ai_model.AIModel.model_name", "modulename": "main.askai.core.engine.ai_model", "qualname": "AIModel.model_name", "kind": "function", "doc": "

    Get the official model's name.

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_model.AIModel.token_limit", "modulename": "main.askai.core.engine.ai_model", "qualname": "AIModel.token_limit", "kind": "function", "doc": "

    Get the official model tokens limit.

    \n", "signature": "(self) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_reply", "modulename": "main.askai.core.engine.ai_reply", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.engine.model\n @file: ai_reply.py\n@created: Fri, 12 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.ai_reply.AIReply", "modulename": "main.askai.core.engine.ai_reply", "qualname": "AIReply", "kind": "class", "doc": "

    Data class that represent AI replies.

    \n"}, {"fullname": "main.askai.core.engine.ai_reply.AIReply.__init__", "modulename": "main.askai.core.engine.ai_reply", "qualname": "AIReply.__init__", "kind": "function", "doc": "

    \n", "signature": "(message: str = None, is_success: bool = None)"}, {"fullname": "main.askai.core.engine.ai_reply.AIReply.message", "modulename": "main.askai.core.engine.ai_reply", "qualname": "AIReply.message", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.engine.ai_reply.AIReply.is_success", "modulename": "main.askai.core.engine.ai_reply", "qualname": "AIReply.is_success", "kind": "variable", "doc": "

    \n", "annotation": ": bool", "default_value": "None"}, {"fullname": "main.askai.core.engine.engine_factory", "modulename": "main.askai.core.engine.engine_factory", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.engine\n @file: engine_factory.py\n@created: Tue, 23 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.engine_factory.EngineFactory", "modulename": "main.askai.core.engine.engine_factory", "qualname": "EngineFactory", "kind": "class", "doc": "

    TODO

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.engine.engine_factory.EngineFactory.__init__", "modulename": "main.askai.core.engine.engine_factory", "qualname": "EngineFactory.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.engine.engine_factory.EngineFactory.INSTANCE", "modulename": "main.askai.core.engine.engine_factory", "qualname": "EngineFactory.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.engine.engine_factory.EngineFactory"}, {"fullname": "main.askai.core.engine.engine_factory.EngineFactory.create_engine", "modulename": "main.askai.core.engine.engine_factory", "qualname": "EngineFactory.create_engine", "kind": "function", "doc": "

    Find the suitable AI engine according to the provided engine name.

    \n\n
    Parameters
    \n\n
      \n
    • engine_name: the AI engine name.
    • \n
    • engine_model: the AI engine model.
    • \n
    \n", "signature": "(\tcls,\tengine_name: Union[str, List[str]],\tengine_model: Union[str, List[str]]) -> askai.core.engine.ai_engine.AIEngine:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.engine_factory.EngineFactory.active_ai", "modulename": "main.askai.core.engine.engine_factory", "qualname": "EngineFactory.active_ai", "kind": "function", "doc": "

    \n", "signature": "(cls) -> askai.core.engine.ai_engine.AIEngine:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai", "modulename": "main.askai.core.engine.openai", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.engine.openai.openai_configs", "modulename": "main.askai.core.engine.openai.openai_configs", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.engine.openai\n @file: openai_configs.py\n@created: Fri, 12 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs", "kind": "class", "doc": "

    Provides access to OpenAI configurations.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs.__init__", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs.INSTANCE", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.engine.openai.openai_configs.OpenAiConfigs", "default_value": "<askai.core.askai_configs.AskAiConfigs object>"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs.RESOURCE_DIR", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs.RESOURCE_DIR", "kind": "variable", "doc": "

    \n", "default_value": "'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources'"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs.stt_model", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs.stt_model", "kind": "variable", "doc": "

    \n", "annotation": ": Literal['whisper-1']"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs.tts_model", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs.tts_model", "kind": "variable", "doc": "

    \n", "annotation": ": Literal['tts-1', 'tts-1-hd']"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs.tts_voice", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs.tts_voice", "kind": "variable", "doc": "

    \n", "annotation": ": Literal['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer']"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs.tts_format", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs.tts_format", "kind": "variable", "doc": "

    \n", "annotation": ": Literal['mp3', 'opus', 'aac', 'flac']"}, {"fullname": "main.askai.core.engine.openai.openai_engine", "modulename": "main.askai.core.engine.openai.openai_engine", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.engine.openai\n @file: openai_engine.py\n@created: Fri, 12 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine", "kind": "class", "doc": "

    Provide a base class for OpenAI features. Implements the prototype AIEngine.

    \n"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.__init__", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.__init__", "kind": "function", "doc": "

    \n", "signature": "(model: askai.core.engine.ai_model.AIModel = GPT_4_O_MINI)"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.url", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.url", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.client", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.client", "kind": "variable", "doc": "

    \n", "annotation": ": openai.OpenAI"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.configs", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.configs", "kind": "function", "doc": "

    \n", "signature": "(self) -> askai.core.engine.openai.openai_configs.OpenAiConfigs:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.voices", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.voices", "kind": "function", "doc": "

    \n", "signature": "(self) -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.lc_model", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.lc_model", "kind": "function", "doc": "

    Create a LangChain OpenAI llm model instance.

    \n", "signature": "(\tself,\ttemperature: float,\ttop_p: float) -> langchain_core.language_models.llms.BaseLLM:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.lc_chat_model", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.lc_chat_model", "kind": "function", "doc": "

    Create a LangChain OpenAI llm chat model instance.

    \n", "signature": "(\tself,\ttemperature: float) -> langchain_core.language_models.chat_models.BaseChatModel:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.lc_embeddings", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.lc_embeddings", "kind": "function", "doc": "

    Create a LangChain AI embeddings instance.

    \n", "signature": "(self, model: str) -> langchain_core.embeddings.embeddings.Embeddings:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.ai_name", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.ai_name", "kind": "function", "doc": "

    Get the AI model name.

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.ai_model_name", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.ai_model_name", "kind": "function", "doc": "

    Get the AI model name.

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.ai_token_limit", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.ai_token_limit", "kind": "function", "doc": "

    Get the AI model tokens limit.

    \n", "signature": "(self) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.nickname", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.nickname", "kind": "function", "doc": "

    Get the AI engine nickname.

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.models", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.models", "kind": "function", "doc": "

    Get the list of available models for the engine.

    \n", "signature": "(self) -> List[askai.core.engine.ai_model.AIModel]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.ask", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.ask", "kind": "function", "doc": "

    Ask AI assistance for the given question and expect a response.

    \n\n
    Parameters
    \n\n
      \n
    • chat_context: The chat history or context.
    • \n
    • temperature: The model engine temperature.
    • \n
    • top_p: The model engine top_p.
    • \n
    \n", "signature": "(\tself,\tchat_context: List[dict],\ttemperature: float = 0.8,\ttop_p: float = 0.0) -> askai.core.engine.ai_reply.AIReply:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.text_to_speech", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.text_to_speech", "kind": "function", "doc": "

    Text-T0-Speech the provided text.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to speech.
    • \n
    • prefix: The prefix of the streamed text.
    • \n
    • stream: Whether to stream the text into stdout.
    • \n
    • playback: Whether to playback the generated audio file.
    • \n
    \n", "signature": "(\tself,\ttext: str,\tprefix: str = '',\tstream: bool = True,\tplayback: bool = True) -> Optional[pathlib.Path]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.speech_to_text", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.speech_to_text", "kind": "function", "doc": "

    Transcribes audio input from the microphone into the text input language.

    \n", "signature": "(self) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.calculate_tokens", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.calculate_tokens", "kind": "function", "doc": "

    Calculate the number of tokens for the given text.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to base the token calculation.
    • \n
    \n", "signature": "(self, text: str) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_model", "modulename": "main.askai.core.engine.openai.openai_model", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.engine.openai\n @file: openai_model.py\n@created: Fri, 12 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel", "kind": "class", "doc": "

    Enumeration for the supported OpenAi models. Implements the AIModel protocol.\nDetails at: https://www.pluralsight.com/resources/blog/data/ai-gpt-models-differences

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_3_5_TURBO", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_3_5_TURBO", "kind": "variable", "doc": "

    \n", "default_value": "GPT_3_5_TURBO"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_3_5_TURBO_16K", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_3_5_TURBO_16K", "kind": "variable", "doc": "

    \n", "default_value": "GPT_3_5_TURBO_16K"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_3_5_TURBO_1106", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_3_5_TURBO_1106", "kind": "variable", "doc": "

    \n", "default_value": "GPT_3_5_TURBO_1106"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_3_5_TURBO_0301", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_3_5_TURBO_0301", "kind": "variable", "doc": "

    \n", "default_value": "GPT_3_5_TURBO_0301"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_3_5_TURBO_0613", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_3_5_TURBO_0613", "kind": "variable", "doc": "

    \n", "default_value": "GPT_3_5_TURBO_0613"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_3_5_TURBO_16K_0613", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_3_5_TURBO_16K_0613", "kind": "variable", "doc": "

    \n", "default_value": "GPT_3_5_TURBO_16K_0613"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_4", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_4", "kind": "variable", "doc": "

    \n", "default_value": "GPT_4"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_4_O", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_4_O", "kind": "variable", "doc": "

    \n", "default_value": "GPT_4_O"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_4_O_MINI", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_4_O_MINI", "kind": "variable", "doc": "

    \n", "default_value": "GPT_4_O_MINI"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_4_0314", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_4_0314", "kind": "variable", "doc": "

    \n", "default_value": "GPT_4_0314"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_4_0613", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_4_0613", "kind": "variable", "doc": "

    \n", "default_value": "GPT_4_0613"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_4_32K", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_4_32K", "kind": "variable", "doc": "

    \n", "default_value": "GPT_4_32K"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_4_32K_0314", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_4_32K_0314", "kind": "variable", "doc": "

    \n", "default_value": "GPT_4_32K_0314"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_4_32K_0613", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_4_32K_0613", "kind": "variable", "doc": "

    \n", "default_value": "GPT_4_32K_0613"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_4_1106_PREVIEW", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_4_1106_PREVIEW", "kind": "variable", "doc": "

    \n", "default_value": "GPT_4_1106_PREVIEW"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_4_VISION_PREVIEW", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_4_VISION_PREVIEW", "kind": "variable", "doc": "

    \n", "default_value": "GPT_4_VISION_PREVIEW"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.models", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.models", "kind": "function", "doc": "

    \n", "signature": "() -> List[askai.core.engine.ai_model.AIModel]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.of_name", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.of_name", "kind": "function", "doc": "

    \n", "signature": "(model_name: str) -> askai.core.engine.ai_model.AIModel:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.model_name", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.model_name", "kind": "function", "doc": "

    Get the official model's name.

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.token_limit", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.token_limit", "kind": "function", "doc": "

    Get the official model tokens limit.

    \n", "signature": "(self) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.temperature", "modulename": "main.askai.core.engine.openai.temperature", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.engine.openai\n @file: temperature.py\n@created: Tue, 23 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature", "kind": "class", "doc": "

    Provide some recommended temperature x top_p combinations for ChatGPT prompts.\nRef:. https://community.openai.com/t/cheat-sheet-mastering-temperature-and-top-p-in-chatgpt-api/172683

    \n\n
      \n
    • Lower temperature (e.g., 0.1 - 0.4): Produces more focused, conservative, and consistent responses.\nThis is useful when the marketer needs factual information, precise answers, or messaging that adheres closely\nto a specific format or brand guideline.

    • \n
    • Moderate temperature (e.g., 0.5 - 0.7): Strikes a balance between creativity and consistency.\nThis setting can be useful for general content generation, where a blend of accuracy and inventiveness\nis desired.

    • \n
    • Higher temperature (e.g., 0.8 - 1.0): Generates more creative, diverse, and unexpected outputs.\nMarketers may prefer this setting when brainstorming innovative campaign ideas, crafting engaging social media\ncontent, or seeking fresh perspectives on a topic.

    • \n
    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.COLDEST", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.COLDEST", "kind": "variable", "doc": "

    \n", "default_value": "COLDEST"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.DATA_ANALYSIS", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.DATA_ANALYSIS", "kind": "variable", "doc": "

    \n", "default_value": "DATA_ANALYSIS"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.CODE_GENERATION", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.CODE_GENERATION", "kind": "variable", "doc": "

    \n", "default_value": "DATA_ANALYSIS"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.CODE_COMMENT_GENERATION", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.CODE_COMMENT_GENERATION", "kind": "variable", "doc": "

    \n", "default_value": "CODE_COMMENT_GENERATION"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.CHATBOT_RESPONSES", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.CHATBOT_RESPONSES", "kind": "variable", "doc": "

    \n", "default_value": "CHATBOT_RESPONSES"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.EXPLORATORY_CODE_WRITING", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.EXPLORATORY_CODE_WRITING", "kind": "variable", "doc": "

    \n", "default_value": "EXPLORATORY_CODE_WRITING"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.CREATIVE_WRITING", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.CREATIVE_WRITING", "kind": "variable", "doc": "

    \n", "default_value": "CREATIVE_WRITING"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.HOTTEST", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.HOTTEST", "kind": "variable", "doc": "

    \n", "default_value": "HOTTEST"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.temp", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.temp", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.top_p", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.top_p", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.enums", "modulename": "main.askai.core.enums", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.enums.acc_response", "modulename": "main.askai.core.enums.acc_response", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.enums.acc_response\n @file: acc_response.py\n@created: Tue, 23 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse", "kind": "class", "doc": "

    Keep track of the accuracy responses (color classifications).

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.EXCELLENT", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.EXCELLENT", "kind": "variable", "doc": "

    \n", "default_value": "EXCELLENT"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.GOOD", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.GOOD", "kind": "variable", "doc": "

    \n", "default_value": "GOOD"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.MODERATE", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.MODERATE", "kind": "variable", "doc": "

    \n", "default_value": "MODERATE"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.INCOMPLETE", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.INCOMPLETE", "kind": "variable", "doc": "

    \n", "default_value": "INCOMPLETE"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.BAD", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.BAD", "kind": "variable", "doc": "

    \n", "default_value": "BAD"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.matches", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.matches", "kind": "function", "doc": "

    \n", "signature": "(cls, output: str) -> re.Match:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.strip_code", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.strip_code", "kind": "function", "doc": "

    Strip the color code from the message

    \n", "signature": "(cls, message: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.of_status", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.of_status", "kind": "function", "doc": "

    \n", "signature": "(\tcls,\tstatus: str,\treasoning: str | None) -> main.askai.core.enums.acc_response.AccResponse:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.color", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.color", "kind": "variable", "doc": "

    \n"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.reasoning", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.reasoning", "kind": "variable", "doc": "

    \n", "annotation": ": str | None"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.rate", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.rate", "kind": "variable", "doc": "

    \n", "annotation": ": float | None"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.is_bad", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.is_bad", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.is_moderate", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.is_moderate", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.is_good", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.is_good", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.passed", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.passed", "kind": "function", "doc": "

    whether the response matches a 'PASS' classification.

    \n", "signature": "(self, threshold: main.askai.core.enums.acc_response.AccResponse) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.router_mode", "modulename": "main.askai.core.enums.router_mode", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.enums.router_mode\n @file: router_mode.py\n@created: Tue, 24 Jun 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode", "kind": "class", "doc": "

    The available router modes used to provide an answer to the user.

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.TASK_SPLIT", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.TASK_SPLIT", "kind": "variable", "doc": "

    \n", "default_value": "Task Splitter"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.QNA", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.QNA", "kind": "variable", "doc": "

    \n", "default_value": "Questions and Answers"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.QSTRING", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.QSTRING", "kind": "variable", "doc": "

    \n", "default_value": "Non-Interactive"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.RAG", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.RAG", "kind": "variable", "doc": "

    \n", "default_value": "Retrieval-Augmented Generation"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.modes", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.modes", "kind": "function", "doc": "

    Return a list containing al available agent modes.

    \n", "signature": "(cls) -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.default", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.default", "kind": "function", "doc": "

    Return the default routing mode.

    \n", "signature": "() -> main.askai.core.enums.router_mode.RouterMode:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.of_name", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.of_name", "kind": "function", "doc": "

    \n", "signature": "(cls, name: str) -> main.askai.core.enums.router_mode.RouterMode:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.name", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.name", "kind": "variable", "doc": "

    The name of the Enum member.

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.processor", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.processor", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.features.router.ai_processor.AIProcessor"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.is_default", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.is_default", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.process", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.process", "kind": "function", "doc": "

    Invoke the processor associated with the mode.

    \n", "signature": "(self, question: str, **kwargs) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.routing_model", "modulename": "main.askai.core.enums.routing_model", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.enums.routing_model\n @file: routing_model.py\n@created: Tue, 11 Jun 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel", "kind": "class", "doc": "

    The model used to provide the final answer to the user.

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.NEUTRAL", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.NEUTRAL", "kind": "variable", "doc": "

    \n", "default_value": "NEUTRAL::ASK_000"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.ASSISTIVE_TECH_HELPER", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.ASSISTIVE_TECH_HELPER", "kind": "variable", "doc": "

    \n", "default_value": "ASSISTIVE_TECH_HELPER::ASK_001"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.TERMINAL_COMMAND", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.TERMINAL_COMMAND", "kind": "variable", "doc": "

    \n", "default_value": "TERMINAL_COMMAND::ASK_002"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.CONTENT_MASTER", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.CONTENT_MASTER", "kind": "variable", "doc": "

    \n", "default_value": "CONTENT_MASTER::ASK_003"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.TEXT_ANALYZER", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.TEXT_ANALYZER", "kind": "variable", "doc": "

    \n", "default_value": "TEXT_ANALYZER::ASK_004"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.DATA_ANALYSIS", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.DATA_ANALYSIS", "kind": "variable", "doc": "

    \n", "default_value": "DATA_ANALYSIS::ASK_005"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.CHAT_MASTER", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.CHAT_MASTER", "kind": "variable", "doc": "

    \n", "default_value": "CHAT_MASTER::ASK_006"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.MEDIA_MANAGEMENT_AND_PLAYBACK", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.MEDIA_MANAGEMENT_AND_PLAYBACK", "kind": "variable", "doc": "

    \n", "default_value": "MEDIA_MANAGEMENT_AND_PLAYBACK::ASK_007"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.IMAGE_PROCESSOR", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.IMAGE_PROCESSOR", "kind": "variable", "doc": "

    \n", "default_value": "IMAGE_PROCESSOR::ASK_008"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.SUMMARIZE_AND_QUERY", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.SUMMARIZE_AND_QUERY", "kind": "variable", "doc": "

    \n", "default_value": "SUMMARIZE_AND_QUERY::ASK_009"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.WEB_FETCH", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.WEB_FETCH", "kind": "variable", "doc": "

    \n", "default_value": "WEB_FETCH::ASK_010"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.FINAL_ANSWER", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.FINAL_ANSWER", "kind": "variable", "doc": "

    \n", "default_value": "FINAL_ANSWER::ASK_011"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.REFINER", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.REFINER", "kind": "variable", "doc": "

    \n", "default_value": "REFINER::ASK_012"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.of_model", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.of_model", "kind": "function", "doc": "

    Returning teh matching model ID instance.

    \n", "signature": "(cls, model_id: str) -> main.askai.core.enums.routing_model.RoutingModel:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.enlist", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.enlist", "kind": "function", "doc": "

    Return a list of selectable models.

    \n", "signature": "(cls, separator: str = '\\n') -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.model", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.model", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.description", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.description", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.features", "modulename": "main.askai.core.features", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.features.router", "modulename": "main.askai.core.features.router", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.features.router.ai_processor", "modulename": "main.askai.core.features.router.ai_processor", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.features.router\n @file: ai_engine.py\n@created: Fri, 5 May 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.router.ai_processor.AIProcessor", "modulename": "main.askai.core.features.router.ai_processor", "qualname": "AIProcessor", "kind": "class", "doc": "

    Provide an interface for AI processors (routing modes).

    \n", "bases": "typing.Protocol"}, {"fullname": "main.askai.core.features.router.ai_processor.AIProcessor.__init__", "modulename": "main.askai.core.features.router.ai_processor", "qualname": "AIProcessor.__init__", "kind": "function", "doc": "

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.router.ai_processor.AIProcessor.process", "modulename": "main.askai.core.features.router.ai_processor", "qualname": "AIProcessor.process", "kind": "function", "doc": "

    Process a user query.

    \n", "signature": "(self, question: str, **kwargs) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.model_selector", "modulename": "main.askai.core.features.router.model_selector", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.router.model_selector\n @file: model_selector.py\n@created: Tue, 24 Jun 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.router.model_selector.ModelSelector", "modulename": "main.askai.core.features.router.model_selector", "qualname": "ModelSelector", "kind": "class", "doc": "

    TODO

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.features.router.model_selector.ModelSelector.__init__", "modulename": "main.askai.core.features.router.model_selector", "qualname": "ModelSelector.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.router.model_selector.ModelSelector.INSTANCE", "modulename": "main.askai.core.features.router.model_selector", "qualname": "ModelSelector.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.features.router.model_selector.ModelSelector", "default_value": "<main.askai.core.features.router.model_selector.ModelSelector object>"}, {"fullname": "main.askai.core.features.router.model_selector.ModelSelector.model_template", "modulename": "main.askai.core.features.router.model_selector", "qualname": "ModelSelector.model_template", "kind": "variable", "doc": "

    Retrieve the Routing Model Template.

    \n", "annotation": ": langchain_core.prompts.prompt.PromptTemplate"}, {"fullname": "main.askai.core.features.router.model_selector.ModelSelector.select_model", "modulename": "main.askai.core.features.router.model_selector", "qualname": "ModelSelector.select_model", "kind": "function", "doc": "

    Select the response model.

    \n", "signature": "(self, query: str) -> askai.core.model.model_result.ModelResult:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.model_selector.selector", "modulename": "main.askai.core.features.router.model_selector", "qualname": "selector", "kind": "variable", "doc": "

    \n", "default_value": "<main.askai.core.features.router.model_selector.ModelSelector object>"}, {"fullname": "main.askai.core.features.router.procs", "modulename": "main.askai.core.features.router.procs", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.features.router.procs.qna", "modulename": "main.askai.core.features.router.procs.qna", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.features.router.procs.qna.QnA", "modulename": "main.askai.core.features.router.procs.qna", "qualname": "QnA", "kind": "class", "doc": "

    Processor to provide a questions and answers session about a summarized context.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.features.router.procs.qna.QnA.__init__", "modulename": "main.askai.core.features.router.procs.qna", "qualname": "QnA.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.router.procs.qna.QnA.INSTANCE", "modulename": "main.askai.core.features.router.procs.qna", "qualname": "QnA.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.features.router.procs.qna.QnA"}, {"fullname": "main.askai.core.features.router.procs.qna.QnA.process", "modulename": "main.askai.core.features.router.procs.qna", "qualname": "QnA.process", "kind": "function", "doc": "

    Process the user question against a summarized context to retrieve answers.

    \n\n
    Parameters
    \n\n
      \n
    • question: The user question to process.
    • \n
    \n", "signature": "(self, question: str, **_) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.procs.qstring", "modulename": "main.askai.core.features.router.procs.qstring", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.features.router.procs.qstring.NonInteractive", "modulename": "main.askai.core.features.router.procs.qstring", "qualname": "NonInteractive", "kind": "class", "doc": "

    Processor to provide a answers from custom prompts (non-interactive).

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.features.router.procs.qstring.NonInteractive.__init__", "modulename": "main.askai.core.features.router.procs.qstring", "qualname": "NonInteractive.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.router.procs.qstring.NonInteractive.INSTANCE", "modulename": "main.askai.core.features.router.procs.qstring", "qualname": "NonInteractive.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.features.router.procs.qstring.NonInteractive"}, {"fullname": "main.askai.core.features.router.procs.qstring.NonInteractive.DEFAULT_PROMPT", "modulename": "main.askai.core.features.router.procs.qstring", "qualname": "NonInteractive.DEFAULT_PROMPT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources/prompts/taius/taius-non-interactive'"}, {"fullname": "main.askai.core.features.router.procs.qstring.NonInteractive.DEFAULT_CONTEXT", "modulename": "main.askai.core.features.router.procs.qstring", "qualname": "NonInteractive.DEFAULT_CONTEXT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'No context has been provided'"}, {"fullname": "main.askai.core.features.router.procs.qstring.NonInteractive.DEFAULT_TEMPERATURE", "modulename": "main.askai.core.features.router.procs.qstring", "qualname": "NonInteractive.DEFAULT_TEMPERATURE", "kind": "variable", "doc": "

    \n", "annotation": ": int", "default_value": "0.7"}, {"fullname": "main.askai.core.features.router.procs.qstring.NonInteractive.process", "modulename": "main.askai.core.features.router.procs.qstring", "qualname": "NonInteractive.process", "kind": "function", "doc": "

    Process the user question to retrieve the final response.

    \n\n
    Parameters
    \n\n
      \n
    • question: The user question to process.
    • \n
    \n", "signature": "(self, question: str, **kwargs) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.procs.rag", "modulename": "main.askai.core.features.router.procs.rag", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.features.router.procs.rag.Rag", "modulename": "main.askai.core.features.router.procs.rag", "qualname": "Rag", "kind": "class", "doc": "

    Processor to provide a answers from a RAG datasource.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.features.router.procs.rag.Rag.__init__", "modulename": "main.askai.core.features.router.procs.rag", "qualname": "Rag.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.router.procs.rag.Rag.INSTANCE", "modulename": "main.askai.core.features.router.procs.rag", "qualname": "Rag.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.features.router.procs.rag.Rag"}, {"fullname": "main.askai.core.features.router.procs.rag.Rag.process", "modulename": "main.askai.core.features.router.procs.rag", "qualname": "Rag.process", "kind": "function", "doc": "

    Process the user question to retrieve the final response.

    \n\n
    Parameters
    \n\n
      \n
    • question: The user question to process.
    • \n
    \n", "signature": "(self, question: str, **_) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.procs.task_splitter", "modulename": "main.askai.core.features.router.procs.task_splitter", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.router\n @file: task_splitter.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.router.procs.task_splitter.AgentResponse", "modulename": "main.askai.core.features.router.procs.task_splitter", "qualname": "AgentResponse", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "dict[str, typing.Any]"}, {"fullname": "main.askai.core.features.router.procs.task_splitter.TaskSplitter", "modulename": "main.askai.core.features.router.procs.task_splitter", "qualname": "TaskSplitter", "kind": "class", "doc": "

    Processor to provide a divide and conquer set of tasks to fulfill an objective. This is responsible for the\norchestration and execution of the smaller tasks.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.features.router.procs.task_splitter.TaskSplitter.__init__", "modulename": "main.askai.core.features.router.procs.task_splitter", "qualname": "TaskSplitter.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.router.procs.task_splitter.TaskSplitter.INSTANCE", "modulename": "main.askai.core.features.router.procs.task_splitter", "qualname": "TaskSplitter.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.features.router.procs.task_splitter.TaskSplitter"}, {"fullname": "main.askai.core.features.router.procs.task_splitter.TaskSplitter.HUMAN_PROMPT", "modulename": "main.askai.core.features.router.procs.task_splitter", "qualname": "TaskSplitter.HUMAN_PROMPT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": ""Human Question: '{input}'""}, {"fullname": "main.askai.core.features.router.procs.task_splitter.TaskSplitter.RETRIABLE_ERRORS", "modulename": "main.askai.core.features.router.procs.task_splitter", "qualname": "TaskSplitter.RETRIABLE_ERRORS", "kind": "variable", "doc": "

    \n", "annotation": ": tuple[typing.Type[Exception], ...]", "default_value": "(<class 'askai.exception.exceptions.InaccurateResponse'>, <class 'hspylib.core.exception.exceptions.InvalidArgumentError'>, <class 'ValueError'>, <class 'AttributeError'>, <class 'PIL.UnidentifiedImageError'>)"}, {"fullname": "main.askai.core.features.router.procs.task_splitter.TaskSplitter.template", "modulename": "main.askai.core.features.router.procs.task_splitter", "qualname": "TaskSplitter.template", "kind": "variable", "doc": "

    Retrieve the processor Template.

    \n", "annotation": ": langchain_core.prompts.chat.ChatPromptTemplate"}, {"fullname": "main.askai.core.features.router.procs.task_splitter.TaskSplitter.process", "modulename": "main.askai.core.features.router.procs.task_splitter", "qualname": "TaskSplitter.process", "kind": "function", "doc": "

    Process the user question by splitting complex tasks into smaller single actionable tasks.

    \n\n
    Parameters
    \n\n
      \n
    • question: The user question to process.
    • \n
    \n", "signature": "(self, question: str, **_) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_agent", "modulename": "main.askai.core.features.router.task_agent", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.features.router.task_agent.TaskAgent", "modulename": "main.askai.core.features.router.task_agent", "qualname": "TaskAgent", "kind": "class", "doc": "

    This Langchain agent is responsible for executing the routers tasks using the available tools.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.features.router.task_agent.TaskAgent.__init__", "modulename": "main.askai.core.features.router.task_agent", "qualname": "TaskAgent.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.router.task_agent.TaskAgent.INSTANCE", "modulename": "main.askai.core.features.router.task_agent", "qualname": "TaskAgent.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.features.router.task_agent.TaskAgent"}, {"fullname": "main.askai.core.features.router.task_agent.TaskAgent.wrap_answer", "modulename": "main.askai.core.features.router.task_agent", "qualname": "TaskAgent.wrap_answer", "kind": "function", "doc": "

    Provide a final answer to the user.

    \n\n
    Parameters
    \n\n
      \n
    • query: The user question.
    • \n
    • answer: The AI response.
    • \n
    • model_result: The selected routing model.
    • \n
    • rag: The final accuracy check (RAG) response.
    • \n
    \n", "signature": "(\tquery: str,\tanswer: str,\tmodel_result: askai.core.model.model_result.ModelResult = ModelResult(mid='ASK_000', goal='Default model', reason='Provide the answer as received by the AI'),\trag: askai.core.enums.acc_response.AccResponse | None = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_agent.TaskAgent.agent_template", "modulename": "main.askai.core.features.router.task_agent", "qualname": "TaskAgent.agent_template", "kind": "variable", "doc": "

    Retrieve the Structured Agent Template.\nRef: https://smith.langchain.com/hub/hwchase17/structured-chat-agent

    \n", "annotation": ": langchain_core.prompts.chat.ChatPromptTemplate"}, {"fullname": "main.askai.core.features.router.task_agent.TaskAgent.lc_agent", "modulename": "main.askai.core.features.router.task_agent", "qualname": "TaskAgent.lc_agent", "kind": "variable", "doc": "

    \n", "annotation": ": langchain_core.runnables.base.Runnable"}, {"fullname": "main.askai.core.features.router.task_agent.TaskAgent.invoke", "modulename": "main.askai.core.features.router.task_agent", "qualname": "TaskAgent.invoke", "kind": "function", "doc": "

    Invoke the agent to respond the given query, using the specified action plan.

    \n\n
    Parameters
    \n\n
      \n
    • query: The user question.
    • \n
    • plan: The AI action plan.
    • \n
    \n", "signature": "(self, query: str, plan: askai.core.model.action_plan.ActionPlan) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit", "modulename": "main.askai.core.features.router.task_toolkit", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.actions\n @file: task_toolkit.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit", "kind": "class", "doc": "

    This class provides the AskAI task agent tools.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.__init__", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.INSTANCE", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.features.router.task_toolkit.AgentToolkit"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.RESERVED", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.RESERVED", "kind": "variable", "doc": "

    \n", "annotation": ": list[str]", "default_value": "['tools']"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.tools", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.tools", "kind": "function", "doc": "

    Return a list of Langchain base tools.

    \n", "signature": "(self) -> list[langchain_core.tools.BaseTool]:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.browse", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.browse", "kind": "function", "doc": "

    Use this tool to browse the internet or to stay informed about the latest news and current events, especially when you require up-to-date information quickly. It is especially effective for accessing the most recent data available online.\nUsage: browse(search_query)

    \n\n
    Parameters
    \n\n
      \n
    • search_query: The web search query in string format.
    • \n
    \n", "signature": "(self, search_query: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.query_output", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.query_output", "kind": "function", "doc": "

    Use this tool to analyze textual content to identify the presence of files, folders, and applications. It is designed to process and analyze content that is already available in textual form, but not to read or extract file contents directly.\nUsage: query_output(query)

    \n\n
    Parameters
    \n\n
      \n
    • output_query: The query regarding the output. Prefer using \"Identify \".
    • \n
    \n", "signature": "(self, output_query: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.image_captioner", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.image_captioner", "kind": "function", "doc": "

    Use this tool to provide a textual description of a visual content, such as, image files.\nUsage: image_captioner(image_path)

    \n\n
    Parameters
    \n\n
      \n
    • image_path: The absolute path of the image file to be analyzed.
    • \n
    \n", "signature": "(self, image_path: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.generate_content", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.generate_content", "kind": "function", "doc": "

    Use this tool for tasks that require generating any kind of content, such as, code and text, image, etc.\nUsage: generate_content(instructions, mime_type)

    \n\n
    Parameters
    \n\n
      \n
    • instructions: The instructions for generating the content.
    • \n
    • mime_type: The generated content type (use MIME types).
    • \n
    • filepath: Optional file path for saving the content.
    • \n
    \n", "signature": "(\tself,\tinstructions: str,\tmime_type: str,\tfilepath: Union[pathlib.Path, str, NoneType]) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.save_content", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.save_content", "kind": "function", "doc": "

    Use this tool to save generated content into disk (program, script, text, image, etc).\nUsage: save_content(filepath)

    \n\n
    Parameters
    \n\n
      \n
    • filepath: The path where you want to save the content.
    • \n
    \n", "signature": "(self, filepath: Union[pathlib.Path, str, NoneType]) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.display_tool", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.display_tool", "kind": "function", "doc": "

    Name: 'Display Tool'\nDescription: Use this tool to display textual information.\nUsage: 'display_tool(text, ...repeat N times)'

    \n\n
    Parameters
    \n\n
      \n
    • texts: The comma separated list of texts to be displayed.
    • \n
    \n", "signature": "(self, texts: list[str] | str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.direct_answer", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.direct_answer", "kind": "function", "doc": "

    Use this tool to execute terminal commands or process user-provided commands.\"\nUsage: 'direct_answer(answer)'

    \n\n
    Parameters
    \n\n
      \n
    • question: The original user question.
    • \n
    • answer: Your direct answer to the user.
    • \n
    \n", "signature": "(self, question: str, answer: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.list_tool", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.list_tool", "kind": "function", "doc": "

    Name: 'List Folder'\nDescription: Use this tool to access the contents of a specified folder.\nUsage: 'list_tool(folder, filters)'

    \n\n
    Parameters
    \n\n
      \n
    • folder: The absolute path of the folder whose contents you wish to list or access.
    • \n
    • filters: Optional Parameter: Specify a comma-separated list of file glob to filter the results (e.g., \"., *.txt\").
    • \n
    \n", "signature": "(self, folder: str, filters: str | None = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.open_tool", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.open_tool", "kind": "function", "doc": "

    Use this tool to open, read (show) content of files, and also, to playback any media files.\nUsage: 'open_tool(path_name)'

    \n\n
    Parameters
    \n\n
      \n
    • path_name: The absolute file, folder, or application path name.
    • \n
    \n", "signature": "(self, path_name: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.summarize", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.summarize", "kind": "function", "doc": "

    Use this tool only when the user explicitly requests a summary of files and folders.\nUsage: summarize(folder_name, glob)

    \n\n
    Parameters
    \n\n
      \n
    • folder: The base name of the folder containing the files to be summarized.
    • \n
    • glob: The glob expression to specify files to be included for summarization.
    • \n
    \n", "signature": "(self, folder: str, glob: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.terminal", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.terminal", "kind": "function", "doc": "

    Use this tool to execute terminal commands or process user-provided commands.\"\nUsage: 'terminal(shell_type, command)'

    \n\n
    Parameters
    \n\n
      \n
    • shell_type: The type of the shell (e.g. bash, zsh, powershell, etc).
    • \n
    • command: The actual commands you wish to execute in the terminal.
    • \n
    \n", "signature": "(self, shell_type: str, command: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.shutdown", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.shutdown", "kind": "function", "doc": "

    Use this tool when the user provides a query that indicates he wants to conclude the interaction (e.g. bye, exit).\nUsage: 'shutdown(reason)'

    \n\n
    Parameters
    \n\n
      \n
    • reason: The reason for termination.
    • \n
    \n", "signature": "(self, reason: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.tools", "modulename": "main.askai.core.features.router.tools", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.features.router.tools.analysis", "modulename": "main.askai.core.features.router.tools.analysis", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.tools.analysis\n @file: analysis.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.router.tools.analysis.query_output", "modulename": "main.askai.core.features.router.tools.analysis", "qualname": "query_output", "kind": "function", "doc": "

    Handle 'Text analysis', invoking: analyze(question: str). Analyze the context and answer the question.

    \n\n
    Parameters
    \n\n
      \n
    • query: The question about the content to be analyzed.
    • \n
    • context: The context of the question.
    • \n
    \n", "signature": "(query: str, context: str = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.tools.browser", "modulename": "main.askai.core.features.router.tools.browser", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.tools.browser\n @file: browser.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.router.tools.browser.browse", "modulename": "main.askai.core.features.router.tools.browser", "qualname": "browse", "kind": "function", "doc": "

    Fetch the information from the Internet.

    \n\n
    Parameters
    \n\n
      \n
    • query: The search query.
    • \n
    \n", "signature": "(query: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.tools.general", "modulename": "main.askai.core.features.router.tools.general", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.tools.general\n @file: general.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.router.tools.general.display_tool", "modulename": "main.askai.core.features.router.tools.general", "qualname": "display_tool", "kind": "function", "doc": "

    Display the given texts using markdown.

    \n\n
    Parameters
    \n\n
      \n
    • texts: The list of texts to be displayed.
    • \n
    \n", "signature": "(*texts: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.tools.general.final_answer", "modulename": "main.askai.core.features.router.tools.general", "qualname": "final_answer", "kind": "function", "doc": "

    Provide the final response to the user.

    \n\n
    Parameters
    \n\n
      \n
    • persona_prompt: The persona prompt to be used.
    • \n
    • input_variables: The prompt input variables.
    • \n
    • prompt_args: The prompt input arguments.
    • \n
    \n", "signature": "(\tpersona_prompt: str | None = None,\tinput_variables: list[str] | None = None,\t**prompt_args) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.tools.generation", "modulename": "main.askai.core.features.router.tools.generation", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.tools.generation\n @file: generation.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.router.tools.generation.generate_content", "modulename": "main.askai.core.features.router.tools.generation", "qualname": "generate_content", "kind": "function", "doc": "

    Generate content using the AI. It's a general function now, but it can be specialized afterwards.

    \n\n
    Parameters
    \n\n
      \n
    • instructions: The instructions for generating the content.
    • \n
    • mime_type: The generated content type (use MIME types).
    • \n
    • filepath: Optional file path for saving the content.
    • \n
    \n", "signature": "(\tinstructions: str,\tmime_type: str,\tfilepath: Union[pathlib.Path, str, NoneType] = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.tools.generation.save_content", "modulename": "main.askai.core.features.router.tools.generation", "qualname": "save_content", "kind": "function", "doc": "

    Save any generated context into a file.

    \n\n
    Parameters
    \n\n
      \n
    • filepath: The path where you want to save the content.
    • \n
    • content: Optional content to be saved. If not provided, it will get from the last generated context.
    • \n
    \n", "signature": "(\tfilepath: Union[pathlib.Path, str, NoneType],\tcontent: Optional[~AnyStr] = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.tools.summarization", "modulename": "main.askai.core.features.router.tools.summarization", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.tools.summarization\n @file: summarization.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.router.tools.summarization.summarize", "modulename": "main.askai.core.features.router.tools.summarization", "qualname": "summarize", "kind": "function", "doc": "

    Summarize files and folders.

    \n\n
    Parameters
    \n\n
      \n
    • base_folder: The base folder to be summarized.
    • \n
    • glob: The glob to match the files to be summarized.
    • \n
    \n", "signature": "(base_folder: str, glob: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.tools.terminal", "modulename": "main.askai.core.features.router.tools.terminal", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.tools.terminal\n @file: terminal.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.router.tools.terminal.list_contents", "modulename": "main.askai.core.features.router.tools.terminal", "qualname": "list_contents", "kind": "function", "doc": "

    List the contents of a folder.

    \n\n
    Parameters
    \n\n
      \n
    • folder: The folder to list contents from.
    • \n
    • filters: The optional listing filters (file glob).
    • \n
    \n", "signature": "(folder: str, filters: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.tools.terminal.open_command", "modulename": "main.askai.core.features.router.tools.terminal", "qualname": "open_command", "kind": "function", "doc": "

    Open the specified path, regardless if it's a file, folder or application.

    \n\n
    Parameters
    \n\n
      \n
    • path_name: The file path to open.
    • \n
    \n", "signature": "(path_name: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.tools.terminal.execute_command", "modulename": "main.askai.core.features.router.tools.terminal", "qualname": "execute_command", "kind": "function", "doc": "

    Execute a terminal command using the specified language.

    \n\n
    Parameters
    \n\n
      \n
    • shell: The shell type to be used.
    • \n
    • command_line: The command line to be executed.
    • \n
    \n", "signature": "(shell: str, command_line: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.tools.vision", "modulename": "main.askai.core.features.router.tools.vision", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.features.router.tools.vision.image_captioner", "modulename": "main.askai.core.features.router.tools.vision", "qualname": "image_captioner", "kind": "function", "doc": "

    This tool is used to describe an image.

    \n", "signature": "(path_name: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.features.validation", "modulename": "main.askai.core.features.validation", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.features.validation.accuracy", "modulename": "main.askai.core.features.validation.accuracy", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.rag.commons\n @file: analysis.py\n@created: Fri, 03 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.validation.accuracy.EVALUATION_GUIDE", "modulename": "main.askai.core.features.validation.accuracy", "qualname": "EVALUATION_GUIDE", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'**Accuracy Evaluation Guidelines:**\\n\\n1. Review and analyze your past responses to ensure optimal accuracy.\\n2. Constructively self-criticize your overall responses regularly.\\n3. Reflect on past decisions and strategies to refine your approach.\\n4. Try something different.'"}, {"fullname": "main.askai.core.features.validation.accuracy.assert_accuracy", "modulename": "main.askai.core.features.validation.accuracy", "qualname": "assert_accuracy", "kind": "function", "doc": "

    Function responsible for asserting that the question was properly answered.

    \n\n
    Parameters
    \n\n
      \n
    • question: The user question.
    • \n
    • ai_response: The AI response to be analysed.
    • \n
    • pass_threshold: The threshold color to be considered as a pass.
    • \n
    \n", "signature": "(\tquestion: str,\tai_response: str,\tpass_threshold: askai.core.enums.acc_response.AccResponse = MODERATE) -> askai.core.enums.acc_response.AccResponse:", "funcdef": "def"}, {"fullname": "main.askai.core.features.validation.accuracy.resolve_x_refs", "modulename": "main.askai.core.features.validation.accuracy", "qualname": "resolve_x_refs", "kind": "function", "doc": "

    Replace all cross references by their actual values.

    \n\n
    Parameters
    \n\n
      \n
    • ref_name: The cross-reference or variable name.
    • \n
    • context: The context to analyze the references.
    • \n
    \n", "signature": "(ref_name: str, context: str | None = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.model", "modulename": "main.askai.core.model", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.model.action_plan", "modulename": "main.askai.core.model.action_plan", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model.action_plan\n @file: action_plan.py\n@created: Fri, 19 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan", "kind": "class", "doc": "

    Keep track of the router action plan.

    \n"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.__init__", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tquestion: str = None,\tprimary_goal: str = None,\tsub_goals: list[str] = None,\tthoughts: types.SimpleNamespace = None,\ttasks: list[types.SimpleNamespace] = None,\tmodel: askai.core.model.model_result.ModelResult = <factory>)"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.question", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.question", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.primary_goal", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.primary_goal", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.sub_goals", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.sub_goals", "kind": "variable", "doc": "

    \n", "annotation": ": list[str]", "default_value": "None"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.thoughts", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.thoughts", "kind": "variable", "doc": "

    \n", "annotation": ": types.SimpleNamespace", "default_value": "None"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.tasks", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.tasks", "kind": "variable", "doc": "

    \n", "annotation": ": list[types.SimpleNamespace]", "default_value": "None"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.model", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.model", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.model.model_result.ModelResult"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.create", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.create", "kind": "function", "doc": "

    TODO

    \n", "signature": "(\tquestion: str,\tmessage: langchain_core.messages.ai.AIMessage,\tmodel: askai.core.model.model_result.ModelResult) -> main.askai.core.model.action_plan.ActionPlan:", "funcdef": "def"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.reasoning", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.reasoning", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[str]"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.observations", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.observations", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[str]"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.criticism", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.criticism", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[str]"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.speak", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.speak", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[str]"}, {"fullname": "main.askai.core.model.api_keys", "modulename": "main.askai.core.model.api_keys", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.askai_configs\n @file: askai_configs.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.model.api_keys.API_KEY_FILE", "modulename": "main.askai.core.model.api_keys", "qualname": "API_KEY_FILE", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "$HHS_ENV_FILE"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys", "kind": "class", "doc": "

    Provide a class no handle the required Api Keys.

    \n", "bases": "pydantic.v1.env_settings.BaseSettings"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.OPENAI_API_KEY", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.OPENAI_API_KEY", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.GOOGLE_API_KEY", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.GOOGLE_API_KEY", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.DEEPL_API_KEY", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.DEEPL_API_KEY", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.not_empty", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.not_empty", "kind": "function", "doc": "

    \n", "signature": "(cls, value):", "funcdef": "def"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.Config", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.Config", "kind": "class", "doc": "

    \n"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.Config.env_file", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.Config.env_file", "kind": "variable", "doc": "

    \n", "default_value": "$HHS_ENV_FILE"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.Config.env_file_encoding", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.Config.env_file_encoding", "kind": "variable", "doc": "

    \n", "default_value": "'utf-8'"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.prompt", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.prompt", "kind": "function", "doc": "

    TODO

    \n", "signature": "() -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.model.model_result", "modulename": "main.askai.core.model.model_result", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model\n @file: summary_result.py\n@created: Tue, 11 Mar 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.model.model_result.ModelResult", "modulename": "main.askai.core.model.model_result", "qualname": "ModelResult", "kind": "class", "doc": "

    Keep track of the model select responses.

    \n"}, {"fullname": "main.askai.core.model.model_result.ModelResult.__init__", "modulename": "main.askai.core.model.model_result", "qualname": "ModelResult.__init__", "kind": "function", "doc": "

    \n", "signature": "(mid: str = None, goal: str = None, reason: str = None)"}, {"fullname": "main.askai.core.model.model_result.ModelResult.mid", "modulename": "main.askai.core.model.model_result", "qualname": "ModelResult.mid", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.model_result.ModelResult.goal", "modulename": "main.askai.core.model.model_result", "qualname": "ModelResult.goal", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.model_result.ModelResult.reason", "modulename": "main.askai.core.model.model_result", "qualname": "ModelResult.reason", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.model_result.ModelResult.default", "modulename": "main.askai.core.model.model_result", "qualname": "ModelResult.default", "kind": "function", "doc": "

    Return hte default ModelResult.

    \n", "signature": "() -> main.askai.core.model.model_result.ModelResult:", "funcdef": "def"}, {"fullname": "main.askai.core.model.search_result", "modulename": "main.askai.core.model.search_result", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model\n @file: search_result.py\n@created: Sun, 12 Mar 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.model.search_result.SearchResult", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult", "kind": "class", "doc": "

    Keep track of the internet search responses.

    \n"}, {"fullname": "main.askai.core.model.search_result.SearchResult.__init__", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tquestion: str = None,\tdatetime: str = None,\tcategory: str = None,\tkeywords: List[str] = None,\tsites: List[str] = None,\tfilters: List[str] = None,\tresponse: str = None)"}, {"fullname": "main.askai.core.model.search_result.SearchResult.question", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.question", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.search_result.SearchResult.datetime", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.datetime", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.search_result.SearchResult.category", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.category", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.search_result.SearchResult.keywords", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.keywords", "kind": "variable", "doc": "

    \n", "annotation": ": List[str]", "default_value": "None"}, {"fullname": "main.askai.core.model.search_result.SearchResult.sites", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.sites", "kind": "variable", "doc": "

    \n", "annotation": ": List[str]", "default_value": "None"}, {"fullname": "main.askai.core.model.search_result.SearchResult.filters", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.filters", "kind": "variable", "doc": "

    \n", "annotation": ": List[str]", "default_value": "None"}, {"fullname": "main.askai.core.model.search_result.SearchResult.response", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.response", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.summary_result", "modulename": "main.askai.core.model.summary_result", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model\n @file: summary_result.py\n@created: Sun, 10 Mar 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.model.summary_result.SummaryResult", "modulename": "main.askai.core.model.summary_result", "qualname": "SummaryResult", "kind": "class", "doc": "

    Keep track of the summarization responses.

    \n"}, {"fullname": "main.askai.core.model.summary_result.SummaryResult.__init__", "modulename": "main.askai.core.model.summary_result", "qualname": "SummaryResult.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tfolder: str = None,\tglob: str = None,\tquestion: str = None,\tanswer: str = None)"}, {"fullname": "main.askai.core.model.summary_result.SummaryResult.folder", "modulename": "main.askai.core.model.summary_result", "qualname": "SummaryResult.folder", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.summary_result.SummaryResult.glob", "modulename": "main.askai.core.model.summary_result", "qualname": "SummaryResult.glob", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.summary_result.SummaryResult.question", "modulename": "main.askai.core.model.summary_result", "qualname": "SummaryResult.question", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.summary_result.SummaryResult.answer", "modulename": "main.askai.core.model.summary_result", "qualname": "SummaryResult.answer", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.support", "modulename": "main.askai.core.support", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.support.chat_context", "modulename": "main.askai.core.support.chat_context", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.support.chat_context\n @file: chat_context.py\n@created: Fri, 28 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.chat_context.ChatRoles", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatRoles", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "Literal['system', 'human', 'assistant']"}, {"fullname": "main.askai.core.support.chat_context.ContextRaw", "modulename": "main.askai.core.support.chat_context", "qualname": "ContextRaw", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "list[dict[str, str]]"}, {"fullname": "main.askai.core.support.chat_context.LangChainContext", "modulename": "main.askai.core.support.chat_context", "qualname": "LangChainContext", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "list[tuple[str, str]]"}, {"fullname": "main.askai.core.support.chat_context.ContextEntry", "modulename": "main.askai.core.support.chat_context", "qualname": "ContextEntry", "kind": "class", "doc": "

    ContextEntry(role, content)

    \n", "bases": "builtins.tuple"}, {"fullname": "main.askai.core.support.chat_context.ContextEntry.__init__", "modulename": "main.askai.core.support.chat_context", "qualname": "ContextEntry.__init__", "kind": "function", "doc": "

    Create new instance of ContextEntry(role, content)

    \n", "signature": "(role, content)"}, {"fullname": "main.askai.core.support.chat_context.ContextEntry.role", "modulename": "main.askai.core.support.chat_context", "qualname": "ContextEntry.role", "kind": "variable", "doc": "

    Alias for field number 0

    \n"}, {"fullname": "main.askai.core.support.chat_context.ContextEntry.content", "modulename": "main.askai.core.support.chat_context", "qualname": "ContextEntry.content", "kind": "variable", "doc": "

    Alias for field number 1

    \n"}, {"fullname": "main.askai.core.support.chat_context.ChatContext", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext", "kind": "class", "doc": "

    Provide a chat context helper for AI engines.

    \n"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.__init__", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.__init__", "kind": "function", "doc": "

    \n", "signature": "(token_limit: int, max_context_size: int)"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.LANGCHAIN_ROLE_MAP", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.LANGCHAIN_ROLE_MAP", "kind": "variable", "doc": "

    \n", "annotation": ": dict", "default_value": "{'human': <class 'langchain_core.messages.human.HumanMessage'>, 'system': <class 'langchain_core.messages.system.SystemMessage'>, 'assistant': <class 'langchain_core.messages.ai.AIMessage'>}"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.of", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.of", "kind": "function", "doc": "

    Create a chat context from a context list on the format:

    \n", "signature": "(\tcontext: list[str],\ttoken_limit: int,\tmax_context_size: int) -> main.askai.core.support.chat_context.ChatContext:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.keys", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.keys", "kind": "variable", "doc": "

    \n", "annotation": ": list[~AnyStr]"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.max_context_size", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.max_context_size", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.token_limit", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.token_limit", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.push", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.push", "kind": "function", "doc": "

    Push a context message to the chat with the provided role.

    \n", "signature": "(\tself,\tkey: str,\tcontent: Any,\trole: Literal['system', 'human', 'assistant'] = 'human') -> list[dict[str, str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.context_length", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.context_length", "kind": "function", "doc": "

    \n", "signature": "(self, key: str):", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.set", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.set", "kind": "function", "doc": "

    Set the context to the chat with the provided role.

    \n", "signature": "(\tself,\tkey: str,\tcontent: Any,\trole: Literal['system', 'human', 'assistant'] = 'human') -> list[dict[str, str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.remove", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.remove", "kind": "function", "doc": "

    Remove a context message from the chat at the provided index.

    \n", "signature": "(self, key: str, index: int) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.get", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.get", "kind": "function", "doc": "

    Retrieve a context from the specified by key.

    \n", "signature": "(self, key: str) -> list[dict[str, str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.join", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.join", "kind": "function", "doc": "

    Join contexts specified by keys.

    \n", "signature": "(self, *keys: str) -> list[tuple[str, str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.flat", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.flat", "kind": "function", "doc": "

    Flatten contexts specified by keys.

    \n", "signature": "(\tself,\t*keys: str) -> langchain_core.chat_history.InMemoryChatMessageHistory:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.clear", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.clear", "kind": "function", "doc": "

    Clear the all the chat context specified by key.

    \n", "signature": "(self, *keys: str) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.forget", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.forget", "kind": "function", "doc": "

    Forget all entries pushed to the chat context.

    \n", "signature": "(self, *keys: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.size", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.size", "kind": "function", "doc": "

    Return the amount of entries a context specified by key have.

    \n", "signature": "(self, key: str) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.save", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.save", "kind": "function", "doc": "

    Save the current context window.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.support.langchain_support", "modulename": "main.askai.core.support.langchain_support", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.support.langchain_support\n @file: langchain_support.py\n@created: Fri, 28 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.langchain_support.LangChainSupport", "modulename": "main.askai.core.support.langchain_support", "qualname": "LangChainSupport", "kind": "class", "doc": "

    Helper class to support the use of langchain framework.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.support.langchain_support.LangChainSupport.__init__", "modulename": "main.askai.core.support.langchain_support", "qualname": "LangChainSupport.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.support.langchain_support.LangChainSupport.INSTANCE", "modulename": "main.askai.core.support.langchain_support", "qualname": "LangChainSupport.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.support.langchain_support.LangChainSupport"}, {"fullname": "main.askai.core.support.langchain_support.LangChainSupport.create_model", "modulename": "main.askai.core.support.langchain_support", "qualname": "LangChainSupport.create_model", "kind": "function", "doc": "

    TODO

    \n", "signature": "(temperature: float = 0.0, top_p: float = 0.0) -> Any:", "funcdef": "def"}, {"fullname": "main.askai.core.support.langchain_support.LangChainSupport.create_chat_model", "modulename": "main.askai.core.support.langchain_support", "qualname": "LangChainSupport.create_chat_model", "kind": "function", "doc": "

    TODO

    \n", "signature": "(temperature: float = 0.0) -> Any:", "funcdef": "def"}, {"fullname": "main.askai.core.support.langchain_support.LangChainSupport.create_embeddings", "modulename": "main.askai.core.support.langchain_support", "qualname": "LangChainSupport.create_embeddings", "kind": "function", "doc": "

    TODO

    \n", "signature": "(model: str = 'text-embedding-3-small') -> Any:", "funcdef": "def"}, {"fullname": "main.askai.core.support.platform", "modulename": "main.askai.core.support.platform", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model\n @file: platform.py\n@created: Thu, 29 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.platform.SupportedPlatforms", "modulename": "main.askai.core.support.platform", "qualname": "SupportedPlatforms", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "Optional[Literal['linux', 'windows', 'darwin']]"}, {"fullname": "main.askai.core.support.platform.SupportedShells", "modulename": "main.askai.core.support.platform", "qualname": "SupportedShells", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "Optional[Literal['bash', 'csh', 'dash', 'ksh', 'tcsh', 'zsh', 'sh']]"}, {"fullname": "main.askai.core.support.platform.get_os", "modulename": "main.askai.core.support.platform", "qualname": "get_os", "kind": "function", "doc": "

    \n", "signature": "() -> Optional[Literal['linux', 'windows', 'darwin']]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.platform.get_shell", "modulename": "main.askai.core.support.platform", "qualname": "get_shell", "kind": "function", "doc": "

    \n", "signature": "() -> Optional[Literal['bash', 'csh', 'dash', 'ksh', 'tcsh', 'zsh', 'sh']]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.platform.get_user", "modulename": "main.askai.core.support.platform", "qualname": "get_user", "kind": "function", "doc": "

    \n", "signature": "() -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.support.presets", "modulename": "main.askai.core.support.presets", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.support\n @file: presets.py\n@created: Tue, 16 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.presets.Presets", "modulename": "main.askai.core.support.presets", "qualname": "Presets", "kind": "class", "doc": "

    Provides text streaming presets according to the language used.

    \n"}, {"fullname": "main.askai.core.support.presets.Presets.__init__", "modulename": "main.askai.core.support.presets", "qualname": "Presets.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tlang: str,\twords_per_breath: int,\tbase_speed: float,\twords_interval: float,\tbreath_interval: float,\tnumber_interval: float,\tcomma_interval: float,\tpunct_interval: float,\tenum_interval: float,\tperiod_interval: float)"}, {"fullname": "main.askai.core.support.presets.Presets.get", "modulename": "main.askai.core.support.presets", "qualname": "Presets.get", "kind": "function", "doc": "

    \n", "signature": "(\tcls,\tlang: str = 'en',\ttempo: int = 1,\tbase_interval: float = 0.01) -> main.askai.core.support.presets.Presets:", "funcdef": "def"}, {"fullname": "main.askai.core.support.presets.Presets.words_per_breath", "modulename": "main.askai.core.support.presets", "qualname": "Presets.words_per_breath", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.support.presets.Presets.base_speed", "modulename": "main.askai.core.support.presets", "qualname": "Presets.base_speed", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.presets.Presets.words_interval", "modulename": "main.askai.core.support.presets", "qualname": "Presets.words_interval", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.presets.Presets.breath_interval", "modulename": "main.askai.core.support.presets", "qualname": "Presets.breath_interval", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.presets.Presets.number_interval", "modulename": "main.askai.core.support.presets", "qualname": "Presets.number_interval", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.presets.Presets.comma_interval", "modulename": "main.askai.core.support.presets", "qualname": "Presets.comma_interval", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.presets.Presets.punct_interval", "modulename": "main.askai.core.support.presets", "qualname": "Presets.punct_interval", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.presets.Presets.enum_interval", "modulename": "main.askai.core.support.presets", "qualname": "Presets.enum_interval", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.presets.Presets.period_interval", "modulename": "main.askai.core.support.presets", "qualname": "Presets.period_interval", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.shared_instances", "modulename": "main.askai.core.support.shared_instances", "kind": "module", "doc": "

    @project: \"askai\"\n@package: \"askai\".main.askai.core.support\n @file: shared_instances.py\n@created: Tue, 23 Apr 2024\n @author: \"Hugo Saporetti Junior\n @site: \"https://github.com/yorevs/askai\")\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances", "kind": "class", "doc": "

    Provides access to shared instances.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.__init__", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.INSTANCE", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.support.shared_instances.SharedInstances"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.UNCERTAIN_ID", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.UNCERTAIN_ID", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'bde6f44d-c1a0-4b0c-bd74-8278e468e50c'"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.engine", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.engine", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[askai.core.engine.ai_engine.AIEngine]"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.context", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.context", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[askai.core.support.chat_context.ChatContext]"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.nickname", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.nickname", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.username", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.username", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.nickname_md", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.nickname_md", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.username_md", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.username_md", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.idiom", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.idiom", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.memory", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.memory", "kind": "variable", "doc": "

    \n", "annotation": ": langchain.memory.buffer_window.ConversationBufferWindowMemory"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.max_short_memory_size", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.max_short_memory_size", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.max_iteractions", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.max_iteractions", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.app_info", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.app_info", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.create_engine", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.create_engine", "kind": "function", "doc": "

    Create an AI engine specified by the engine and model names.

    \n", "signature": "(\tself,\tengine_name: str,\tmodel_name: str) -> askai.core.engine.ai_engine.AIEngine:", "funcdef": "def"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.create_context", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.create_context", "kind": "function", "doc": "

    Create the chat context, limiting to the specified token

    \n", "signature": "(self, token_limit: int) -> askai.core.support.chat_context.ChatContext:", "funcdef": "def"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.create_memory", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.create_memory", "kind": "function", "doc": "

    TODO

    \n", "signature": "(\tself,\tmemory_key: str = 'chat_history') -> langchain.memory.buffer_window.ConversationBufferWindowMemory:", "funcdef": "def"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.input_text", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.input_text", "kind": "function", "doc": "

    Prompt for user input.

    \n\n
    Parameters
    \n\n
      \n
    • input_prompt: The prompt to display to the user.
    • \n
    • placeholder: The input placeholder text.
    • \n
    \n", "signature": "(self, input_prompt: str, placeholder: str | None = None) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.text_formatter", "modulename": "main.askai.core.support.text_formatter", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.support.text_formatter\n @file: text_formatter.py\n@created: Fri, 28 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter", "kind": "class", "doc": "

    TODO

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.__init__", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.INSTANCE", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.support.text_formatter.TextFormatter"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.RE_URL", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.RE_URL", "kind": "variable", "doc": "

    \n", "default_value": ""(https?:\\\\/\\\\/(?:www\\\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\\\.[^\\\\s')]{2,}|www\\\\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\\\.[^\\\\s')]{2,}|https?:\\\\/\\\\/(?:www\\\\.|(?!www))[a-zA-Z0-9]+\\\\.[^\\\\s')]{2,}|www\\\\.[a-zA-Z0-9]+\\\\.[^\\\\s')]{2,})""}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.RE_MD_CODE_BLOCK", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.RE_MD_CODE_BLOCK", "kind": "variable", "doc": "

    \n", "default_value": "'(```.+```)'"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.CHAT_ICONS", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.CHAT_ICONS", "kind": "variable", "doc": "

    \n", "default_value": "{'\\uf659': ' Oops!\\n> \\uf659 An Exception Occurred: \\n#### ', '\\uf400': '\\n> \\uf400 *Tip:* ', '\\uf691': '\\n> \\uf691 *Analysis:* ', '\\uf006': '\\n> \\uf006 *Summary:* ', '\\uf6f6': '\\n> \\uf6f6 *Joke:* ', '\\uf6f2': '\\n> \\uf6f2 *Fun-Fact:* ', '\\uf421': '\\n> \\uf421 *Advice:* ', '\\ufb3d': '\\n> \\ufb3d *Conclusion:* '}"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.RE_TYPES", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.RE_TYPES", "kind": "variable", "doc": "

    \n", "default_value": "{'MD': '(```.+```)', '\\uf0c1': "(https?:\\\\/\\\\/(?:www\\\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\\\.[^\\\\s')]{2,}|www\\\\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\\\.[^\\\\s')]{2,}|https?:\\\\/\\\\/(?:www\\\\.|(?!www))[a-zA-Z0-9]+\\\\.[^\\\\s')]{2,}|www\\\\.[a-zA-Z0-9]+\\\\.[^\\\\s')]{2,})", '\\uf659': '[\\\\s*_]*Errors?[_*-:\\\\s]+', '\\uf400': '[\\\\s*_]*Hints?( ([Aa]nd|&) [Tt]ips?)?[_*-:\\\\s]+', '\\uf691': '[\\\\s*_]*Analysis[_*-:\\\\s]+', '\\uf006': '[\\\\s*_]*Summary[_*-:\\\\s]+', '\\uf6f2': '[\\\\s*_]*Fun[\\\\s-]+[Ff]acts?[_*-:\\\\s]+', '\\uf6f6': '[\\\\s*_]*(Jokes?(\\\\s+[Tt]ime)?)[_*-:\\\\s]+', '\\uf421': '[\\\\s*_]*Advice[_*-:\\\\s]+', '\\ufb3d': '[\\\\s*_]*Conclusion[_*-:\\\\s]+'}"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.ensure_ln", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.ensure_ln", "kind": "function", "doc": "

    Ensure text starts and ends with a lien separator.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to be formatted.
    • \n
    \n", "signature": "(text: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.console", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.console", "kind": "variable", "doc": "

    \n", "annotation": ": rich.console.Console"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.beautify", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.beautify", "kind": "function", "doc": "

    Beautify the provided text with icons and other formatting improvements.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to be beautified.
    • \n
    \n", "signature": "(self, text: Any) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.display_markdown", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.display_markdown", "kind": "function", "doc": "

    Display a markdown formatted text.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to be displayed.
    • \n
    \n", "signature": "(self, text: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.display_text", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.display_text", "kind": "function", "doc": "

    Display a vt100 formatted text.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to be displayed.
    • \n
    \n", "signature": "(self, text: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.cmd_print", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.cmd_print", "kind": "function", "doc": "

    Display an AskAI commander text.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to be displayed.
    • \n
    \n", "signature": "(self, text: str):", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities", "modulename": "main.askai.core.support.utilities", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.support.utilities\n @file: utilities.py\n@created: Wed, 10 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.utilities.read_stdin", "modulename": "main.askai.core.support.utilities", "qualname": "read_stdin", "kind": "function", "doc": "

    TODO

    \n", "signature": "() -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.display_text", "modulename": "main.askai.core.support.utilities", "qualname": "display_text", "kind": "function", "doc": "

    Display the provided text ina proper way.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to be displayed.
    • \n
    • prefix: the text prefix.
    • \n
    • markdown: Whether to enable markdown rendering.
    • \n
    • erase_last: Whether to erase the last displayed line.
    • \n
    \n", "signature": "(\ttext: Any,\tprefix: Any = '',\tmarkdown: bool = True,\terase_last=False) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.stream_text", "modulename": "main.askai.core.support.utilities", "qualname": "stream_text", "kind": "function", "doc": "

    Stream the text on the screen. Simulates a typewriter effect. The following presets were\nbenchmarked according to the selected language.

    \n\n
    Parameters
    \n\n
      \n
    • text: the text to stream.
    • \n
    • prefix: the streaming prefix.
    • \n
    • tempo: the speed multiplier of the typewriter effect. Defaults to 1.
    • \n
    • language: the language used to stream the text. Defaults to en_US.
    • \n
    \n", "signature": "(\ttext: Any,\tprefix: Any = '',\ttempo: int = 1,\tlanguage: askai.language.language.Language = English) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.find_file", "modulename": "main.askai.core.support.utilities", "qualname": "find_file", "kind": "function", "doc": "

    Find the specified file, specified by name, from the most common locations.

    \n", "signature": "(filename: str | None) -> Optional[pathlib.Path]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.copy_file", "modulename": "main.askai.core.support.utilities", "qualname": "copy_file", "kind": "function", "doc": "

    Copy the specified file to another folder path.

    \n\n
    Parameters
    \n\n
      \n
    • filename: The file name to be moved.
    • \n
    • destfile: The destination path to move to.
    • \n
    \n", "signature": "(filename: str | pathlib.Path, destfile: str | pathlib.Path) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.build_img_path", "modulename": "main.askai.core.support.utilities", "qualname": "build_img_path", "kind": "function", "doc": "

    TODO

    \n", "signature": "(base_dir: pathlib.Path, filename: str, suffix: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.read_resource", "modulename": "main.askai.core.support.utilities", "qualname": "read_resource", "kind": "function", "doc": "

    Read the prompt template specified by the filename.

    \n\n
    Parameters
    \n\n
      \n
    • base_dir: The base directory, relative to the resources folder.
    • \n
    • filename: The filename of the prompt.
    • \n
    • file_ext: The file extension of.
    • \n
    \n", "signature": "(base_dir: str, filename: str, file_ext: str = '.txt') -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.extract_path", "modulename": "main.askai.core.support.utilities", "qualname": "extract_path", "kind": "function", "doc": "

    Extract the first identifiable path of the executed command line.

    \n\n
    Parameters
    \n\n
      \n
    • command_line: The command line text.
    • \n
    • flags: Regex match flags.
    • \n
    \n", "signature": "(\tcommand_line: str,\tflags: int = re.IGNORECASE|re.MULTILINE) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.extract_codeblock", "modulename": "main.askai.core.support.utilities", "qualname": "extract_codeblock", "kind": "function", "doc": "

    Extract language and actual code from a markdown multi-line code block.

    \n\n
    Parameters
    \n\n
      \n
    • text: The markdown formatted text.
    • \n
    \n", "signature": "(text: str) -> Tuple[Optional[str], str]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.media_type_of", "modulename": "main.askai.core.support.utilities", "qualname": "media_type_of", "kind": "function", "doc": "

    Return the file media type, or none is guessing was not possible.

    \n\n
    Parameters
    \n\n
      \n
    • pathname: The file path to check.
    • \n
    \n", "signature": "(pathname: str) -> Optional[tuple[str, ...]]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.seconds", "modulename": "main.askai.core.support.utilities", "qualname": "seconds", "kind": "function", "doc": "

    Return the amount of second from milliseconds.

    \n", "signature": "(millis: int) -> float:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.ensure_ln", "modulename": "main.askai.core.support.utilities", "qualname": "ensure_ln", "kind": "function", "doc": "

    Ensure text starts and ends with new line.

    \n", "signature": "(text: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.exception", "modulename": "main.askai.exception", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.exception.exceptions", "modulename": "main.askai.exception.exceptions", "kind": "module", "doc": "

    @project: HsPyLib-CFMan\n@package: askai.exception.exceptions\n @file: exceptions.py\n@created: Fri, 12 May 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.exception.exceptions.NoSuchEngineError", "modulename": "main.askai.exception.exceptions", "qualname": "NoSuchEngineError", "kind": "class", "doc": "

    Raised when the provided engine does not exist

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.InvalidRecognitionApiError", "modulename": "main.askai.exception.exceptions", "qualname": "InvalidRecognitionApiError", "kind": "class", "doc": "

    Raised when an invalid recognition API callback is provided.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.IntelligibleAudioError", "modulename": "main.askai.exception.exceptions", "qualname": "IntelligibleAudioError", "kind": "class", "doc": "

    Raised when an the provided audio was not recognized by the API.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.RecognitionApiRequestError", "modulename": "main.askai.exception.exceptions", "qualname": "RecognitionApiRequestError", "kind": "class", "doc": "

    Raised when an there was an error calling the recognition API.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.TranslationPackageError", "modulename": "main.askai.exception.exceptions", "qualname": "TranslationPackageError", "kind": "class", "doc": "

    Raised when an there was an error installing an Argos translation package.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.TokenLengthExceeded", "modulename": "main.askai.exception.exceptions", "qualname": "TokenLengthExceeded", "kind": "class", "doc": "

    Raised when the token is too big to fit the token context window.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.InvalidInputDevice", "modulename": "main.askai.exception.exceptions", "qualname": "InvalidInputDevice", "kind": "class", "doc": "

    Raised when an invalid recording input device is used.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.DocumentsNotFound", "modulename": "main.askai.exception.exceptions", "qualname": "DocumentsNotFound", "kind": "class", "doc": "

    Raised when no documents are found for summarization.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.InaccurateResponse", "modulename": "main.askai.exception.exceptions", "qualname": "InaccurateResponse", "kind": "class", "doc": "

    Raised when detected an inaccurate response form the AI.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.MaxInteractionsReached", "modulename": "main.askai.exception.exceptions", "qualname": "MaxInteractionsReached", "kind": "class", "doc": "

    Raised when the number of executed actions exceeded the limit.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.ImpossibleQuery", "modulename": "main.askai.exception.exceptions", "qualname": "ImpossibleQuery", "kind": "class", "doc": "

    Raised when tracing or executing an action plan is/was not possible.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.TerminatingQuery", "modulename": "main.askai.exception.exceptions", "qualname": "TerminatingQuery", "kind": "class", "doc": "

    Raised when received a terminating question.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.InvalidStructuredResponse", "modulename": "main.askai.exception.exceptions", "qualname": "InvalidStructuredResponse", "kind": "class", "doc": "

    Raised when received an invalid structured response.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.MissingApiKeyError", "modulename": "main.askai.exception.exceptions", "qualname": "MissingApiKeyError", "kind": "class", "doc": "

    Raised when received an invalid structured response.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.WebCamInitializationFailure", "modulename": "main.askai.exception.exceptions", "qualname": "WebCamInitializationFailure", "kind": "class", "doc": "

    Raised when failed to initialize the computer Webcam.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.CameraAccessFailure", "modulename": "main.askai.exception.exceptions", "qualname": "CameraAccessFailure", "kind": "class", "doc": "

    Raised when failed to operate with the computer Webcam.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.language", "modulename": "main.askai.language", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.language.ai_translator", "modulename": "main.askai.language.ai_translator", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.language.argos_translator\n @file: translator.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.language.ai_translator.AITranslator", "modulename": "main.askai.language.ai_translator", "qualname": "AITranslator", "kind": "class", "doc": "

    Provides a base class for multilingual offline translation engines. Various implementations can be used.

    \n", "bases": "abc.ABC"}, {"fullname": "main.askai.language.ai_translator.AITranslator.__init__", "modulename": "main.askai.language.ai_translator", "qualname": "AITranslator.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tfrom_idiom: askai.language.language.Language,\tto_idiom: askai.language.language.Language)"}, {"fullname": "main.askai.language.ai_translator.AITranslator.translate", "modulename": "main.askai.language.ai_translator", "qualname": "AITranslator.translate", "kind": "function", "doc": "

    Translate text using te default translator.

    \n\n
    Parameters
    \n\n
      \n
    • text: Text to translate.
    • \n
    \n", "signature": "(self, text: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.language.ai_translator.AITranslator.name", "modulename": "main.askai.language.ai_translator", "qualname": "AITranslator.name", "kind": "function", "doc": "

    Return the translator name or model.

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.language.language", "modulename": "main.askai.language.language", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.language.language\n @file: language.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.language.language.AnyLocale", "modulename": "main.askai.language.language", "qualname": "AnyLocale", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "str | tuple[str | None, ...]"}, {"fullname": "main.askai.language.language.Language", "modulename": "main.askai.language.language", "qualname": "Language", "kind": "class", "doc": "

    Enumeration to wrap all standard languages.\nRef:. https://docs.oracle.com/cd/E23824_01/html/E26033/glset.html

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.language.language.Language.AF_ZA", "modulename": "main.askai.language.language", "qualname": "Language.AF_ZA", "kind": "variable", "doc": "

    \n", "default_value": "Afrikaans"}, {"fullname": "main.askai.language.language.Language.AR_AE", "modulename": "main.askai.language.language", "qualname": "Language.AR_AE", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_BH", "modulename": "main.askai.language.language", "qualname": "Language.AR_BH", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_DZ", "modulename": "main.askai.language.language", "qualname": "Language.AR_DZ", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_EG", "modulename": "main.askai.language.language", "qualname": "Language.AR_EG", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_IQ", "modulename": "main.askai.language.language", "qualname": "Language.AR_IQ", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_JO", "modulename": "main.askai.language.language", "qualname": "Language.AR_JO", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_KW", "modulename": "main.askai.language.language", "qualname": "Language.AR_KW", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_LY", "modulename": "main.askai.language.language", "qualname": "Language.AR_LY", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_MA", "modulename": "main.askai.language.language", "qualname": "Language.AR_MA", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_OM", "modulename": "main.askai.language.language", "qualname": "Language.AR_OM", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_QA", "modulename": "main.askai.language.language", "qualname": "Language.AR_QA", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_SA", "modulename": "main.askai.language.language", "qualname": "Language.AR_SA", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_TN", "modulename": "main.askai.language.language", "qualname": "Language.AR_TN", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_YE", "modulename": "main.askai.language.language", "qualname": "Language.AR_YE", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AS_IN", "modulename": "main.askai.language.language", "qualname": "Language.AS_IN", "kind": "variable", "doc": "

    \n", "default_value": "Assamese"}, {"fullname": "main.askai.language.language.Language.AZ_AZ", "modulename": "main.askai.language.language", "qualname": "Language.AZ_AZ", "kind": "variable", "doc": "

    \n", "default_value": "Azerbaijani"}, {"fullname": "main.askai.language.language.Language.BE_BY", "modulename": "main.askai.language.language", "qualname": "Language.BE_BY", "kind": "variable", "doc": "

    \n", "default_value": "Belarusian"}, {"fullname": "main.askai.language.language.Language.BG_BG", "modulename": "main.askai.language.language", "qualname": "Language.BG_BG", "kind": "variable", "doc": "

    \n", "default_value": "Bulgarian"}, {"fullname": "main.askai.language.language.Language.BN_IN", "modulename": "main.askai.language.language", "qualname": "Language.BN_IN", "kind": "variable", "doc": "

    \n", "default_value": "Bengali"}, {"fullname": "main.askai.language.language.Language.BS_BA", "modulename": "main.askai.language.language", "qualname": "Language.BS_BA", "kind": "variable", "doc": "

    \n", "default_value": "Bosnian"}, {"fullname": "main.askai.language.language.Language.CA_ES", "modulename": "main.askai.language.language", "qualname": "Language.CA_ES", "kind": "variable", "doc": "

    \n", "default_value": "Catalan"}, {"fullname": "main.askai.language.language.Language.CS_CZ", "modulename": "main.askai.language.language", "qualname": "Language.CS_CZ", "kind": "variable", "doc": "

    \n", "default_value": "Czech"}, {"fullname": "main.askai.language.language.Language.DA_DK", "modulename": "main.askai.language.language", "qualname": "Language.DA_DK", "kind": "variable", "doc": "

    \n", "default_value": "Danish"}, {"fullname": "main.askai.language.language.Language.DE_AT", "modulename": "main.askai.language.language", "qualname": "Language.DE_AT", "kind": "variable", "doc": "

    \n", "default_value": "German"}, {"fullname": "main.askai.language.language.Language.DE_BE", "modulename": "main.askai.language.language", "qualname": "Language.DE_BE", "kind": "variable", "doc": "

    \n", "default_value": "German"}, {"fullname": "main.askai.language.language.Language.DE_CH", "modulename": "main.askai.language.language", "qualname": "Language.DE_CH", "kind": "variable", "doc": "

    \n", "default_value": "German"}, {"fullname": "main.askai.language.language.Language.DE_DE", "modulename": "main.askai.language.language", "qualname": "Language.DE_DE", "kind": "variable", "doc": "

    \n", "default_value": "German"}, {"fullname": "main.askai.language.language.Language.DE_LI", "modulename": "main.askai.language.language", "qualname": "Language.DE_LI", "kind": "variable", "doc": "

    \n", "default_value": "German"}, {"fullname": "main.askai.language.language.Language.DE_LU", "modulename": "main.askai.language.language", "qualname": "Language.DE_LU", "kind": "variable", "doc": "

    \n", "default_value": "German"}, {"fullname": "main.askai.language.language.Language.EL_CY", "modulename": "main.askai.language.language", "qualname": "Language.EL_CY", "kind": "variable", "doc": "

    \n", "default_value": "Greek"}, {"fullname": "main.askai.language.language.Language.EL_GR", "modulename": "main.askai.language.language", "qualname": "Language.EL_GR", "kind": "variable", "doc": "

    \n", "default_value": "Greek"}, {"fullname": "main.askai.language.language.Language.EN_AU", "modulename": "main.askai.language.language", "qualname": "Language.EN_AU", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_BW", "modulename": "main.askai.language.language", "qualname": "Language.EN_BW", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_CA", "modulename": "main.askai.language.language", "qualname": "Language.EN_CA", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_GB", "modulename": "main.askai.language.language", "qualname": "Language.EN_GB", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_HK", "modulename": "main.askai.language.language", "qualname": "Language.EN_HK", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_IE", "modulename": "main.askai.language.language", "qualname": "Language.EN_IE", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_IN", "modulename": "main.askai.language.language", "qualname": "Language.EN_IN", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_MT", "modulename": "main.askai.language.language", "qualname": "Language.EN_MT", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_NZ", "modulename": "main.askai.language.language", "qualname": "Language.EN_NZ", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_PH", "modulename": "main.askai.language.language", "qualname": "Language.EN_PH", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_SG", "modulename": "main.askai.language.language", "qualname": "Language.EN_SG", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_US", "modulename": "main.askai.language.language", "qualname": "Language.EN_US", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_ZW", "modulename": "main.askai.language.language", "qualname": "Language.EN_ZW", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.ES_AR", "modulename": "main.askai.language.language", "qualname": "Language.ES_AR", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_BO", "modulename": "main.askai.language.language", "qualname": "Language.ES_BO", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_CL", "modulename": "main.askai.language.language", "qualname": "Language.ES_CL", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_CO", "modulename": "main.askai.language.language", "qualname": "Language.ES_CO", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_CR", "modulename": "main.askai.language.language", "qualname": "Language.ES_CR", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_DO", "modulename": "main.askai.language.language", "qualname": "Language.ES_DO", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_EC", "modulename": "main.askai.language.language", "qualname": "Language.ES_EC", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_ES", "modulename": "main.askai.language.language", "qualname": "Language.ES_ES", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_GT", "modulename": "main.askai.language.language", "qualname": "Language.ES_GT", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_HN", "modulename": "main.askai.language.language", "qualname": "Language.ES_HN", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_MX", "modulename": "main.askai.language.language", "qualname": "Language.ES_MX", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_NI", "modulename": "main.askai.language.language", "qualname": "Language.ES_NI", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_PA", "modulename": "main.askai.language.language", "qualname": "Language.ES_PA", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_PE", "modulename": "main.askai.language.language", "qualname": "Language.ES_PE", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_PR", "modulename": "main.askai.language.language", "qualname": "Language.ES_PR", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_PY", "modulename": "main.askai.language.language", "qualname": "Language.ES_PY", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_SV", "modulename": "main.askai.language.language", "qualname": "Language.ES_SV", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_US", "modulename": "main.askai.language.language", "qualname": "Language.ES_US", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_UY", "modulename": "main.askai.language.language", "qualname": "Language.ES_UY", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_VE", "modulename": "main.askai.language.language", "qualname": "Language.ES_VE", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ET_EE", "modulename": "main.askai.language.language", "qualname": "Language.ET_EE", "kind": "variable", "doc": "

    \n", "default_value": "Estonian"}, {"fullname": "main.askai.language.language.Language.FI_FI", "modulename": "main.askai.language.language", "qualname": "Language.FI_FI", "kind": "variable", "doc": "

    \n", "default_value": "Finnish"}, {"fullname": "main.askai.language.language.Language.FR_BE", "modulename": "main.askai.language.language", "qualname": "Language.FR_BE", "kind": "variable", "doc": "

    \n", "default_value": "French"}, {"fullname": "main.askai.language.language.Language.FR_CA", "modulename": "main.askai.language.language", "qualname": "Language.FR_CA", "kind": "variable", "doc": "

    \n", "default_value": "French"}, {"fullname": "main.askai.language.language.Language.FR_CH", "modulename": "main.askai.language.language", "qualname": "Language.FR_CH", "kind": "variable", "doc": "

    \n", "default_value": "French"}, {"fullname": "main.askai.language.language.Language.FR_FR", "modulename": "main.askai.language.language", "qualname": "Language.FR_FR", "kind": "variable", "doc": "

    \n", "default_value": "French"}, {"fullname": "main.askai.language.language.Language.FR_LU", "modulename": "main.askai.language.language", "qualname": "Language.FR_LU", "kind": "variable", "doc": "

    \n", "default_value": "French"}, {"fullname": "main.askai.language.language.Language.GU_IN", "modulename": "main.askai.language.language", "qualname": "Language.GU_IN", "kind": "variable", "doc": "

    \n", "default_value": "Gujarati"}, {"fullname": "main.askai.language.language.Language.HE_IL", "modulename": "main.askai.language.language", "qualname": "Language.HE_IL", "kind": "variable", "doc": "

    \n", "default_value": "Hebrew"}, {"fullname": "main.askai.language.language.Language.HI_IN", "modulename": "main.askai.language.language", "qualname": "Language.HI_IN", "kind": "variable", "doc": "

    \n", "default_value": "Hindi"}, {"fullname": "main.askai.language.language.Language.HR_HR", "modulename": "main.askai.language.language", "qualname": "Language.HR_HR", "kind": "variable", "doc": "

    \n", "default_value": "Croatian"}, {"fullname": "main.askai.language.language.Language.HU_HU", "modulename": "main.askai.language.language", "qualname": "Language.HU_HU", "kind": "variable", "doc": "

    \n", "default_value": "Hungarian"}, {"fullname": "main.askai.language.language.Language.HY_AM", "modulename": "main.askai.language.language", "qualname": "Language.HY_AM", "kind": "variable", "doc": "

    \n", "default_value": "Armenian"}, {"fullname": "main.askai.language.language.Language.ID_ID", "modulename": "main.askai.language.language", "qualname": "Language.ID_ID", "kind": "variable", "doc": "

    \n", "default_value": "Indonesian"}, {"fullname": "main.askai.language.language.Language.IS_IS", "modulename": "main.askai.language.language", "qualname": "Language.IS_IS", "kind": "variable", "doc": "

    \n", "default_value": "Icelandic"}, {"fullname": "main.askai.language.language.Language.IT_CH", "modulename": "main.askai.language.language", "qualname": "Language.IT_CH", "kind": "variable", "doc": "

    \n", "default_value": "Italian"}, {"fullname": "main.askai.language.language.Language.IT_IT", "modulename": "main.askai.language.language", "qualname": "Language.IT_IT", "kind": "variable", "doc": "

    \n", "default_value": "Italian"}, {"fullname": "main.askai.language.language.Language.JA_JP", "modulename": "main.askai.language.language", "qualname": "Language.JA_JP", "kind": "variable", "doc": "

    \n", "default_value": "Japanese"}, {"fullname": "main.askai.language.language.Language.KA_GE", "modulename": "main.askai.language.language", "qualname": "Language.KA_GE", "kind": "variable", "doc": "

    \n", "default_value": "Georgian"}, {"fullname": "main.askai.language.language.Language.KK_KZ", "modulename": "main.askai.language.language", "qualname": "Language.KK_KZ", "kind": "variable", "doc": "

    \n", "default_value": "Kazakh"}, {"fullname": "main.askai.language.language.Language.KN_IN", "modulename": "main.askai.language.language", "qualname": "Language.KN_IN", "kind": "variable", "doc": "

    \n", "default_value": "Kannada"}, {"fullname": "main.askai.language.language.Language.KO_KR", "modulename": "main.askai.language.language", "qualname": "Language.KO_KR", "kind": "variable", "doc": "

    \n", "default_value": "Korean"}, {"fullname": "main.askai.language.language.Language.KS_IN", "modulename": "main.askai.language.language", "qualname": "Language.KS_IN", "kind": "variable", "doc": "

    \n", "default_value": "Kashmiri"}, {"fullname": "main.askai.language.language.Language.KU_TR", "modulename": "main.askai.language.language", "qualname": "Language.KU_TR", "kind": "variable", "doc": "

    \n", "default_value": "Kurdish"}, {"fullname": "main.askai.language.language.Language.KY_KG", "modulename": "main.askai.language.language", "qualname": "Language.KY_KG", "kind": "variable", "doc": "

    \n", "default_value": "Kirghiz"}, {"fullname": "main.askai.language.language.Language.LT_LT", "modulename": "main.askai.language.language", "qualname": "Language.LT_LT", "kind": "variable", "doc": "

    \n", "default_value": "Lithuanian"}, {"fullname": "main.askai.language.language.Language.LV_LV", "modulename": "main.askai.language.language", "qualname": "Language.LV_LV", "kind": "variable", "doc": "

    \n", "default_value": "Latvian"}, {"fullname": "main.askai.language.language.Language.MK_MK", "modulename": "main.askai.language.language", "qualname": "Language.MK_MK", "kind": "variable", "doc": "

    \n", "default_value": "Macedonian"}, {"fullname": "main.askai.language.language.Language.ML_IN", "modulename": "main.askai.language.language", "qualname": "Language.ML_IN", "kind": "variable", "doc": "

    \n", "default_value": "Malayalam"}, {"fullname": "main.askai.language.language.Language.MR_IN", "modulename": "main.askai.language.language", "qualname": "Language.MR_IN", "kind": "variable", "doc": "

    \n", "default_value": "Marathi"}, {"fullname": "main.askai.language.language.Language.MS_MY", "modulename": "main.askai.language.language", "qualname": "Language.MS_MY", "kind": "variable", "doc": "

    \n", "default_value": "Malay"}, {"fullname": "main.askai.language.language.Language.MT_MT", "modulename": "main.askai.language.language", "qualname": "Language.MT_MT", "kind": "variable", "doc": "

    \n", "default_value": "Maltese"}, {"fullname": "main.askai.language.language.Language.NB_NO", "modulename": "main.askai.language.language", "qualname": "Language.NB_NO", "kind": "variable", "doc": "

    \n", "default_value": "Bokmal"}, {"fullname": "main.askai.language.language.Language.NL_BE", "modulename": "main.askai.language.language", "qualname": "Language.NL_BE", "kind": "variable", "doc": "

    \n", "default_value": "Dutch"}, {"fullname": "main.askai.language.language.Language.NL_NL", "modulename": "main.askai.language.language", "qualname": "Language.NL_NL", "kind": "variable", "doc": "

    \n", "default_value": "Dutch"}, {"fullname": "main.askai.language.language.Language.NN_NO", "modulename": "main.askai.language.language", "qualname": "Language.NN_NO", "kind": "variable", "doc": "

    \n", "default_value": "Nynorsk"}, {"fullname": "main.askai.language.language.Language.OR_IN", "modulename": "main.askai.language.language", "qualname": "Language.OR_IN", "kind": "variable", "doc": "

    \n", "default_value": "Oriya"}, {"fullname": "main.askai.language.language.Language.PA_IN", "modulename": "main.askai.language.language", "qualname": "Language.PA_IN", "kind": "variable", "doc": "

    \n", "default_value": "Punjabi"}, {"fullname": "main.askai.language.language.Language.PL_PL", "modulename": "main.askai.language.language", "qualname": "Language.PL_PL", "kind": "variable", "doc": "

    \n", "default_value": "Polish"}, {"fullname": "main.askai.language.language.Language.PT_BR", "modulename": "main.askai.language.language", "qualname": "Language.PT_BR", "kind": "variable", "doc": "

    \n", "default_value": "Portuguese"}, {"fullname": "main.askai.language.language.Language.PT_PT", "modulename": "main.askai.language.language", "qualname": "Language.PT_PT", "kind": "variable", "doc": "

    \n", "default_value": "Portuguese"}, {"fullname": "main.askai.language.language.Language.RO_RO", "modulename": "main.askai.language.language", "qualname": "Language.RO_RO", "kind": "variable", "doc": "

    \n", "default_value": "Romanian"}, {"fullname": "main.askai.language.language.Language.RU_RU", "modulename": "main.askai.language.language", "qualname": "Language.RU_RU", "kind": "variable", "doc": "

    \n", "default_value": "Russian"}, {"fullname": "main.askai.language.language.Language.RU_UA", "modulename": "main.askai.language.language", "qualname": "Language.RU_UA", "kind": "variable", "doc": "

    \n", "default_value": "Russian"}, {"fullname": "main.askai.language.language.Language.SA_IN", "modulename": "main.askai.language.language", "qualname": "Language.SA_IN", "kind": "variable", "doc": "

    \n", "default_value": "Sanskrit"}, {"fullname": "main.askai.language.language.Language.SK_SK", "modulename": "main.askai.language.language", "qualname": "Language.SK_SK", "kind": "variable", "doc": "

    \n", "default_value": "Slovak"}, {"fullname": "main.askai.language.language.Language.SL_SI", "modulename": "main.askai.language.language", "qualname": "Language.SL_SI", "kind": "variable", "doc": "

    \n", "default_value": "Slovenian"}, {"fullname": "main.askai.language.language.Language.SQ_AL", "modulename": "main.askai.language.language", "qualname": "Language.SQ_AL", "kind": "variable", "doc": "

    \n", "default_value": "Albanian"}, {"fullname": "main.askai.language.language.Language.SR_ME", "modulename": "main.askai.language.language", "qualname": "Language.SR_ME", "kind": "variable", "doc": "

    \n", "default_value": "Serbian"}, {"fullname": "main.askai.language.language.Language.SR_RS", "modulename": "main.askai.language.language", "qualname": "Language.SR_RS", "kind": "variable", "doc": "

    \n", "default_value": "Serbian"}, {"fullname": "main.askai.language.language.Language.SV_SE", "modulename": "main.askai.language.language", "qualname": "Language.SV_SE", "kind": "variable", "doc": "

    \n", "default_value": "Swedish"}, {"fullname": "main.askai.language.language.Language.TA_IN", "modulename": "main.askai.language.language", "qualname": "Language.TA_IN", "kind": "variable", "doc": "

    \n", "default_value": "Tamil"}, {"fullname": "main.askai.language.language.Language.TE_IN", "modulename": "main.askai.language.language", "qualname": "Language.TE_IN", "kind": "variable", "doc": "

    \n", "default_value": "Telugu"}, {"fullname": "main.askai.language.language.Language.TH_TH", "modulename": "main.askai.language.language", "qualname": "Language.TH_TH", "kind": "variable", "doc": "

    \n", "default_value": "Thai"}, {"fullname": "main.askai.language.language.Language.TR_TR", "modulename": "main.askai.language.language", "qualname": "Language.TR_TR", "kind": "variable", "doc": "

    \n", "default_value": "Turkish"}, {"fullname": "main.askai.language.language.Language.UK_UA", "modulename": "main.askai.language.language", "qualname": "Language.UK_UA", "kind": "variable", "doc": "

    \n", "default_value": "Ukrainian"}, {"fullname": "main.askai.language.language.Language.VI_VN", "modulename": "main.askai.language.language", "qualname": "Language.VI_VN", "kind": "variable", "doc": "

    \n", "default_value": "Vietnamese"}, {"fullname": "main.askai.language.language.Language.ZH_CN", "modulename": "main.askai.language.language", "qualname": "Language.ZH_CN", "kind": "variable", "doc": "

    \n", "default_value": "Simplified Chinese"}, {"fullname": "main.askai.language.language.Language.ZH_HK", "modulename": "main.askai.language.language", "qualname": "Language.ZH_HK", "kind": "variable", "doc": "

    \n", "default_value": "Traditional Chinese"}, {"fullname": "main.askai.language.language.Language.ZH_SG", "modulename": "main.askai.language.language", "qualname": "Language.ZH_SG", "kind": "variable", "doc": "

    \n", "default_value": "Chinese"}, {"fullname": "main.askai.language.language.Language.ZH_TW", "modulename": "main.askai.language.language", "qualname": "Language.ZH_TW", "kind": "variable", "doc": "

    \n", "default_value": "Traditional Chinese"}, {"fullname": "main.askai.language.language.Language.of_locale", "modulename": "main.askai.language.language", "qualname": "Language.of_locale", "kind": "function", "doc": "

    Create a Language object based on a locale string or tuple containing the language code and encoding.

    \n\n
    Parameters
    \n\n
      \n
    • loc: The locale to parse.
    • \n
    \n", "signature": "(\tloc: str | tuple[str | None, ...]) -> main.askai.language.language.Language:", "funcdef": "def"}, {"fullname": "main.askai.language.language.Language.locale", "modulename": "main.askai.language.language", "qualname": "Language.locale", "kind": "variable", "doc": "

    Return a tuple containing the locale attributes.\nE.g:. (en_US, utf-8)

    \n", "annotation": ": tuple"}, {"fullname": "main.askai.language.language.Language.idiom", "modulename": "main.askai.language.language", "qualname": "Language.idiom", "kind": "variable", "doc": "

    Return a string representing the idiom.\nE.g:. en_US

    \n", "annotation": ": str"}, {"fullname": "main.askai.language.language.Language.encoding", "modulename": "main.askai.language.language", "qualname": "Language.encoding", "kind": "variable", "doc": "

    Return the charset (encoding) required for the language to be properly displayed.\nE.g:. utf-8

    \n", "annotation": ": hspylib.core.enums.charset.Charset"}, {"fullname": "main.askai.language.language.Language.name", "modulename": "main.askai.language.language", "qualname": "Language.name", "kind": "variable", "doc": "

    Return the language name.\nE.g:. English

    \n", "annotation": ": str"}, {"fullname": "main.askai.language.language.Language.country", "modulename": "main.askai.language.language", "qualname": "Language.country", "kind": "variable", "doc": "

    Return the country where the language is spoken.\nE.g:. U.S.A.

    \n", "annotation": ": str"}, {"fullname": "main.askai.language.language.Language.language", "modulename": "main.askai.language.language", "qualname": "Language.language", "kind": "variable", "doc": "

    Return a mnemonic representing the language.\nE.g:. en

    \n", "annotation": ": str"}, {"fullname": "main.askai.language.language.Language.territory", "modulename": "main.askai.language.language", "qualname": "Language.territory", "kind": "variable", "doc": "

    Return a mnemonic representing the territory (Alpha-2 code).\nE.g:. US

    \n", "annotation": ": str"}, {"fullname": "main.askai.language.translators", "modulename": "main.askai.language.translators", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.language.translators.argos", "modulename": "main.askai.language.translators.argos", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.language.argos_translator\n @file: argos_translator.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.language.translators.argos.ArgosTranslator", "modulename": "main.askai.language.translators.argos", "qualname": "ArgosTranslator", "kind": "class", "doc": "

    Provides a multilingual offline translation engine.\nLanguage packages are downloaded at: ~/.local/share/argos-translate/packages

    \n", "bases": "askai.language.ai_translator.AITranslator"}, {"fullname": "main.askai.language.translators.argos.ArgosTranslator.__init__", "modulename": "main.askai.language.translators.argos", "qualname": "ArgosTranslator.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tfrom_idiom: askai.language.language.Language,\tto_idiom: askai.language.language.Language)"}, {"fullname": "main.askai.language.translators.argos.ArgosTranslator.translate", "modulename": "main.askai.language.translators.argos", "qualname": "ArgosTranslator.translate", "kind": "function", "doc": "

    Translate text using Argos translator.

    \n\n
    Parameters
    \n\n
      \n
    • text: Text to translate.
    • \n
    \n", "signature": "(self, text: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.language.translators.argos.ArgosTranslator.name", "modulename": "main.askai.language.translators.argos", "qualname": "ArgosTranslator.name", "kind": "function", "doc": "

    Return the translator name or model.

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.language.translators.deep", "modulename": "main.askai.language.translators.deep", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.language.argos_translator\n @file: argos_translator.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.language.translators.deep.DeepLTranslator", "modulename": "main.askai.language.translators.deep", "qualname": "DeepLTranslator", "kind": "class", "doc": "

    Provides a multilingual online translation engine.

    \n", "bases": "askai.language.ai_translator.AITranslator"}, {"fullname": "main.askai.language.translators.deep.DeepLTranslator.__init__", "modulename": "main.askai.language.translators.deep", "qualname": "DeepLTranslator.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tfrom_idiom: askai.language.language.Language,\tto_idiom: askai.language.language.Language)"}, {"fullname": "main.askai.language.translators.deep.DeepLTranslator.translate", "modulename": "main.askai.language.translators.deep", "qualname": "DeepLTranslator.translate", "kind": "function", "doc": "

    Translate text using te default translator.

    \n\n
    Parameters
    \n\n
      \n
    • text: Text to translate.
    • \n
    \n", "signature": "(self, text: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.language.translators.deep.DeepLTranslator.name", "modulename": "main.askai.language.translators.deep", "qualname": "DeepLTranslator.name", "kind": "function", "doc": "

    Return the translator name or model.

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.language.translators.marian", "modulename": "main.askai.language.translators.marian", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.language.translators.marian.MarianTranslator", "modulename": "main.askai.language.translators.marian", "qualname": "MarianTranslator", "kind": "class", "doc": "

    Provides a multilingual offline translation engine.

    \n", "bases": "askai.language.ai_translator.AITranslator"}, {"fullname": "main.askai.language.translators.marian.MarianTranslator.__init__", "modulename": "main.askai.language.translators.marian", "qualname": "MarianTranslator.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tfrom_idiom: askai.language.language.Language,\tto_idiom: askai.language.language.Language)"}, {"fullname": "main.askai.language.translators.marian.MarianTranslator.MODEL_NAME", "modulename": "main.askai.language.translators.marian", "qualname": "MarianTranslator.MODEL_NAME", "kind": "variable", "doc": "

    \n", "default_value": "'Helsinki-NLP/opus-mt-en-ROMANCE'"}, {"fullname": "main.askai.language.translators.marian.MarianTranslator.translate", "modulename": "main.askai.language.translators.marian", "qualname": "MarianTranslator.translate", "kind": "function", "doc": "

    Translate text using te default translator.

    \n\n
    Parameters
    \n\n
      \n
    • text: Text to translate.
    • \n
    \n", "signature": "(self, text: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.language.translators.marian.MarianTranslator.name", "modulename": "main.askai.language.translators.marian", "qualname": "MarianTranslator.name", "kind": "function", "doc": "

    Return the translator name or model.

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.tui", "modulename": "main.askai.tui", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.tui.app_header", "modulename": "main.askai.tui.app_header", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.tui.header\n @file: app_header.py\n@created: Mon, 29 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.tui.app_header.Header", "modulename": "main.askai.tui.app_header", "qualname": "Header", "kind": "class", "doc": "

    A header widget with icon and notifications.

    \n", "bases": "textual.widget.Widget"}, {"fullname": "main.askai.tui.app_header.Header.__init__", "modulename": "main.askai.tui.app_header", "qualname": "Header.__init__", "kind": "function", "doc": "

    Initialize a Widget.

    \n\n

    Args:\n *children: Child widgets.\n name: The name of the widget.\n id: The ID of the widget in the DOM.\n classes: The CSS classes for the widget.\n disabled: Whether the widget is disabled or not.

    \n", "signature": "(**kwargs)"}, {"fullname": "main.askai.tui.app_header.Header.notifications", "modulename": "main.askai.tui.app_header", "qualname": "Header.notifications", "kind": "variable", "doc": "

    \n"}, {"fullname": "main.askai.tui.app_header.Header.screen_title", "modulename": "main.askai.tui.app_header", "qualname": "Header.screen_title", "kind": "variable", "doc": "

    The title that this header will display.

    \n", "annotation": ": str"}, {"fullname": "main.askai.tui.app_header.Header.screen_sub_title", "modulename": "main.askai.tui.app_header", "qualname": "Header.screen_sub_title", "kind": "variable", "doc": "

    The sub-title that this header will display.

    \n", "annotation": ": str"}, {"fullname": "main.askai.tui.app_header.Header.compose", "modulename": "main.askai.tui.app_header", "qualname": "Header.compose", "kind": "function", "doc": "

    Compose the Header Widget.

    \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "main.askai.tui.app_header.Header.can_focus", "modulename": "main.askai.tui.app_header", "qualname": "Header.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_header.Header.can_focus_children", "modulename": "main.askai.tui.app_header", "qualname": "Header.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.app_header.HeaderTitle", "modulename": "main.askai.tui.app_header", "qualname": "HeaderTitle", "kind": "class", "doc": "

    Display the title / subtitle in the header.

    \n", "bases": "textual.widget.Widget"}, {"fullname": "main.askai.tui.app_header.HeaderTitle.text", "modulename": "main.askai.tui.app_header", "qualname": "HeaderTitle.text", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderTitle.sub_text", "modulename": "main.askai.tui.app_header", "qualname": "HeaderTitle.sub_text", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderTitle.render", "modulename": "main.askai.tui.app_header", "qualname": "HeaderTitle.render", "kind": "function", "doc": "

    Render the title and sub-title.

    \n", "signature": "(\tself) -> Union[rich.console.ConsoleRenderable, rich.console.RichCast, str]:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_header.HeaderTitle.can_focus", "modulename": "main.askai.tui.app_header", "qualname": "HeaderTitle.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_header.HeaderTitle.can_focus_children", "modulename": "main.askai.tui.app_header", "qualname": "HeaderTitle.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications", "kind": "class", "doc": "

    Display a notification widget on the right of the header.

    \n", "bases": "textual.widget.Widget"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.__init__", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.__init__", "kind": "function", "doc": "

    Initialize a Widget.

    \n\n

    Args:\n *children: Child widgets.\n name: The name of the widget.\n id: The ID of the widget in the DOM.\n classes: The CSS classes for the widget.\n disabled: Whether the widget is disabled or not.

    \n", "signature": "()"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.speaking", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.speaking", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.debugging", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.debugging", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.caching", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.caching", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.listening", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.listening", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.headphones", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.headphones", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.idiom", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.idiom", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.tooltip", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.tooltip", "kind": "variable", "doc": "

    Tooltip for the widget, or None for no tooltip.

    \n", "annotation": ": Union[rich.console.ConsoleRenderable, rich.console.RichCast, str, NoneType]"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.render", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.render", "kind": "function", "doc": "

    Render the header notifications.

    \n", "signature": "(\tself) -> Union[rich.console.ConsoleRenderable, rich.console.RichCast, str]:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.refresh_icons", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.refresh_icons", "kind": "function", "doc": "

    Update the application widgets. This callback is required because ask_and_reply is async.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.watch_speaking", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.watch_speaking", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.watch_debugging", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.watch_debugging", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.watch_caching", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.watch_caching", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.watch_listening", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.watch_listening", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.watch_headphones", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.watch_headphones", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.watch_idiom", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.watch_idiom", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.can_focus", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.can_focus_children", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.app_icons", "modulename": "main.askai.tui.app_icons", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.tui.app_icons\n @file: app_icons.py\n@created: Mon, 29 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.tui.app_icons.AppIcons", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons", "kind": "class", "doc": "

    Enumerated icons of the new AskAI UI application.

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.tui.app_icons.AppIcons.DEFAULT", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.DEFAULT", "kind": "variable", "doc": "

    \n", "default_value": "DEFAULT"}, {"fullname": "main.askai.tui.app_icons.AppIcons.STARTED", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.STARTED", "kind": "variable", "doc": "

    \n", "default_value": "STARTED"}, {"fullname": "main.askai.tui.app_icons.AppIcons.MENU", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.MENU", "kind": "variable", "doc": "

    \n", "default_value": "MENU"}, {"fullname": "main.askai.tui.app_icons.AppIcons.TOC", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.TOC", "kind": "variable", "doc": "

    \n", "default_value": "TOC"}, {"fullname": "main.askai.tui.app_icons.AppIcons.EXIT", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.EXIT", "kind": "variable", "doc": "

    \n", "default_value": "EXIT"}, {"fullname": "main.askai.tui.app_icons.AppIcons.HELP", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.HELP", "kind": "variable", "doc": "

    \n", "default_value": "HELP"}, {"fullname": "main.askai.tui.app_icons.AppIcons.SETTINGS", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.SETTINGS", "kind": "variable", "doc": "

    \n", "default_value": "SETTINGS"}, {"fullname": "main.askai.tui.app_icons.AppIcons.INFO", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.INFO", "kind": "variable", "doc": "

    \n", "default_value": "INFO"}, {"fullname": "main.askai.tui.app_icons.AppIcons.CONSOLE", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.CONSOLE", "kind": "variable", "doc": "

    \n", "default_value": "CONSOLE"}, {"fullname": "main.askai.tui.app_icons.AppIcons.DEBUG_ON", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.DEBUG_ON", "kind": "variable", "doc": "

    \n", "default_value": "DEBUG_ON"}, {"fullname": "main.askai.tui.app_icons.AppIcons.DEBUG_OFF", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.DEBUG_OFF", "kind": "variable", "doc": "

    \n", "default_value": "DEBUG_OFF"}, {"fullname": "main.askai.tui.app_icons.AppIcons.SPEAKING_ON", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.SPEAKING_ON", "kind": "variable", "doc": "

    \n", "default_value": "SPEAKING_ON"}, {"fullname": "main.askai.tui.app_icons.AppIcons.SPEAKING_OFF", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.SPEAKING_OFF", "kind": "variable", "doc": "

    \n", "default_value": "SPEAKING_OFF"}, {"fullname": "main.askai.tui.app_icons.AppIcons.CACHING_ON", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.CACHING_ON", "kind": "variable", "doc": "

    \n", "default_value": "CACHING_ON"}, {"fullname": "main.askai.tui.app_icons.AppIcons.CACHING_OFF", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.CACHING_OFF", "kind": "variable", "doc": "

    \n", "default_value": "CACHING_OFF"}, {"fullname": "main.askai.tui.app_icons.AppIcons.SEPARATOR_V", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.SEPARATOR_V", "kind": "variable", "doc": "

    \n", "default_value": "SEPARATOR_V"}, {"fullname": "main.askai.tui.app_icons.AppIcons.SEPARATOR_H", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.SEPARATOR_H", "kind": "variable", "doc": "

    \n", "default_value": "SEPARATOR_H"}, {"fullname": "main.askai.tui.app_icons.AppIcons.LISTENING_ON", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.LISTENING_ON", "kind": "variable", "doc": "

    \n", "default_value": "LISTENING_ON"}, {"fullname": "main.askai.tui.app_icons.AppIcons.LISTENING_OFF", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.LISTENING_OFF", "kind": "variable", "doc": "

    \n", "default_value": "LISTENING_OFF"}, {"fullname": "main.askai.tui.app_icons.AppIcons.BUILT_IN_SPEAKER", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.BUILT_IN_SPEAKER", "kind": "variable", "doc": "

    \n", "default_value": "BUILT_IN_SPEAKER"}, {"fullname": "main.askai.tui.app_icons.AppIcons.HEADPHONES", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.HEADPHONES", "kind": "variable", "doc": "

    \n", "default_value": "HEADPHONES"}, {"fullname": "main.askai.tui.app_icons.AppIcons.CLOCK", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.CLOCK", "kind": "variable", "doc": "

    \n", "default_value": "CLOCK"}, {"fullname": "main.askai.tui.app_suggester", "modulename": "main.askai.tui.app_suggester", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.tui.app_suggester\n @file: app_suggester.py\n@created: Wed, 19 Jun 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.tui.app_suggester.InputSuggester", "modulename": "main.askai.tui.app_suggester", "qualname": "InputSuggester", "kind": "class", "doc": "

    Implement a list-based Input suggester.

    \n", "bases": "textual.suggester.Suggester"}, {"fullname": "main.askai.tui.app_suggester.InputSuggester.__init__", "modulename": "main.askai.tui.app_suggester", "qualname": "InputSuggester.__init__", "kind": "function", "doc": "

    Create a suggester object.

    \n\n

    Args:\n use_cache: Whether to cache suggestion results.\n case_sensitive: Whether suggestions are case sensitive or not.\n If they are not, incoming values are casefolded before generating\n the suggestion.

    \n", "signature": "(*, case_sensitive: bool = True)"}, {"fullname": "main.askai.tui.app_suggester.InputSuggester.suggestions", "modulename": "main.askai.tui.app_suggester", "qualname": "InputSuggester.suggestions", "kind": "function", "doc": "

    Return all available suggestions.

    \n", "signature": "(self) -> list[str]:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_suggester.InputSuggester.add_suggestion", "modulename": "main.askai.tui.app_suggester", "qualname": "InputSuggester.add_suggestion", "kind": "function", "doc": "

    Add a new suggestion.

    \n", "signature": "(self, value: str) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_suggester.InputSuggester.get_suggestion", "modulename": "main.askai.tui.app_suggester", "qualname": "InputSuggester.get_suggestion", "kind": "function", "doc": "

    Get a suggestion from the list.

    \n", "signature": "(self, value: str) -> Optional[str]:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_widgets", "modulename": "main.askai.tui.app_widgets", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.tui.app_widgets\n @file: app_widgets.py\n@created: Mon, 29 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon", "kind": "class", "doc": "

    Display an 'icon' on the left of the header.

    \n", "bases": "textual.widget.Widget"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.__init__", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.__init__", "kind": "function", "doc": "

    Initialize a Widget.

    \n\n

    Args:\n *children: Child widgets.\n name: The name of the widget.\n id: The ID of the widget in the DOM.\n classes: The CSS classes for the widget.\n disabled: Whether the widget is disabled or not.

    \n", "signature": "(\tmenu_icon: str = '\\uf128',\ttooltip: str = None,\ton_click: Optional[Callable] = None,\t**kwargs)"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.menu_icon", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.menu_icon", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.click_cb", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.click_cb", "kind": "variable", "doc": "

    \n", "annotation": ": Callable"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.tooltip", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.tooltip", "kind": "variable", "doc": "

    Tooltip for the widget, or None for no tooltip.

    \n", "annotation": ": Union[rich.console.ConsoleRenderable, rich.console.RichCast, str, NoneType]"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.on_click", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.on_click", "kind": "function", "doc": "

    \n", "signature": "(self, event: textual.events.Click) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.render", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.render", "kind": "function", "doc": "

    Get text or Rich renderable for this widget.

    \n\n

    Implement this for custom widgets.

    \n\n

    Example:

    \n\n
    \n
    from textual.app import RenderableType\nfrom textual.widget import Widget\n\nclass CustomWidget(Widget):\n    def render(self) -> RenderableType:\n        return "Welcome to [bold red]Textual[/]!"\n
    \n
    \n
    \n\n

    Returns:\n Any renderable.

    \n", "signature": "(\tself) -> Union[rich.console.ConsoleRenderable, rich.console.RichCast, str]:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.can_focus", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.can_focus_children", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.app_widgets.Splash", "modulename": "main.askai.tui.app_widgets", "qualname": "Splash", "kind": "class", "doc": "

    Splash widget that extends Container.

    \n", "bases": "textual.containers.Container"}, {"fullname": "main.askai.tui.app_widgets.Splash.__init__", "modulename": "main.askai.tui.app_widgets", "qualname": "Splash.__init__", "kind": "function", "doc": "

    Initialize a Widget.

    \n\n

    Args:\n *children: Child widgets.\n name: The name of the widget.\n id: The ID of the widget in the DOM.\n classes: The CSS classes for the widget.\n disabled: Whether the widget is disabled or not.

    \n", "signature": "(splash_image: str)"}, {"fullname": "main.askai.tui.app_widgets.Splash.splash_image", "modulename": "main.askai.tui.app_widgets", "qualname": "Splash.splash_image", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n", "annotation": ": str"}, {"fullname": "main.askai.tui.app_widgets.Splash.compose", "modulename": "main.askai.tui.app_widgets", "qualname": "Splash.compose", "kind": "function", "doc": "

    Called by Textual to create child widgets.

    \n\n

    This method is called when a widget is mounted or by setting recompose=True when\ncalling [refresh()][textual.widget.Widget.refresh].

    \n\n

    Note that you don't typically need to explicitly call this method.

    \n\n

    Example:

    \n\n
    \n
    def compose(self) -> ComposeResult:\n    yield Header()\n    yield Label("Press the button below:")\n    yield Button()\n    yield Footer()\n
    \n
    \n
    \n", "signature": "(self) -> Iterable[textual.widget.Widget]:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_widgets.Splash.on_mount", "modulename": "main.askai.tui.app_widgets", "qualname": "Splash.on_mount", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_widgets.Splash.can_focus", "modulename": "main.askai.tui.app_widgets", "qualname": "Splash.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_widgets.Splash.can_focus_children", "modulename": "main.askai.tui.app_widgets", "qualname": "Splash.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.app_widgets.AppHelp", "modulename": "main.askai.tui.app_widgets", "qualname": "AppHelp", "kind": "class", "doc": "

    Application Help Widget.

    \n", "bases": "textual.widgets._markdown.Markdown"}, {"fullname": "main.askai.tui.app_widgets.AppHelp.__init__", "modulename": "main.askai.tui.app_widgets", "qualname": "AppHelp.__init__", "kind": "function", "doc": "

    A Markdown widget.

    \n\n

    Args:\n markdown: String containing Markdown or None to leave blank for now.\n name: The name of the widget.\n id: The ID of the widget in the DOM.\n classes: The CSS classes of the widget.\n parser_factory: A factory function to return a configured MarkdownIt instance. If None, a \"gfm-like\" parser is used.

    \n", "signature": "(help_text: str)"}, {"fullname": "main.askai.tui.app_widgets.AppHelp.help_text", "modulename": "main.askai.tui.app_widgets", "qualname": "AppHelp.help_text", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.tui.app_widgets.AppHelp.on_mount", "modulename": "main.askai.tui.app_widgets", "qualname": "AppHelp.on_mount", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_widgets.AppHelp.can_focus", "modulename": "main.askai.tui.app_widgets", "qualname": "AppHelp.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_widgets.AppHelp.can_focus_children", "modulename": "main.askai.tui.app_widgets", "qualname": "AppHelp.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.app_widgets.AppInfo", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo", "kind": "class", "doc": "

    Application Information Widget.

    \n", "bases": "textual.widgets._static.Static"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.__init__", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.__init__", "kind": "function", "doc": "

    Initialize a Widget.

    \n\n

    Args:\n *children: Child widgets.\n name: The name of the widget.\n id: The ID of the widget in the DOM.\n classes: The CSS classes for the widget.\n disabled: Whether the widget is disabled or not.

    \n", "signature": "(app_info: str)"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.info_text", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.info_text", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.credits", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.credits", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'\\n Author: \\uf486 Hugo Saporetti Junior\\n GitHub: \\uf408 https://github.com/yorevs/askai\\nLinkedIn: \\uf83a https://www.linkedin.com/in/yorevs/\\n Demo: \\uf16a https://www.youtube.com/watch?v=ZlVOisiUEvs&t=69s\\n--------------------------------------------------------------------------------\\n\\uf004 Thanks for using AskAI \\uf004\\n'"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.info", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.info", "kind": "variable", "doc": "

    \n", "annotation": ": textual.widgets._static.Static"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.compose", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.compose", "kind": "function", "doc": "

    Called by Textual to create child widgets.

    \n\n

    This method is called when a widget is mounted or by setting recompose=True when\ncalling [refresh()][textual.widget.Widget.refresh].

    \n\n

    Note that you don't typically need to explicitly call this method.

    \n\n

    Example:

    \n\n
    \n
    def compose(self) -> ComposeResult:\n    yield Header()\n    yield Label("Press the button below:")\n    yield Button()\n    yield Footer()\n
    \n
    \n
    \n", "signature": "(self) -> Iterable[textual.widget.Widget]:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.on_mount", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.on_mount", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.watch_info_text", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.watch_info_text", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.can_focus", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.can_focus_children", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.app_widgets.AppSettings", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings", "kind": "class", "doc": "

    Application DataTable Widget.

    \n", "bases": "textual.widgets._static.Static"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.__init__", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.__init__", "kind": "function", "doc": "

    Initialize a Widget.

    \n\n

    Args:\n *children: Child widgets.\n name: The name of the widget.\n id: The ID of the widget in the DOM.\n classes: The CSS classes for the widget.\n disabled: Whether the widget is disabled or not.

    \n", "signature": "(data: list[tuple[str, ...]] = None)"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.data", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.data", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.table", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.table", "kind": "variable", "doc": "

    \n"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.compose", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.compose", "kind": "function", "doc": "

    Called by Textual to create child widgets.

    \n\n

    This method is called when a widget is mounted or by setting recompose=True when\ncalling [refresh()][textual.widget.Widget.refresh].

    \n\n

    Note that you don't typically need to explicitly call this method.

    \n\n

    Example:

    \n\n
    \n
    def compose(self) -> ComposeResult:\n    yield Header()\n    yield Label("Press the button below:")\n    yield Button()\n    yield Footer()\n
    \n
    \n
    \n", "signature": "(self) -> Iterable[textual.widget.Widget]:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.on_mount", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.on_mount", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.watch_data", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.watch_data", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.can_focus", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.can_focus_children", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.askai_app", "modulename": "main.askai.tui.askai_app", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.tui.askai_app\n @file: askai_app.py\n@created: Mon, 29 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.tui.askai_app.SOURCE_DIR", "modulename": "main.askai.tui.askai_app", "qualname": "SOURCE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai')"}, {"fullname": "main.askai.tui.askai_app.RESOURCE_DIR", "modulename": "main.askai.tui.askai_app", "qualname": "RESOURCE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources')"}, {"fullname": "main.askai.tui.askai_app.AskAiApp", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp", "kind": "class", "doc": "

    The AskAI Textual application.

    \n", "bases": "textual.app.App[NoneType]"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.__init__", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.__init__", "kind": "function", "doc": "

    Create an instance of an app.

    \n\n

    Args:\n driver_class: Driver class or None to auto-detect.\n This will be used by some Textual tools.\n css_path: Path to CSS or None to use the CSS_PATH class variable.\n To load multiple CSS files, pass a list of strings or paths which\n will be loaded in order.\n watch_css: Reload CSS if the files changed. This is set automatically if\n you are using textual run with the dev switch.

    \n\n

    Raises:\n CssPathError: When the supplied CSS path(s) are an unexpected type.

    \n", "signature": "(\tspeak: bool,\tdebug: bool,\tcacheable: bool,\ttempo: int,\tengine_name: str,\tmodel_name: str)"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.APP_TITLE", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.APP_TITLE", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'AskAI v1.0.11'"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.CSS_PATH", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.CSS_PATH", "kind": "variable", "doc": "

    File paths to load CSS from.

    \n", "default_value": "'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources/askai.tcss'"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.BINDINGS", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.BINDINGS", "kind": "variable", "doc": "

    \n", "default_value": "[('q', 'quit', ' Quit'), ('c', 'clear', ' Clear'), ('d', 'debugging', ' Debugging'), ('s', 'speaking', ' Speaking'), ('ctrl+l', 'ptt', ' Push-to-Talk')]"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.ENABLE_COMMAND_PALETTE", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.ENABLE_COMMAND_PALETTE", "kind": "variable", "doc": "

    Should the [command palette][textual.command.CommandPalette] be enabled for the application?

    \n", "default_value": "False"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.askai", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.askai", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.askai.AskAi"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.engine", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.engine", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.engine.ai_engine.AIEngine"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.app_settings", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.app_settings", "kind": "variable", "doc": "

    \n", "annotation": ": list[tuple[str, ...]]"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.console_path", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.console_path", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.md_console", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.md_console", "kind": "variable", "doc": "

    Get the MarkdownViewer widget.

    \n", "annotation": ": textual.widgets._markdown.MarkdownViewer"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.info", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.info", "kind": "variable", "doc": "

    Get the AppInfo widget.

    \n", "annotation": ": askai.tui.app_widgets.AppInfo"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.help", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.help", "kind": "variable", "doc": "

    Get the AppHelp widget.

    \n", "annotation": ": askai.tui.app_widgets.AppHelp"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.settings", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.settings", "kind": "variable", "doc": "

    Get the AppSettings widget.

    \n", "annotation": ": askai.tui.app_widgets.AppSettings"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.splash", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.splash", "kind": "variable", "doc": "

    Get the Splash widget.

    \n", "annotation": ": askai.tui.app_widgets.Splash"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.line_input", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.line_input", "kind": "variable", "doc": "

    Get the Input widget.

    \n", "annotation": ": textual.widgets._input.Input"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.suggester", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.suggester", "kind": "variable", "doc": "

    Get the Input Suggester.

    \n", "annotation": ": Optional[askai.tui.app_suggester.InputSuggester]"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.header", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.header", "kind": "variable", "doc": "

    Get the Input widget.

    \n", "annotation": ": askai.tui.app_header.Header"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.footer", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.footer", "kind": "variable", "doc": "

    Get the Input widget.

    \n", "annotation": ": textual.widgets._footer.Footer"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.compose", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.compose", "kind": "function", "doc": "

    Called to add widgets to the app.

    \n", "signature": "(self) -> Iterable[textual.widget.Widget]:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.on_mount", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.on_mount", "kind": "function", "doc": "

    Called application is mounted.

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.on_markdown_viewer_navigator_updated", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.on_markdown_viewer_navigator_updated", "kind": "function", "doc": "

    Refresh bindings for forward / back when the document changes.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.action_back", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.action_back", "kind": "function", "doc": "

    Navigate backwards.

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.action_forward", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.action_forward", "kind": "function", "doc": "

    Navigate forwards.

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.action_toggle_table_of_contents", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.action_toggle_table_of_contents", "kind": "function", "doc": "

    Toggles display of the table of contents.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.check_action", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.check_action", "kind": "function", "doc": "

    Check if certain actions can be performed.

    \n", "signature": "(self, action: str, _) -> Optional[bool]:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.enable_controls", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.enable_controls", "kind": "function", "doc": "

    Enable all UI controls (header, input and footer).

    \n", "signature": "(self, enable: bool = True) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.activate_markdown", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.activate_markdown", "kind": "function", "doc": "

    Activate the Markdown console.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.action_clear", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.action_clear", "kind": "function", "doc": "

    Clear the output console.

    \n", "signature": "(self, overwrite: bool = True) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.action_speaking", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.action_speaking", "kind": "function", "doc": "

    Toggle Speaking ON/OFF.

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.action_debugging", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.action_debugging", "kind": "function", "doc": "

    Toggle Debugging ON/OFF.

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.action_ptt", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.action_ptt", "kind": "function", "doc": "

    Push-To-Talk STT as input method.

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.on_submit", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.on_submit", "kind": "function", "doc": "

    A coroutine to handle a input submission.

    \n", "signature": "(self, submitted: textual.widgets._input.Input.Submitted) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.display_text", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.display_text", "kind": "function", "doc": "

    Send the text to the Markdown console.

    \n\n
    Parameters
    \n\n
      \n
    • markdown_text: the text to be displayed.
    • \n
    \n", "signature": "(self, markdown_text: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.ask_and_reply", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.ask_and_reply", "kind": "function", "doc": "

    Ask the question to the AI, and provide the reply.

    \n\n
    Parameters
    \n\n
      \n
    • question: The question to ask to the AI engine.
    • \n
    \n", "signature": "(self, question: str) -> tuple[bool, typing.Optional[str]]:", "funcdef": "def"}]; + /** pdoc search index */const docs = [{"fullname": "main.askai", "modulename": "main.askai", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core", "modulename": "main.askai.core", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.askai", "modulename": "main.askai.core.askai", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core\n @file: askai.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.askai.QueryString", "modulename": "main.askai.core.askai", "qualname": "QueryString", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "Union[str, List[str], NoneType]"}, {"fullname": "main.askai.core.askai.AskAi", "modulename": "main.askai.core.askai", "qualname": "AskAi", "kind": "class", "doc": "

    The AskAI core functionalities.

    \n"}, {"fullname": "main.askai.core.askai.AskAi.__init__", "modulename": "main.askai.core.askai", "qualname": "AskAi.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tinteractive: bool,\tspeak: bool,\tdebug: bool,\tcacheable: bool,\ttempo: int,\tengine_name: str,\tmodel_name: str)"}, {"fullname": "main.askai.core.askai.AskAi.SOURCE_DIR", "modulename": "main.askai.core.askai", "qualname": "AskAi.SOURCE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai')"}, {"fullname": "main.askai.core.askai.AskAi.RESOURCE_DIR", "modulename": "main.askai.core.askai", "qualname": "AskAi.RESOURCE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources')"}, {"fullname": "main.askai.core.askai.AskAi.SPLASH", "modulename": "main.askai.core.askai", "qualname": "AskAi.SPLASH", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "" ################\\n ### ###\\n ## ##### ### ##\\n # ####### # ######## ###\\n ### # #### # ###### ## #\\n ### # ## ( ) #### ## ### #\\n ## ( ) # ## ########## # #\\n _ _ _\\n | | ___ __ _ __| (_)_ __ __ _\\n | | / _ \\\\ / _` |/ _` | | '_ \\\\ / _` |\\n | |__| (_) | (_| | (_| | | | | | (_| |_ _ _\\n |_____\\\\___/ \\\\__,_|\\\\__,_|_|_| |_|\\\\__, (_|_|_)\\n |___/\\n\\n # # ## ## ### ################ ####\\n( ) ( ) ### ### ############## #\\n ### # ### ############ #\\n # ### ######## #\\n # #### #\\n ( ) #####\\n\\n""}, {"fullname": "main.askai.core.askai.AskAi.RunModes", "modulename": "main.askai.core.askai", "qualname": "AskAi.RunModes", "kind": "class", "doc": "

    AskAI run modes

    \n", "bases": "enum.Enum"}, {"fullname": "main.askai.core.askai.AskAi.RunModes.ASKAI_TUI", "modulename": "main.askai.core.askai", "qualname": "AskAi.RunModes.ASKAI_TUI", "kind": "variable", "doc": "

    \n", "default_value": "<RunModes.ASKAI_TUI: 'ASKAI_TUI'>"}, {"fullname": "main.askai.core.askai.AskAi.RunModes.ASKAI_CLI", "modulename": "main.askai.core.askai", "qualname": "AskAi.RunModes.ASKAI_CLI", "kind": "variable", "doc": "

    \n", "default_value": "<RunModes.ASKAI_CLI: 'ASKAI_CLI'>"}, {"fullname": "main.askai.core.askai.AskAi.RunModes.ASKAI_CMD", "modulename": "main.askai.core.askai", "qualname": "AskAi.RunModes.ASKAI_CMD", "kind": "variable", "doc": "

    \n", "default_value": "<RunModes.ASKAI_CMD: 'ASKAI_CMD'>"}, {"fullname": "main.askai.core.askai.AskAi.engine", "modulename": "main.askai.core.askai", "qualname": "AskAi.engine", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.engine.ai_engine.AIEngine"}, {"fullname": "main.askai.core.askai.AskAi.context", "modulename": "main.askai.core.askai", "qualname": "AskAi.context", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.support.chat_context.ChatContext"}, {"fullname": "main.askai.core.askai.AskAi.mode", "modulename": "main.askai.core.askai", "qualname": "AskAi.mode", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.enums.router_mode.RouterMode"}, {"fullname": "main.askai.core.askai.AskAi.query_prompt", "modulename": "main.askai.core.askai", "qualname": "AskAi.query_prompt", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.askai.AskAi.console_path", "modulename": "main.askai.core.askai", "qualname": "AskAi.console_path", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path"}, {"fullname": "main.askai.core.askai.AskAi.session_id", "modulename": "main.askai.core.askai", "qualname": "AskAi.session_id", "kind": "variable", "doc": "

    Get the Session id.

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.askai.AskAi.app_settings", "modulename": "main.askai.core.askai", "qualname": "AskAi.app_settings", "kind": "variable", "doc": "

    \n", "annotation": ": list[tuple[str, ...]]"}, {"fullname": "main.askai.core.askai.AskAi.run", "modulename": "main.askai.core.askai", "qualname": "AskAi.run", "kind": "function", "doc": "

    Run the application.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.askai.AskAi.ask_and_reply", "modulename": "main.askai.core.askai", "qualname": "AskAi.ask_and_reply", "kind": "function", "doc": "

    Ask the specified question to the AI and provide the reply.

    \n\n
    Parameters
    \n\n
      \n
    • question: The question to ask the AI engine.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A tuple containing a boolean indicating success or failure, and the AI's reply as an optional string.

    \n
    \n", "signature": "(self, question: str) -> tuple[bool, typing.Optional[str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_cli", "modulename": "main.askai.core.askai_cli", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core\n @file: askai_cli.py\n@created: Fri, 9 Aug 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.askai_cli.QueryString", "modulename": "main.askai.core.askai_cli", "qualname": "QueryString", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "Union[str, List[str], NoneType]"}, {"fullname": "main.askai.core.askai_cli.AskAiCli", "modulename": "main.askai.core.askai_cli", "qualname": "AskAiCli", "kind": "class", "doc": "

    The AskAI CLI application.

    \n", "bases": "askai.core.askai.AskAi"}, {"fullname": "main.askai.core.askai_cli.AskAiCli.__init__", "modulename": "main.askai.core.askai_cli", "qualname": "AskAiCli.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tinteractive: bool,\tspeak: bool,\tdebug: bool,\tcacheable: bool,\ttempo: int,\tquery_prompt: str,\tengine_name: str,\tmodel_name: str,\tquery_string: Union[str, List[str], NoneType])"}, {"fullname": "main.askai.core.askai_cli.AskAiCli.run", "modulename": "main.askai.core.askai_cli", "qualname": "AskAiCli.run", "kind": "function", "doc": "

    Run the application.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_configs", "modulename": "main.askai.core.askai_configs", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.askai_configs\n @file: askai_configs.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs", "kind": "class", "doc": "

    Provides access to AskAI configurations.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.__init__", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.INSTANCE", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.askai_configs.AskAiConfigs"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.RESOURCE_DIR", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.RESOURCE_DIR", "kind": "variable", "doc": "

    \n", "default_value": "'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources'"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.engine", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.engine", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.model", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.model", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.is_interactive", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.is_interactive", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.is_speak", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.is_speak", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.is_debug", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.is_debug", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.is_cache", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.is_cache", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.tempo", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.tempo", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.ttl", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.ttl", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.verbosity", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.verbosity", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.enums.verbosity.Verbosity"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.chunk_size", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.chunk_size", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.chunk_overlap", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.chunk_overlap", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.rag_retrival_amount", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.rag_retrival_amount", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.is_rag", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.is_rag", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.language", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.language", "kind": "variable", "doc": "

    Lookup order: Settings -> Locale -> Environment.

    \n", "annotation": ": askai.language.language.Language"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.encoding", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.encoding", "kind": "variable", "doc": "

    \n", "annotation": ": hspylib.core.enums.charset.Charset"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.max_iteractions", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.max_iteractions", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.max_short_memory_size", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.max_short_memory_size", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.max_router_retries", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.max_router_retries", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.max_agent_execution_time_seconds", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.max_agent_execution_time_seconds", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.face_detect_alg", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.face_detect_alg", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.scale_factor", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.scale_factor", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.min_neighbors", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.min_neighbors", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.min_max_size", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.min_max_size", "kind": "variable", "doc": "

    \n", "annotation": ": tuple[int, ...]"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.max_id_distance", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.max_id_distance", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.recorder_phrase_limit_millis", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.recorder_phrase_limit_millis", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.recorder_silence_timeout_millis", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.recorder_silence_timeout_millis", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.recorder_noise_detection_duration_millis", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.recorder_noise_detection_duration_millis", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.recorder_input_device_auto_swap", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.recorder_input_device_auto_swap", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.recorder_devices", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.recorder_devices", "kind": "variable", "doc": "

    \n", "annotation": ": set[str]"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.add_device", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.add_device", "kind": "function", "doc": "

    Add a new device to the configuration list.

    \n\n
    Parameters
    \n\n
      \n
    • device_name: The device name to be added.
    • \n
    \n", "signature": "(self, device_name: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.remove_device", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.remove_device", "kind": "function", "doc": "

    Remove a new device from the configuration list.

    \n\n
    Parameters
    \n\n
      \n
    • device_name: The device name to be removed.
    • \n
    \n", "signature": "(self, device_name: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_configs.AskAiConfigs.clear_devices", "modulename": "main.askai.core.askai_configs", "qualname": "AskAiConfigs.clear_devices", "kind": "function", "doc": "

    Remove all devices from the configuration list.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_events", "modulename": "main.askai.core.askai_events", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.askai_events\n @file: askai_events.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.askai_events.ASKAI_BUS_NAME", "modulename": "main.askai.core.askai_events", "qualname": "ASKAI_BUS_NAME", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-reply-bus'"}, {"fullname": "main.askai.core.askai_events.REPLY_EVENT", "modulename": "main.askai.core.askai_events", "qualname": "REPLY_EVENT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-reply-event'"}, {"fullname": "main.askai.core.askai_events.MIC_LISTENING_EVENT", "modulename": "main.askai.core.askai_events", "qualname": "MIC_LISTENING_EVENT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-mic-listening-event'"}, {"fullname": "main.askai.core.askai_events.DEVICE_CHANGED_EVENT", "modulename": "main.askai.core.askai_events", "qualname": "DEVICE_CHANGED_EVENT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-input-device-changed-event'"}, {"fullname": "main.askai.core.askai_events.MODE_CHANGED_EVENT", "modulename": "main.askai.core.askai_events", "qualname": "MODE_CHANGED_EVENT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-routing-mode-changed-event'"}, {"fullname": "main.askai.core.askai_events.AskAiEvents", "modulename": "main.askai.core.askai_events", "qualname": "AskAiEvents", "kind": "class", "doc": "

    Facility class to provide easy access to AskAI events.

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.askai_events.AskAiEvents.ASKAI_BUS", "modulename": "main.askai.core.askai_events", "qualname": "AskAiEvents.ASKAI_BUS", "kind": "variable", "doc": "

    \n", "default_value": "ASKAI_BUS"}, {"fullname": "main.askai.core.askai_events.AskAiEvents.bus", "modulename": "main.askai.core.askai_events", "qualname": "AskAiEvents.bus", "kind": "function", "doc": "

    Return an event bus instance for the given name.

    \n\n
    Parameters
    \n\n
      \n
    • bus_name: The name of the event bus to retrieve.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of FluidEventBus if found; otherwise, None.

    \n
    \n", "signature": "(bus_name: str) -> hspylib.modules.eventbus.fluid.FluidEventBus:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_events.AskAiEvents.events", "modulename": "main.askai.core.askai_events", "qualname": "AskAiEvents.events", "kind": "variable", "doc": "

    \n", "annotation": ": hspylib.core.namespace.Namespace"}, {"fullname": "main.askai.core.askai_events.events", "modulename": "main.askai.core.askai_events", "qualname": "events", "kind": "variable", "doc": "

    \n", "annotation": ": hspylib.core.namespace.Namespace", "default_value": "FluidEventBus::askai-reply-bus(reply=FluidEvent-askai-reply-event::(name, erase_last, cb_event_handler, emit, subscribe), listening=FluidEvent-askai-mic-listening-event::(name, listening, cb_event_handler, emit, subscribe), device_changed=FluidEvent-askai-input-device-changed-event::(name, device, cb_event_handler, emit, subscribe), mode_changed=FluidEvent-askai-routing-mode-changed-event::(name, mode, sum_path, glob, cb_event_handler, emit, subscribe))"}, {"fullname": "main.askai.core.askai_messages", "modulename": "main.askai.core.askai_messages", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.askai_messages\n @file: askai_messages.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages", "kind": "class", "doc": "

    Provide access to static 'translated' messages.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.__init__", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.INSTANCE", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.askai_messages.AskAiMessages"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.TRANSLATOR", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.TRANSLATOR", "kind": "variable", "doc": "

    \n", "annotation": ": askai.language.ai_translator.AITranslator", "default_value": "<class 'askai.language.translators.deepl_translator.DeepLTranslator'>"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.get_translator", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.get_translator", "kind": "function", "doc": "

    \n", "signature": "(\tfrom_lang: askai.language.language.Language,\tto_lang: askai.language.language.Language) -> askai.language.ai_translator.AITranslator:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.accurate_responses", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.accurate_responses", "kind": "variable", "doc": "

    \n", "annotation": ": list[str]"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.translator", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.translator", "kind": "variable", "doc": "

    \n", "annotation": ": askai.language.ai_translator.AITranslator"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.translate", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.translate", "kind": "function", "doc": "

    Translate text using the configured language.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to be translated.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The translated text.

    \n
    \n", "signature": "(self, text: ~AnyStr) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.welcome", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.welcome", "kind": "function", "doc": "

    \n", "signature": "(self, username: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.wait", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.wait", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.welcome_back", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.welcome_back", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.listening", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.listening", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.transcribing", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.transcribing", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.goodbye", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.goodbye", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.smile", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.smile", "kind": "function", "doc": "

    \n", "signature": "(self, countdown: int) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.cmd_success", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.cmd_success", "kind": "function", "doc": "

    \n", "signature": "(self, command_line: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.searching", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.searching", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.scrapping", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.scrapping", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.summarizing", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.summarizing", "kind": "function", "doc": "

    \n", "signature": "(self, path: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.summary_succeeded", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.summary_succeeded", "kind": "function", "doc": "

    \n", "signature": "(self, path: str, glob: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.enter_qna", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.enter_qna", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.leave_qna", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.leave_qna", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.leave_rag", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.leave_rag", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.qna_welcome", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.qna_welcome", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.press_esc_enter", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.press_esc_enter", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.device_switch", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.device_switch", "kind": "function", "doc": "

    \n", "signature": "(self, device_info: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.photo_captured", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.photo_captured", "kind": "function", "doc": "

    \n", "signature": "(self, photo: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.screenshot_saved", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.screenshot_saved", "kind": "function", "doc": "

    \n", "signature": "(self, screenshot: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.executing", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.executing", "kind": "function", "doc": "

    \n", "signature": "(self, command_line: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.analysis", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.analysis", "kind": "function", "doc": "

    \n", "signature": "(self, result: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.assert_acc", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.assert_acc", "kind": "function", "doc": "

    \n", "signature": "(self, status: str, details: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.action_plan", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.action_plan", "kind": "function", "doc": "

    \n", "signature": "(self, plan_text: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.x_reference", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.x_reference", "kind": "function", "doc": "

    \n", "signature": "(self, pathname: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.describe_image", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.describe_image", "kind": "function", "doc": "

    \n", "signature": "(self, image_path: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.model_select", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.model_select", "kind": "function", "doc": "

    \n", "signature": "(self, model: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.task", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.task", "kind": "function", "doc": "

    \n", "signature": "(self, task: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.final_query", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.final_query", "kind": "function", "doc": "

    \n", "signature": "(self, query: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.refine_answer", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.refine_answer", "kind": "function", "doc": "

    \n", "signature": "(self, answer: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.no_caption", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.no_caption", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.no_good_result", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.no_good_result", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.no_output", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.no_output", "kind": "function", "doc": "

    \n", "signature": "(self, source: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.access_grant", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.access_grant", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.no_query_string", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.no_query_string", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.invalid_response", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.invalid_response", "kind": "function", "doc": "

    \n", "signature": "(self, response_text: ~AnyStr) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.invalid_command", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.invalid_command", "kind": "function", "doc": "

    \n", "signature": "(self, response_text: ~AnyStr) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.cmd_no_exist", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.cmd_no_exist", "kind": "function", "doc": "

    \n", "signature": "(self, command: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.cmd_failed", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.cmd_failed", "kind": "function", "doc": "

    \n", "signature": "(self, cmd_line: str, error_msg: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.camera_not_open", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.camera_not_open", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.missing_package", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.missing_package", "kind": "function", "doc": "

    \n", "signature": "(self, err: ImportError) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.summary_not_possible", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.summary_not_possible", "kind": "function", "doc": "

    \n", "signature": "(self, err: BaseException = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.intelligible", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.intelligible", "kind": "function", "doc": "

    \n", "signature": "(self, reason: ~AnyStr) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.impossible", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.impossible", "kind": "function", "doc": "

    \n", "signature": "(self, reason: ~AnyStr) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.timeout", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.timeout", "kind": "function", "doc": "

    \n", "signature": "(self, reason: ~AnyStr) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.llm_error", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.llm_error", "kind": "function", "doc": "

    \n", "signature": "(self, error: ~AnyStr) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.fail_to_search", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.fail_to_search", "kind": "function", "doc": "

    \n", "signature": "(self, error: ~AnyStr) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.too_many_actions", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.too_many_actions", "kind": "function", "doc": "

    \n", "signature": "(self) -> ~AnyStr:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.unprocessable", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.unprocessable", "kind": "function", "doc": "

    \n", "signature": "(self, reason: ~AnyStr) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.quote_exceeded", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.quote_exceeded", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_messages.AskAiMessages.interruption_requested", "modulename": "main.askai.core.askai_messages", "qualname": "AskAiMessages.interruption_requested", "kind": "function", "doc": "

    \n", "signature": "(self, reason: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_prompt", "modulename": "main.askai.core.askai_prompt", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.askai_prompt\n @file: askai_prompt.py\n@created: Mon, 22 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt", "kind": "class", "doc": "

    Provide the prompts used by the AskAi.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.__init__", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.INSTANCE", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.askai_prompt.AskAiPrompt"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.PROMPT_DIR", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.PROMPT_DIR", "kind": "variable", "doc": "

    \n", "default_value": "'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources/prompts'"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.os_type", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.os_type", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[Literal['linux', 'windows', 'darwin']]"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.shell", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.shell", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[Literal['bash', 'csh', 'dash', 'ksh', 'tcsh', 'zsh', 'sh']]"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.user", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.user", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.idiom", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.idiom", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.read_prompt", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.read_prompt", "kind": "function", "doc": "

    Read a processor prompt template.

    \n\n
    Parameters
    \n\n
      \n
    • template_file: The name of the template file to read.
    • \n
    • prompt_dir: Optional directory where the template file is located.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The content of the prompt template as a string.

    \n
    \n", "signature": "(self, template_file: str, prompt_dir: str = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_prompt.AskAiPrompt.append_path", "modulename": "main.askai.core.askai_prompt", "qualname": "AskAiPrompt.append_path", "kind": "function", "doc": "

    Return the PROMPT_DIR with the extra path appended.

    \n\n
    Parameters
    \n\n
      \n
    • path: The path to append to PROMPT_DIR.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The concatenated path.

    \n
    \n", "signature": "(self, path: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings", "modulename": "main.askai.core.askai_settings", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.support\n @file: askai_settings.py\n@created: Tue, 23 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.askai_settings.ASKAI_DIR", "modulename": "main.askai.core.askai_settings", "qualname": "ASKAI_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/askai')"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings", "kind": "class", "doc": "

    The AskAI 'SetMan' Settings.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.__init__", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.INSTANCE", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.askai_settings.AskAiSettings"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.settings", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.settings", "kind": "variable", "doc": "

    \n", "annotation": ": setman.settings.settings.Settings"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.search", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.search", "kind": "function", "doc": "

    Search settings using the specified filters.

    \n\n
    Parameters
    \n\n
      \n
    • filters: Optional filters to apply to the search.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The search results as a string, or None if no results are found.

    \n
    \n", "signature": "(self, filters: str | None = None) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.defaults", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.defaults", "kind": "function", "doc": "

    Create the default settings database.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.get", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.get", "kind": "function", "doc": "

    Retrieve the setting specified by the given key.

    \n\n
    Parameters
    \n\n
      \n
    • key: The name of the setting to retrieve.
    • \n
    • default_value: The value to return if the setting does not exist.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The setting value if it exists, otherwise the default_value.

    \n
    \n", "signature": "(self, key: str, default_value: str | None = '') -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.put", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.put", "kind": "function", "doc": "

    Set the setting specified by the given key.

    \n\n
    Parameters
    \n\n
      \n
    • key: The name of the setting to update.
    • \n
    • value: The value to associate with the key.
    • \n
    \n", "signature": "(self, key: str, value: Any) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.get_bool", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.get_bool", "kind": "function", "doc": "

    Retrieve the setting specified by the given key, converting it to boolean.

    \n\n
    Parameters
    \n\n
      \n
    • key: The name of the setting to retrieve.
    • \n
    • default_value: The value to return if the setting does not exist.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The setting value if it exists, otherwise the default_value.

    \n
    \n", "signature": "(self, key: str, default_value: bool | None = False) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.get_int", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.get_int", "kind": "function", "doc": "

    Retrieve the setting specified by the given key, converting it to integer.

    \n\n
    Parameters
    \n\n
      \n
    • key: The name of the setting to retrieve.
    • \n
    • default_value: The value to return if the setting does not exist.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The setting value if it exists, otherwise the default_value.

    \n
    \n", "signature": "(self, key: str, default_value: int | None = 0) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.get_float", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.get_float", "kind": "function", "doc": "

    Retrieve the setting specified by the given key, converting it to float.

    \n\n
    Parameters
    \n\n
      \n
    • key: The name of the setting to retrieve.
    • \n
    • default_value: The value to return if the setting does not exist.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The setting value if it exists, otherwise the default_value.

    \n
    \n", "signature": "(self, key: str, default_value: float | None = 0.0) -> float:", "funcdef": "def"}, {"fullname": "main.askai.core.askai_settings.AskAiSettings.get_list", "modulename": "main.askai.core.askai_settings", "qualname": "AskAiSettings.get_list", "kind": "function", "doc": "

    Retrieve the setting specified by the given key, converting it to list.

    \n\n
    Parameters
    \n\n
      \n
    • key: The name of the setting to retrieve.
    • \n
    • default_value: The value to return if the setting does not exist.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The setting value if it exists, otherwise the default_value.

    \n
    \n", "signature": "(self, key: str, default_value: list | None = None) -> list:", "funcdef": "def"}, {"fullname": "main.askai.core.commander", "modulename": "main.askai.core.commander", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.commander.commander", "modulename": "main.askai.core.commander.commander", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.commander.commander\n @file: commander.py\n@created: Thu, 25 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.commander.commander.COMMANDER_HELP_TPL", "modulename": "main.askai.core.commander.commander", "qualname": "COMMANDER_HELP_TPL", "kind": "variable", "doc": "

    \n", "default_value": "<string.Template object>"}, {"fullname": "main.askai.core.commander.commander.COMMANDER_HELP_CMD_TPL", "modulename": "main.askai.core.commander.commander", "qualname": "COMMANDER_HELP_CMD_TPL", "kind": "variable", "doc": "

    \n", "default_value": "<string.Template object>"}, {"fullname": "main.askai.core.commander.commander.RE_ASKAI_CMD", "modulename": "main.askai.core.commander.commander", "qualname": "RE_ASKAI_CMD", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'^(?<!\\\\\\\\)/(\\\\w+)( (.*))*$'"}, {"fullname": "main.askai.core.commander.commander.commands", "modulename": "main.askai.core.commander.commander", "qualname": "commands", "kind": "function", "doc": "

    Return the list of all available commander commands.

    \n\n
    Returns
    \n\n
    \n

    A list of strings representing the available commands.

    \n
    \n", "signature": "() -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commander.commander_help", "modulename": "main.askai.core.commander.commander", "qualname": "commander_help", "kind": "function", "doc": "

    Return the help string for the specified commander command.

    \n\n
    Parameters
    \n\n
      \n
    • command: The command for which to retrieve help.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A string containing the help information for the specified command.

    \n
    \n", "signature": "(command: str | None = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commander.is_command", "modulename": "main.askai.core.commander.commander", "qualname": "is_command", "kind": "function", "doc": "

    Check if the given string is a command.

    \n\n
    Parameters
    \n\n
      \n
    • string: The string to check.
    • \n
    \n\n
    Returns
    \n\n
    \n

    True if the string is a command, False otherwise.

    \n
    \n", "signature": "(string: str) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commander.ask_commander", "modulename": "main.askai.core.commander.commander", "qualname": "ask_commander", "kind": "variable", "doc": "

    AskAI commands group. This function serves as the entry point for the AskAI command-line interface (CLI)\ncommands, grouping related commands together.

    \n", "default_value": "<Group ask-commander>"}, {"fullname": "main.askai.core.commander.commander.help", "modulename": "main.askai.core.commander.commander", "qualname": "help", "kind": "variable", "doc": "

    Display the help message for the specified command and exit. If no command is provided, it displays the general\nhelp message.

    \n\n
    Parameters
    \n\n
      \n
    • command: The command to retrieve help for (optional).
    • \n
    \n", "default_value": "<Command help>"}, {"fullname": "main.askai.core.commander.commander.debug", "modulename": "main.askai.core.commander.commander", "qualname": "debug", "kind": "variable", "doc": "

    Toggle debug mode ON/OFF.

    \n", "default_value": "<Command debug>"}, {"fullname": "main.askai.core.commander.commander.speak", "modulename": "main.askai.core.commander.commander", "qualname": "speak", "kind": "variable", "doc": "

    Toggle speak mode ON/OFF.

    \n", "default_value": "<Command speak>"}, {"fullname": "main.askai.core.commander.commander.context", "modulename": "main.askai.core.commander.commander", "qualname": "context", "kind": "variable", "doc": "

    Manage the current chat context window.

    \n\n
    Parameters
    \n\n
      \n
    • operation: The operation to perform on contexts. Options: [list | forget].
    • \n
    • name: The name of the context to target (default is \"ALL\").
    • \n
    \n", "default_value": "<Command context>"}, {"fullname": "main.askai.core.commander.commander.history", "modulename": "main.askai.core.commander.commander", "qualname": "history", "kind": "variable", "doc": "

    Manages the current input history.

    \n\n
    Parameters
    \n\n
      \n
    • operation: The operation to perform on contexts. Options: [list|forget].
    • \n
    \n", "default_value": "<Command history>"}, {"fullname": "main.askai.core.commander.commander.copy", "modulename": "main.askai.core.commander.commander", "qualname": "copy", "kind": "variable", "doc": "

    Copy the specified context entry to the clipboard.

    \n\n
    Parameters
    \n\n
      \n
    • name: The name of the context entry to copy.
    • \n
    \n", "default_value": "<Command copy>"}, {"fullname": "main.askai.core.commander.commander.devices", "modulename": "main.askai.core.commander.commander", "qualname": "devices", "kind": "variable", "doc": "

    Manages the audio input devices.

    \n\n
    Parameters
    \n\n
      \n
    • operation: Specifies the device operation. Options: [list|set].
    • \n
    • name: The target device name for setting.
    • \n
    \n", "default_value": "<Command devices>"}, {"fullname": "main.askai.core.commander.commander.settings", "modulename": "main.askai.core.commander.commander", "qualname": "settings", "kind": "variable", "doc": "

    Handles modifications to AskAI settings.

    \n\n
    Parameters
    \n\n
      \n
    • operation: The action to perform on settings. Options: [list|get|set|reset]
    • \n
    • name: The key for the setting to modify.
    • \n
    • value: The new value for the specified setting.
    • \n
    \n", "default_value": "<Command settings>"}, {"fullname": "main.askai.core.commander.commander.cache", "modulename": "main.askai.core.commander.commander", "qualname": "cache", "kind": "variable", "doc": "

    Manages AskAI TTL-cache management and associated files.

    \n\n
    Parameters
    \n\n
      \n
    • operation: Specifies the cache operation. Options: [list|get|clear|files|enable|ttl]
    • \n
    • args: Arguments relevant to the chosen operation.
    • \n
    \n", "default_value": "<Command cache>"}, {"fullname": "main.askai.core.commander.commander.tempo", "modulename": "main.askai.core.commander.commander", "qualname": "tempo", "kind": "variable", "doc": "

    Manages the speech-to-text tempo.

    \n\n
    Parameters
    \n\n
      \n
    • speed: Desired speech tempo setting. Options: [list|get]
    • \n
    \n", "default_value": "<Command tempo>"}, {"fullname": "main.askai.core.commander.commander.voices", "modulename": "main.askai.core.commander.commander", "qualname": "voices", "kind": "variable", "doc": "

    Manages speech-to-text voice operations.

    \n\n
    Parameters
    \n\n
      \n
    • operation: The action to perform on voices. Options: [list/set/play]
    • \n
    • name: The voice name.
    • \n
    \n", "default_value": "<Command voices>"}, {"fullname": "main.askai.core.commander.commander.tts", "modulename": "main.askai.core.commander.commander", "qualname": "tts", "kind": "variable", "doc": "

    Convert text to speech using the default AI engine.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to convert. If text represents a valid file, its contents will be used instead.
    • \n
    • dest_dir: The directory where the converted audio file will be saved.
    • \n
    • playback: Whether to play the audio file after conversion.
    • \n
    \n", "default_value": "<Command tts>"}, {"fullname": "main.askai.core.commander.commander.summarize", "modulename": "main.askai.core.commander.commander", "qualname": "summarize", "kind": "variable", "doc": "

    Create a summary of the folder's contents.

    \n\n
    Parameters
    \n\n
      \n
    • folder: The root directory for the summary.
    • \n
    • glob: The file pattern or path to summarize.
    • \n
    \n", "default_value": "<Command summarize>"}, {"fullname": "main.askai.core.commander.commander.idiom", "modulename": "main.askai.core.commander.commander", "qualname": "idiom", "kind": "variable", "doc": "

    Set the application's language preference.

    \n\n
    Parameters
    \n\n
      \n
    • locale_str: The locale identifier, e.g., 'pt_BR'.
    • \n
    \n", "default_value": "<Command idiom>"}, {"fullname": "main.askai.core.commander.commander.info", "modulename": "main.askai.core.commander.commander", "qualname": "info", "kind": "variable", "doc": "

    Display key information about the running application.

    \n", "default_value": "<Command info>"}, {"fullname": "main.askai.core.commander.commander.translate", "modulename": "main.askai.core.commander.commander", "qualname": "translate", "kind": "variable", "doc": "

    Translate text from the source language to the target language.

    \n\n
    Parameters
    \n\n
      \n
    • from_locale_str: The source locale identifier, e.g., 'pt_BR'.
    • \n
    • to_locale_str: The target locale identifier, e.g., 'en_US'.
    • \n
    • texts: The list of texts to translate.
    • \n
    \n", "default_value": "<Command translate>"}, {"fullname": "main.askai.core.commander.commander.camera", "modulename": "main.askai.core.commander.commander", "qualname": "camera", "kind": "variable", "doc": "

    Take photos, import images, or identify a person using the WebCam.

    \n\n
    Parameters
    \n\n
      \n
    • operation: The camera operation to perform. Options: [capture|identify|import]
    • \n
    • args: The arguments required for the operation.
    • \n
    \n", "default_value": "<Command camera>"}, {"fullname": "main.askai.core.commander.commands", "modulename": "main.askai.core.commander.commands", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.commander.commands.cache_cmd", "modulename": "main.askai.core.commander.commands.cache_cmd", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.commander.cache_cmd\n @file: cache_cmd.py\n@created: Thu, 27 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.commander.commands.cache_cmd.CacheCmd", "modulename": "main.askai.core.commander.commands.cache_cmd", "qualname": "CacheCmd", "kind": "class", "doc": "

    Provides cache command functionalities.

    \n", "bases": "abc.ABC"}, {"fullname": "main.askai.core.commander.commands.cache_cmd.CacheCmd.list", "modulename": "main.askai.core.commander.commands.cache_cmd", "qualname": "CacheCmd.list", "kind": "function", "doc": "

    List all cache entries.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.cache_cmd.CacheCmd.get", "modulename": "main.askai.core.commander.commands.cache_cmd", "qualname": "CacheCmd.get", "kind": "function", "doc": "

    Retrieve a cache entry by its name.

    \n\n
    Parameters
    \n\n
      \n
    • name: The name of the cache entry to retrieve.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The value of the cache entry as a string, or None if the entry does not exist.

    \n
    \n", "signature": "(name: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.cache_cmd.CacheCmd.clear", "modulename": "main.askai.core.commander.commands.cache_cmd", "qualname": "CacheCmd.clear", "kind": "function", "doc": "

    Clear a specified cache entry or all cache entries.

    \n\n
    Parameters
    \n\n
      \n
    • entry: The cache entry to clear, specified by name or index. If None, all cache entries will be cleared.
    • \n
    \n", "signature": "(entry: str | int | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.cache_cmd.CacheCmd.files", "modulename": "main.askai.core.commander.commands.cache_cmd", "qualname": "CacheCmd.files", "kind": "function", "doc": "

    List all cached files from the AskAI cache directory.

    \n\n
    Parameters
    \n\n
      \n
    • cleanup: If True, clean up the listed cache files after displaying them (default is False).
    • \n
    • args: Specific file names or indices to target for listing or cleanup.
    • \n
    \n", "signature": "(cleanup: bool = False, *args: str | int) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.camera_cmd", "modulename": "main.askai.core.commander.commands.camera_cmd", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.commander.commands.camera_cmd.CameraCmd", "modulename": "main.askai.core.commander.commands.camera_cmd", "qualname": "CameraCmd", "kind": "class", "doc": "

    Provides camera command functionalities.

    \n", "bases": "abc.ABC"}, {"fullname": "main.askai.core.commander.commands.camera_cmd.CameraCmd.capture", "modulename": "main.askai.core.commander.commands.camera_cmd", "qualname": "CameraCmd.capture", "kind": "function", "doc": "

    Take a photo using the webcam.

    \n\n
    Parameters
    \n\n
      \n
    • filename: The filename to save the photo under (optional).
    • \n
    • detect_faces: Whether to detect faces in the photo (default is True).
    • \n
    \n", "signature": "(\tfilename: Union[pathlib.Path, str, NoneType] = None,\tdetect_faces: bool = True) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.camera_cmd.CameraCmd.identify", "modulename": "main.askai.core.commander.commands.camera_cmd", "qualname": "CameraCmd.identify", "kind": "function", "doc": "

    Identify the person in front of the webcam.

    \n\n
    Parameters
    \n\n
      \n
    • max_distance: The maximum allowable distance for face recognition. A lower value means closer matching\nto the real face (default is configs.max_id_distance).
    • \n
    \n", "signature": "(max_distance: int = 0.7) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.camera_cmd.CameraCmd.import_images", "modulename": "main.askai.core.commander.commands.camera_cmd", "qualname": "CameraCmd.import_images", "kind": "function", "doc": "

    Import image files into the image collection.

    \n\n
    Parameters
    \n\n
      \n
    • pathname: The pathname or glob pattern specifying the images to be imported (optional).
    • \n
    • detect_faces: Whether to detect faces in the imported images (default is True).
    • \n
    \n", "signature": "(\tpathname: Union[pathlib.Path, str, NoneType] = None,\tdetect_faces: bool = True) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.general_cmd", "modulename": "main.askai.core.commander.commands.general_cmd", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.commander.general_cmd\n @file: general_cmd.py\n@created: Mon, 06 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.commander.commands.general_cmd.GeneralCmd", "modulename": "main.askai.core.commander.commands.general_cmd", "qualname": "GeneralCmd", "kind": "class", "doc": "

    Provides general command functionalities.

    \n", "bases": "abc.ABC"}, {"fullname": "main.askai.core.commander.commands.general_cmd.GeneralCmd.execute", "modulename": "main.askai.core.commander.commands.general_cmd", "qualname": "GeneralCmd.execute", "kind": "function", "doc": "

    Execute a terminal command.

    \n\n
    Parameters
    \n\n
      \n
    • cmd_line: The command line to execute (optional).
    • \n
    \n", "signature": "(cmd_line: str | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.general_cmd.GeneralCmd.summarize", "modulename": "main.askai.core.commander.commands.general_cmd", "qualname": "GeneralCmd.summarize", "kind": "function", "doc": "

    Generate a summarization of files and folder contents.

    \n\n
    Parameters
    \n\n
      \n
    • folder: The base folder from which the summarization will be generated.
    • \n
    • glob: The glob pattern specifying which files or folders to include in the summarization.
    • \n
    \n", "signature": "(folder: str, glob: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.general_cmd.GeneralCmd.idiom", "modulename": "main.askai.core.commander.commands.general_cmd", "qualname": "GeneralCmd.idiom", "kind": "function", "doc": "

    Set the application language.

    \n\n
    Parameters
    \n\n
      \n
    • locale_str: The locale string.
    • \n
    \n", "signature": "(locale_str: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.general_cmd.GeneralCmd.app_info", "modulename": "main.askai.core.commander.commands.general_cmd", "qualname": "GeneralCmd.app_info", "kind": "function", "doc": "

    Display some useful application information.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.general_cmd.GeneralCmd.translate", "modulename": "main.askai.core.commander.commands.general_cmd", "qualname": "GeneralCmd.translate", "kind": "function", "doc": "

    Translate text from the source language to the target language.

    \n\n
    Parameters
    \n\n
      \n
    • from_lang: The source language.
    • \n
    • to_lang: The target language.
    • \n
    • texts: The texts to be translated.
    • \n
    \n", "signature": "(\tfrom_lang: askai.language.language.Language,\tto_lang: askai.language.language.Language,\t*texts: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.history_cmd", "modulename": "main.askai.core.commander.commands.history_cmd", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.commander.history_cmd\n @file: general_cmd.py\n@created: Sat, 22 Jun 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.commander.commands.history_cmd.HistoryCmd", "modulename": "main.askai.core.commander.commands.history_cmd", "qualname": "HistoryCmd", "kind": "class", "doc": "

    Provides history command functionalities.

    \n", "bases": "abc.ABC"}, {"fullname": "main.askai.core.commander.commands.history_cmd.HistoryCmd.context_list", "modulename": "main.askai.core.commander.commands.history_cmd", "qualname": "HistoryCmd.context_list", "kind": "function", "doc": "

    List the entries in the chat context window.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.history_cmd.HistoryCmd.context_forget", "modulename": "main.askai.core.commander.commands.history_cmd", "qualname": "HistoryCmd.context_forget", "kind": "function", "doc": "

    Forget entries pushed to the chat context.

    \n\n
    Parameters
    \n\n
      \n
    • context: The context key to forget, or None to forget all context entries.
    • \n
    \n", "signature": "(context: str | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.history_cmd.HistoryCmd.context_copy", "modulename": "main.askai.core.commander.commands.history_cmd", "qualname": "HistoryCmd.context_copy", "kind": "function", "doc": "

    Copy a context entry to the clipboard.

    \n\n
    Parameters
    \n\n
      \n
    • name: The name of the context entry to copy. If None, the default context will be copied.
    • \n
    \n", "signature": "(name: str | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.history_cmd.HistoryCmd.history_list", "modulename": "main.askai.core.commander.commands.history_cmd", "qualname": "HistoryCmd.history_list", "kind": "function", "doc": "

    List the input history entries.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.history_cmd.HistoryCmd.history_forget", "modulename": "main.askai.core.commander.commands.history_cmd", "qualname": "HistoryCmd.history_forget", "kind": "function", "doc": "

    Forget entries pushed to the history.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.settings_cmd", "modulename": "main.askai.core.commander.commands.settings_cmd", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.commander.settings_cmd\n @file: settings_cmd.py\n@created: Thu, 25 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.commander.commands.settings_cmd.SettingsCmd", "modulename": "main.askai.core.commander.commands.settings_cmd", "qualname": "SettingsCmd", "kind": "class", "doc": "

    Provides settings manipulation command functionalities.

    \n", "bases": "abc.ABC"}, {"fullname": "main.askai.core.commander.commands.settings_cmd.SettingsCmd.list", "modulename": "main.askai.core.commander.commands.settings_cmd", "qualname": "SettingsCmd.list", "kind": "function", "doc": "

    List all settings, optionally matching filters.

    \n", "signature": "(filters: str | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.settings_cmd.SettingsCmd.set", "modulename": "main.askai.core.commander.commands.settings_cmd", "qualname": "SettingsCmd.set", "kind": "function", "doc": "

    Set a setting value.

    \n\n
    Parameters
    \n\n
      \n
    • name: The name of the setting to update or create.
    • \n
    • value: The value to assign to the setting.
    • \n
    \n", "signature": "(name: str, value: Any) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.settings_cmd.SettingsCmd.get", "modulename": "main.askai.core.commander.commands.settings_cmd", "qualname": "SettingsCmd.get", "kind": "function", "doc": "

    Retrieve the setting specified by the key.

    \n\n
    Parameters
    \n\n
      \n
    • key: The key of the setting to retrieve.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The corresponding SettingsEntry if found, otherwise None.

    \n
    \n", "signature": "(key: str) -> Optional[setman.settings.settings_entry.SettingsEntry]:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.settings_cmd.SettingsCmd.reset", "modulename": "main.askai.core.commander.commands.settings_cmd", "qualname": "SettingsCmd.reset", "kind": "function", "doc": "

    Reset all settings to their default values.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.commander.tts_stt_cmd\n @file: tts_stt_cmd.py\n@created: Thu, 25 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd", "kind": "class", "doc": "

    Provides Speech-to-Text (STT) and Text-to-Speech (TTS) command functionalities.

    \n", "bases": "abc.ABC"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd.voice_list", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd.voice_list", "kind": "function", "doc": "

    List all available voices for the current engine/model.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd.voice_set", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd.voice_set", "kind": "function", "doc": "

    Set the specified voice for the current engine.

    \n\n
    Parameters
    \n\n
      \n
    • name_or_index: The name or index of the voice to set. If None, the default voice will be used.
    • \n
    \n", "signature": "(name_or_index: str | int | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd.voice_play", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd.voice_play", "kind": "function", "doc": "

    Play a sample using the specified voice.

    \n\n
    Parameters
    \n\n
      \n
    • name_or_index: The name or index of the voice to use for the sample. If None, the default voice will be used.
    • \n
    \n", "signature": "(name_or_index: str | int | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd.tempo", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd.tempo", "kind": "function", "doc": "

    Set the playing speed of the speech.

    \n\n
    Parameters
    \n\n
      \n
    • speed: The speed to set for speech playback. If None, the default speed will be used.
    • \n
    \n", "signature": "(speed: int | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd.device_list", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd.device_list", "kind": "function", "doc": "

    List the available audio input devices.

    \n", "signature": "() -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd.device_set", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd.device_set", "kind": "function", "doc": "

    Set the current audio input device.

    \n\n
    Parameters
    \n\n
      \n
    • name_or_index: The name or index of the audio input device to set. If None, the default device will be\nused.
    • \n
    \n", "signature": "(name_or_index: str | int | None = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.commander.commands.tts_stt_cmd.TtsSttCmd.tts", "modulename": "main.askai.core.commander.commands.tts_stt_cmd", "qualname": "TtsSttCmd.tts", "kind": "function", "doc": "

    Convert text to speech and optionally play it back.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to be converted to speech.
    • \n
    • dest_dir: The directory where the audio file will be saved (default is the current working directory).
    • \n
    • playback: Whether to play back the generated speech after conversion (default is True).
    • \n
    \n", "signature": "(\ttext: str,\tdest_dir: str = '/Users/hjunior/GIT-Repository/GitHub/askai',\tplayback: bool = True) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component", "modulename": "main.askai.core.component", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.component.audio_player", "modulename": "main.askai.core.component.audio_player", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.component\n @file: audio_player.py\n@created: Wed, 22 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer", "kind": "class", "doc": "

    Provide an interface for playing audio through the default speaker device. This class allows for the playback of\naudio files using the system's default audio output device.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer.__init__", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer.INSTANCE", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.audio_player.AudioPlayer"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer.SFX_DIR", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer.SFX_DIR", "kind": "variable", "doc": "

    \n", "default_value": "'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources/sound-fx'"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer.play_audio_file", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer.play_audio_file", "kind": "function", "doc": "

    Play the specified audio file using the ffplay (ffmpeg) application.

    \n\n
    Parameters
    \n\n
      \n
    • path_to_audio_file: The path to the audio file (e.g., MP3) to be played.
    • \n
    • tempo: The playback speed (default is 1).
    • \n
    \n\n
    Returns
    \n\n
    \n

    True if the audio file is played successfully, otherwise False.

    \n
    \n", "signature": "(path_to_audio_file: str | pathlib.Path, tempo: int = 1) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer.start_delay", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer.start_delay", "kind": "function", "doc": "

    Determine the amount of delay before start streaming the text.

    \n", "signature": "(self) -> float:", "funcdef": "def"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer.audio_length", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer.audio_length", "kind": "function", "doc": "

    Determine the length of an audio file using ffmpeg.

    \n\n
    Parameters
    \n\n
      \n
    • path_to_audio_file: The path to the audio file whose length is to be determined.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The length of the audio file in seconds as a float.

    \n
    \n", "signature": "(self, path_to_audio_file: str) -> float:", "funcdef": "def"}, {"fullname": "main.askai.core.component.audio_player.AudioPlayer.play_sfx", "modulename": "main.askai.core.component.audio_player", "qualname": "AudioPlayer.play_sfx", "kind": "function", "doc": "

    Play a sound effect audio file.

    \n\n
    Parameters
    \n\n
      \n
    • filename: The name of the sound effect file (without the extension).
    • \n
    • file_ext: The file extension of the sound effect (default is \".mp3\").
    • \n
    \n\n
    Returns
    \n\n
    \n

    True if the sound effect is played successfully, otherwise False.

    \n
    \n", "signature": "(\tself,\tfilename: str,\tfile_ext: Literal['.mp3', '.wav', '.m4a'] = '.mp3') -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service", "modulename": "main.askai.core.component.cache_service", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.component\n @file: cache_service.py\n@created: Tue, 16 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.cache_service.CACHE_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "CACHE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache')"}, {"fullname": "main.askai.core.component.cache_service.SETTINGS_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "SETTINGS_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/settings')"}, {"fullname": "main.askai.core.component.cache_service.AUDIO_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "AUDIO_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/audio')"}, {"fullname": "main.askai.core.component.cache_service.PICTURE_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "PICTURE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/pictures')"}, {"fullname": "main.askai.core.component.cache_service.PHOTO_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "PHOTO_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/pictures/photos')"}, {"fullname": "main.askai.core.component.cache_service.FACE_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "FACE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/pictures/faces')"}, {"fullname": "main.askai.core.component.cache_service.IMG_IMPORTS_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "IMG_IMPORTS_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/pictures/imports')"}, {"fullname": "main.askai.core.component.cache_service.GEN_AI_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "GEN_AI_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/generated')"}, {"fullname": "main.askai.core.component.cache_service.REC_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "REC_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/recordings')"}, {"fullname": "main.askai.core.component.cache_service.PERSIST_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "PERSIST_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/chroma')"}, {"fullname": "main.askai.core.component.cache_service.RAG_DIR", "modulename": "main.askai.core.component.cache_service", "qualname": "RAG_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/.config/hhs/askai/cache/rag')"}, {"fullname": "main.askai.core.component.cache_service.CacheEntry", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheEntry", "kind": "class", "doc": "

    CacheEntry(key, expires)

    \n", "bases": "builtins.tuple"}, {"fullname": "main.askai.core.component.cache_service.CacheEntry.__init__", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheEntry.__init__", "kind": "function", "doc": "

    Create new instance of CacheEntry(key, expires)

    \n", "signature": "(key, expires)"}, {"fullname": "main.askai.core.component.cache_service.CacheEntry.key", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheEntry.key", "kind": "variable", "doc": "

    Alias for field number 0

    \n"}, {"fullname": "main.askai.core.component.cache_service.CacheEntry.expires", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheEntry.expires", "kind": "variable", "doc": "

    Alias for field number 1

    \n"}, {"fullname": "main.askai.core.component.cache_service.CacheService", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService", "kind": "class", "doc": "

    Provide a cache service for previously used queries, audio generation, and other recurring operations. This\nclass is designed to store and retrieve cached data efficiently, reducing the need for repeated processing and\nenhancing performance.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.cache_service.CacheService.__init__", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.cache_service.CacheService.INSTANCE", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.cache_service.CacheService"}, {"fullname": "main.askai.core.component.cache_service.CacheService.ASKAI_CACHE_KEYS", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.ASKAI_CACHE_KEYS", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-cache-keys'"}, {"fullname": "main.askai.core.component.cache_service.CacheService.ASKAI_INPUT_CACHE_KEY", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.ASKAI_INPUT_CACHE_KEY", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-input-history'"}, {"fullname": "main.askai.core.component.cache_service.CacheService.ASKAI_CONTEXT_KEY", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.ASKAI_CONTEXT_KEY", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'askai-context-key'"}, {"fullname": "main.askai.core.component.cache_service.CacheService.keys", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.keys", "kind": "variable", "doc": "

    \n", "annotation": ": set[str]"}, {"fullname": "main.askai.core.component.cache_service.CacheService.audio_file_path", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.audio_file_path", "kind": "function", "doc": "

    Retrieve the hashed audio file path and determine whether the file already exists.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text that the audio represents.
    • \n
    • voice: The AI voice used for speech synthesis (default is \"onyx\").
    • \n
    • audio_format: The audio file format (default is \"mp3\").
    • \n
    \n\n
    Returns
    \n\n
    \n

    A tuple containing the hashed file path as a string and a boolean indicating if the file exists.

    \n
    \n", "signature": "(\tself,\ttext: str,\tvoice: str = 'onyx',\taudio_format: str = 'mp3') -> tuple[str, bool]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.save_reply", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.save_reply", "kind": "function", "doc": "

    Save an AI reply into the TTL (Time-To-Live) cache.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to be cached.
    • \n
    • reply: The AI reply associated with this text.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The key under which the reply is saved, or None if the save operation fails.

    \n
    \n", "signature": "(self, text: str, reply: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.read_reply", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.read_reply", "kind": "function", "doc": "

    Retrieve AI replies from the TTL (Time-To-Live) cache.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text key to look up in the cache.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The cached reply associated with the text, or None if not found.

    \n
    \n", "signature": "(self, text: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.del_reply", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.del_reply", "kind": "function", "doc": "

    Delete an AI reply from the TTL (Time-To-Live) cache.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text key whose associated reply is to be deleted from the cache.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The deleted reply if it existed, or None if no reply was found.

    \n
    \n", "signature": "(self, text: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.clear_replies", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.clear_replies", "kind": "function", "doc": "

    Clear all cached replies.

    \n\n
    Returns
    \n\n
    \n

    A list of keys for the replies that were deleted from the cache.

    \n
    \n", "signature": "(self) -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.read_input_history", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.read_input_history", "kind": "function", "doc": "

    Retrieve line input queries from the TTL (Time-To-Live) cache.

    \n\n
    Returns
    \n\n
    \n

    A list of input queries stored in the cache.

    \n
    \n", "signature": "(self) -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.save_input_history", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.save_input_history", "kind": "function", "doc": "

    Save input queries into the TTL (Time-To-Live) cache.

    \n\n
    Parameters
    \n\n
      \n
    • history: A list of input queries to be saved. If None, the current input history will be saved.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The temporary file name of the saved entry.

    \n
    \n", "signature": "(self, history: list[str] = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.load_input_history", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.load_input_history", "kind": "function", "doc": "

    Load input queries from the TTL (Time-To-Live) cache extending it with a predefined input history.

    \n\n
    Parameters
    \n\n
      \n
    • predefined: A list of predefined input queries to be appended to the final list.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A list of input queries loaded from the cache.

    \n
    \n", "signature": "(self, predefined: list[str] = None) -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.save_context", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.save_context", "kind": "function", "doc": "

    Save the context window entries into the TTL (Time-To-Live) cache.

    \n\n
    Parameters
    \n\n
      \n
    • context: A list of context entries to be saved.
    • \n
    \n", "signature": "(self, context: list[str]) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.cache_service.CacheService.read_context", "modulename": "main.askai.core.component.cache_service", "qualname": "CacheService.read_context", "kind": "function", "doc": "

    Read the context window entries from the TTL (Time-To-Live) cache.

    \n\n
    Returns
    \n\n
    \n

    A list of context entries retrieved from the cache.

    \n
    \n", "signature": "(self) -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.camera", "modulename": "main.askai.core.component.camera", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.components\n @file: recorder.py\n@created: Wed, 22 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.camera.InputDevice", "modulename": "main.askai.core.component.camera", "qualname": "InputDevice", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "tuple[int, str]"}, {"fullname": "main.askai.core.component.camera.Camera", "modulename": "main.askai.core.component.camera", "qualname": "Camera", "kind": "class", "doc": "

    Provide an interface to interact with the webcam. This class offers methods for controlling and accessing the\nwebcam's functionality, ensuring that only one instance interacts with the hardware at a time.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.camera.Camera.__init__", "modulename": "main.askai.core.component.camera", "qualname": "Camera.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.camera.Camera.INSTANCE", "modulename": "main.askai.core.component.camera", "qualname": "Camera.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.camera.Camera"}, {"fullname": "main.askai.core.component.camera.Camera.RESOURCE_DIR", "modulename": "main.askai.core.component.camera", "qualname": "Camera.RESOURCE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources')"}, {"fullname": "main.askai.core.component.camera.Camera.ALG", "modulename": "main.askai.core.component.camera", "qualname": "Camera.ALG", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'haarcascade_frontalface_default.xml'"}, {"fullname": "main.askai.core.component.camera.Camera.initialize", "modulename": "main.askai.core.component.camera", "qualname": "Camera.initialize", "kind": "function", "doc": "

    Initialize the camera device.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.camera.Camera.shutdown", "modulename": "main.askai.core.component.camera", "qualname": "Camera.shutdown", "kind": "function", "doc": "

    Shutdown the camera device.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.camera.Camera.capture", "modulename": "main.askai.core.component.camera", "qualname": "Camera.capture", "kind": "function", "doc": "

    Capture a webcam frame (take a photo).

    \n\n
    Parameters
    \n\n
      \n
    • filename: The file name for the capturing image.
    • \n
    • countdown: The number of seconds for the countdown before capturing the photo (default is 3).
    • \n
    • with_caption: Whether to generate a caption for the captured image (default is True).
    • \n
    • store_image: Whether to save the captured image to the image store (default is True).
    • \n
    \n\n
    Returns
    \n\n
    \n

    A tuple containing the image file and image data, or None if the capture fails.

    \n
    \n", "signature": "(\tself,\tfilename: Union[pathlib.Path, str, NoneType],\tcountdown: int = 3,\twith_caption: bool = True,\tstore_image: bool = True) -> Optional[tuple[askai.core.component.image_store.ImageFile, numpy.ndarray[Any, numpy.dtype]]]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.camera.Camera.detect_faces", "modulename": "main.askai.core.component.camera", "qualname": "Camera.detect_faces", "kind": "function", "doc": "

    Detect all faces in the provided photo.

    \n\n
    Parameters
    \n\n
      \n
    • photo: The image data in which to detect faces.
    • \n
    • filename: The file name for the detected face image.(optional).
    • \n
    • with_caption: Whether to generate captions for the detected faces (default is True).
    • \n
    • store_image: Whether to save the processed images to the image store (default is True).
    • \n
    \n\n
    Returns
    \n\n
    \n

    A tuple containing a list of image files and a list of image data for the detected faces.

    \n
    \n", "signature": "(\tself,\tphoto: numpy.ndarray[typing.Any, numpy.dtype],\tfilename: Union[pathlib.Path, str, NoneType] = None,\twith_caption: bool = True,\tstore_image: bool = True) -> tuple[list[askai.core.component.image_store.ImageFile], list[numpy.ndarray[typing.Any, numpy.dtype]]]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.camera.Camera.identify", "modulename": "main.askai.core.component.camera", "qualname": "Camera.identify", "kind": "function", "doc": "

    Identify the person in front of the webcam.

    \n\n
    Parameters
    \n\n
      \n
    • countdown: The number of seconds for the countdown before capturing an identification the photo\n(default is 0).
    • \n
    • max_distance: The maximum allowable distance for face recognition accuracy (default is\nconfigs.max_id_distance).
    • \n
    \n\n
    Returns
    \n\n
    \n

    Metadata about the identified person, or None if identification fails.

    \n
    \n", "signature": "(\tself,\tcountdown: int = 0,\tmax_distance: float = 0.7) -> Optional[askai.core.component.image_store.ImageMetadata]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.camera.Camera.import_images", "modulename": "main.askai.core.component.camera", "qualname": "Camera.import_images", "kind": "function", "doc": "

    Import image files into the image collection.

    \n\n
    Parameters
    \n\n
      \n
    • pathname: The path or glob pattern of the images to be imported.
    • \n
    • detect_faces: Whether to detect faces in the imported images (default is False).
    • \n
    • with_caption: Whether to generate captions for the imported images (default is True).
    • \n
    • store_image: Whether to save the processed images to the image store (default is True).
    • \n
    \n\n
    Returns
    \n\n
    \n

    A tuple containing the number of images successfully imported, and the number of detected faces.

    \n
    \n", "signature": "(\tself,\tpathname: Union[pathlib.Path, str, NoneType],\tdetect_faces: bool = False,\twith_caption: bool = True,\tstore_image: bool = True) -> tuple[int, ...]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.geo_location", "modulename": "main.askai.core.component.geo_location", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.component\n @file: geo_location.py\n@created: Tue, 23 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation", "kind": "class", "doc": "

    A class for managing and retrieving geographic location data.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.__init__", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.INSTANCE", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.geo_location.GeoLocation"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.GEO_LOC_URL", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.GEO_LOC_URL", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'http://ip-api.com/json'"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.EMPTY_JSON_RESP", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.EMPTY_JSON_RESP", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'{\\n "status": "failure", "country": "", "countryCode": "", "region": "", "regionName": "",\\n "city": "", "zip": "", "lat": 0.0, "lon": 0.0, "timezone": "",\\n "isp": "", "org": "", "as": "", "query": ""\\n}'"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.DATE_FMT", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.DATE_FMT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'%a %d %b %-H:%M %Y'"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.get_location", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.get_location", "kind": "function", "doc": "

    Retrieve the geographic location based on the provided IP address.

    \n\n
    Parameters
    \n\n
      \n
    • ip: The IP address to locate. If None, the current device's IP address will be used.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A Namespace object containing the geolocation data, such as latitude, longitude, city, and country.

    \n
    \n", "signature": "(cls, ip: str = None) -> hspylib.core.namespace.Namespace:", "funcdef": "def"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.latitude", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.latitude", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.longitude", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.longitude", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.country", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.country", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.country_code", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.country_code", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.region", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.region", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.region_name", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.region_name", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.city", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.city", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.zip", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.zip", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.timezone", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.timezone", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.location", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.location", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.geo_location.GeoLocation.datetime", "modulename": "main.askai.core.component.geo_location", "qualname": "GeoLocation.datetime", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.image_store", "modulename": "main.askai.core.component.image_store", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.components\n @file: recorder.py\n@created: Wed, 22 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.image_store.Metadata", "modulename": "main.askai.core.component.image_store", "qualname": "Metadata", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "Mapping[str, str | int | float | bool]"}, {"fullname": "main.askai.core.component.image_store.ImageData", "modulename": "main.askai.core.component.image_store", "qualname": "ImageData", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "numpy.ndarray[typing.Any, numpy.dtype]"}, {"fullname": "main.askai.core.component.image_store.ImageFile", "modulename": "main.askai.core.component.image_store", "qualname": "ImageFile", "kind": "class", "doc": "

    ImageFile(img_id, img_path, img_category, img_caption)

    \n", "bases": "builtins.tuple"}, {"fullname": "main.askai.core.component.image_store.ImageFile.__init__", "modulename": "main.askai.core.component.image_store", "qualname": "ImageFile.__init__", "kind": "function", "doc": "

    Create new instance of ImageFile(img_id, img_path, img_category, img_caption)

    \n", "signature": "(img_id, img_path, img_category, img_caption)"}, {"fullname": "main.askai.core.component.image_store.ImageFile.img_id", "modulename": "main.askai.core.component.image_store", "qualname": "ImageFile.img_id", "kind": "variable", "doc": "

    Alias for field number 0

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageFile.img_path", "modulename": "main.askai.core.component.image_store", "qualname": "ImageFile.img_path", "kind": "variable", "doc": "

    Alias for field number 1

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageFile.img_category", "modulename": "main.askai.core.component.image_store", "qualname": "ImageFile.img_category", "kind": "variable", "doc": "

    Alias for field number 2

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageFile.img_caption", "modulename": "main.askai.core.component.image_store", "qualname": "ImageFile.img_caption", "kind": "variable", "doc": "

    Alias for field number 3

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageMetadata", "modulename": "main.askai.core.component.image_store", "qualname": "ImageMetadata", "kind": "class", "doc": "

    ImageMetadata(caption, data, uri, distance)

    \n", "bases": "builtins.tuple"}, {"fullname": "main.askai.core.component.image_store.ImageMetadata.__init__", "modulename": "main.askai.core.component.image_store", "qualname": "ImageMetadata.__init__", "kind": "function", "doc": "

    Create new instance of ImageMetadata(caption, data, uri, distance)

    \n", "signature": "(caption, data, uri, distance)"}, {"fullname": "main.askai.core.component.image_store.ImageMetadata.caption", "modulename": "main.askai.core.component.image_store", "qualname": "ImageMetadata.caption", "kind": "variable", "doc": "

    Alias for field number 0

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageMetadata.data", "modulename": "main.askai.core.component.image_store", "qualname": "ImageMetadata.data", "kind": "variable", "doc": "

    Alias for field number 1

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageMetadata.uri", "modulename": "main.askai.core.component.image_store", "qualname": "ImageMetadata.uri", "kind": "variable", "doc": "

    Alias for field number 2

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageMetadata.distance", "modulename": "main.askai.core.component.image_store", "qualname": "ImageMetadata.distance", "kind": "variable", "doc": "

    Alias for field number 3

    \n"}, {"fullname": "main.askai.core.component.image_store.ImageStore", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore", "kind": "class", "doc": "

    Provide an interface to store, retrieve, locate, and vectorize images. This class manages the storage and\nretrieval of images, as well as their localization and vectorization for various applications.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.image_store.ImageStore.__init__", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.image_store.ImageStore.INSTANCE", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.image_store.ImageStore"}, {"fullname": "main.askai.core.component.image_store.ImageStore.COLLECTION_NAME", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.COLLECTION_NAME", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'image_store'"}, {"fullname": "main.askai.core.component.image_store.ImageStore.PHOTO_CATEGORY", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.PHOTO_CATEGORY", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'photos'"}, {"fullname": "main.askai.core.component.image_store.ImageStore.FACE_CATEGORY", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.FACE_CATEGORY", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'faces'"}, {"fullname": "main.askai.core.component.image_store.ImageStore.IMPORTS_CATEGORY", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.IMPORTS_CATEGORY", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'imports'"}, {"fullname": "main.askai.core.component.image_store.ImageStore.sync_folders", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.sync_folders", "kind": "function", "doc": "

    Load image files from the specified directories.

    \n", "signature": "(\twith_caption: bool = False,\t*dirs: Union[pathlib.Path, str, NoneType]) -> list[main.askai.core.component.image_store.ImageFile]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.persist_dir", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.persist_dir", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path"}, {"fullname": "main.askai.core.component.image_store.ImageStore.metadatas", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.metadatas", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[list[Mapping[str, str | int | float | bool]]]"}, {"fullname": "main.askai.core.component.image_store.ImageStore.enlist", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.enlist", "kind": "function", "doc": "

    \n", "signature": "(self) -> Optional[list[str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.store_image", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.store_image", "kind": "function", "doc": "

    Add the provided images to the image store collection.

    \n\n
    Parameters
    \n\n
      \n
    • image_files: One or more ImageFile objects representing the images to be stored.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The number of images successfully added to the store.

    \n
    \n", "signature": "(\tself,\t*image_files: main.askai.core.component.image_store.ImageFile) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.clear_store", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.clear_store", "kind": "function", "doc": "

    Clear the image store collection.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.sync_store", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.sync_store", "kind": "function", "doc": "

    Synchronize the image store collection with the cached pictures folder.

    \n\n
    Parameters
    \n\n
      \n
    • re_caption: Whether to regenerate captions for the images during synchronization (default is False).
    • \n
    \n\n
    Returns
    \n\n
    \n

    The number of images synchronized with the store.

    \n
    \n", "signature": "(self, re_caption: bool = False) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.query_image", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.query_image", "kind": "function", "doc": "

    Query the image store for photos matching the provided description.

    \n\n
    Parameters
    \n\n
      \n
    • description: A text description to match against the stored images.
    • \n
    • k: The maximum number of matching results to return (default is 3).
    • \n
    \n\n
    Returns
    \n\n
    \n

    A list of ImageMetadata objects for the photos that match the description.

    \n
    \n", "signature": "(\tself,\tdescription: str,\tk: int = 3) -> list[main.askai.core.component.image_store.ImageMetadata]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.query_face", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.query_face", "kind": "function", "doc": "

    Query the image store for faces matching the provided description.

    \n\n
    Parameters
    \n\n
      \n
    • description: A text description to match against the stored faces.
    • \n
    • k: The maximum number of matching faces to return (default is 1).
    • \n
    \n\n
    Returns
    \n\n
    \n

    A list of ImageMetadata objects for the faces that match the description.

    \n
    \n", "signature": "(\tself,\tdescription: str,\tk: int = 3) -> list[main.askai.core.component.image_store.ImageMetadata]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.find_by_description", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.find_by_description", "kind": "function", "doc": "

    Find images using natural language.

    \n\n
    Parameters
    \n\n
      \n
    • description: A natural language description to match against stored images.
    • \n
    • categories: A list of categories to limit the search within.
    • \n
    • k: The maximum number of matching images to return (default is 3).
    • \n
    \n\n
    Returns
    \n\n
    \n

    A list of ImageMetadata objects for the images that match the description and categories.

    \n
    \n", "signature": "(\tself,\tdescription: str,\tcategories: list[str],\tk: int = 3) -> list[main.askai.core.component.image_store.ImageMetadata]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.image_store.ImageStore.find_by_similarity", "modulename": "main.askai.core.component.image_store", "qualname": "ImageStore.find_by_similarity", "kind": "function", "doc": "

    Find images that match the provided photo using similarity methods.

    \n\n
    Parameters
    \n\n
      \n
    • photo: The ImageData object representing the photo to match against stored faces.
    • \n
    • k: The maximum number of matching faces to return (default is 3).
    • \n
    \n\n
    Returns
    \n\n
    \n

    A list of ImageMetadata objects for the faces that match the provided photo.

    \n
    \n", "signature": "(\tself,\tphoto: numpy.ndarray[typing.Any, numpy.dtype],\tk: int = 3) -> list[main.askai.core.component.image_store.ImageMetadata]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.internet_service", "modulename": "main.askai.core.component.internet_service", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.component\n @file: internet_service.py\n@created: Sun, 10 Mar 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.internet_service.InternetService", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService", "kind": "class", "doc": "

    Provide an internet search service to complete queries that require real-time data. This service allows for the\nretrieval of up-to-date information from the web, enabling queries that depend on current data.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.internet_service.InternetService.__init__", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.internet_service.InternetService.INSTANCE", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.internet_service.InternetService"}, {"fullname": "main.askai.core.component.internet_service.InternetService.ASKAI_INTERNET_DATA_KEY", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.ASKAI_INTERNET_DATA_KEY", "kind": "variable", "doc": "

    \n", "default_value": "'askai-internet-data'"}, {"fullname": "main.askai.core.component.internet_service.InternetService.refine_template", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.refine_template", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.internet_service.InternetService.google_search", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.google_search", "kind": "function", "doc": "

    Search the web using the Google Search API. This method utilizes advanced Google search operators to refine\nand execute the search.\nReference: https://ahrefs.com/blog/google-advanced-search-operators/

    \n\n
    Parameters
    \n\n
      \n
    • search: The AI search parameters encapsulated in a SearchResult object.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A refined string containing the search results.

    \n
    \n", "signature": "(self, search: askai.core.model.search_result.SearchResult) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.component.internet_service.InternetService.scrap_sites", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.scrap_sites", "kind": "function", "doc": "

    Scrape a web page and summarize its contents.

    \n\n
    Parameters
    \n\n
      \n
    • search: The AI search parameters encapsulated in a SearchResult object.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A string containing the summarized contents of the scraped web page.

    \n
    \n", "signature": "(self, search: askai.core.model.search_result.SearchResult) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.component.internet_service.InternetService.refine_search", "modulename": "main.askai.core.component.internet_service", "qualname": "InternetService.refine_search", "kind": "function", "doc": "

    Refine the text retrieved by the search engine.

    \n\n
    Parameters
    \n\n
      \n
    • question: The user's question, used to refine and contextualize the search results.
    • \n
    • result: The raw search result text that needs refinement.
    • \n
    • terms: The search terms used in the Google search.
    • \n
    • sites: The list of source sites that were included in the search.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A refined version of the search result text, tailored to better answer the user's question.

    \n
    \n", "signature": "(self, question: str, result: str, terms: str, sites: list[str]) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.component.recorder", "modulename": "main.askai.core.component.recorder", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.components\n @file: recorder.py\n@created: Wed, 22 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.recorder.InputDevice", "modulename": "main.askai.core.component.recorder", "qualname": "InputDevice", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "tuple[int, str]"}, {"fullname": "main.askai.core.component.recorder.Recorder", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder", "kind": "class", "doc": "

    Provide an interface to record voice using the microphone device. This class offers methods to capture and\nmanage audio recordings, ensuring that only one instance interacts with the microphone at a time.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.recorder.Recorder.__init__", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.recorder.Recorder.INSTANCE", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.recorder.Recorder"}, {"fullname": "main.askai.core.component.recorder.Recorder.RecognitionApi", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.RecognitionApi", "kind": "class", "doc": "

    Available voice recognition APIs.

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.component.recorder.Recorder.RecognitionApi.OPEN_AI", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.RecognitionApi.OPEN_AI", "kind": "variable", "doc": "

    \n", "default_value": "OPEN_AI"}, {"fullname": "main.askai.core.component.recorder.Recorder.RecognitionApi.GOOGLE", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.RecognitionApi.GOOGLE", "kind": "variable", "doc": "

    \n", "default_value": "GOOGLE"}, {"fullname": "main.askai.core.component.recorder.Recorder.get_device_list", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.get_device_list", "kind": "function", "doc": "

    Return a list of available audio input devices.

    \n\n
    Returns
    \n\n
    \n

    A list of InputDevice objects representing the available microphone devices.

    \n
    \n", "signature": "(cls) -> list[tuple[int, str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.recorder.Recorder.setup", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.setup", "kind": "function", "doc": "

    Setup the microphone recorder.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.recorder.Recorder.devices", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.devices", "kind": "variable", "doc": "

    \n", "annotation": ": list[tuple[int, str]]"}, {"fullname": "main.askai.core.component.recorder.Recorder.input_device", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.input_device", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[tuple[int, str]]"}, {"fullname": "main.askai.core.component.recorder.Recorder.device_index", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.device_index", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[int]"}, {"fullname": "main.askai.core.component.recorder.Recorder.is_auto_swap", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.is_auto_swap", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.component.recorder.Recorder.set_device", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.set_device", "kind": "function", "doc": "

    Test and set the specified device as the current audio input device.

    \n\n
    Parameters
    \n\n
      \n
    • device: The audio input device to be set as the current device.
    • \n
    \n\n
    Returns
    \n\n
    \n

    True if the device was successfully set, False otherwise.

    \n
    \n", "signature": "(self, device: tuple[int, str]) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.component.recorder.Recorder.is_headphones", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.is_headphones", "kind": "function", "doc": "

    Check if the currently set audio input device is a headphone. This is a simplistic method of detection, but\nit has been effective in practice.

    \n\n
    Returns
    \n\n
    \n

    True if the device is detected as a headphone, False otherwise.

    \n
    \n", "signature": "(self) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.component.recorder.Recorder.listen", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.listen", "kind": "function", "doc": "

    Listen to the microphone, save the recorded audio as a WAV file, and transcribe the speech.

    \n\n
    Parameters
    \n\n
      \n
    • recognition_api: The API to use for recognizing the speech. Defaults to GOOGLE.
    • \n
    • language: The spoken language. Defaults to EN_US.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A tuple containing the path to the saved audio file and the transcribed text.\n If transcription fails, the second element of the tuple will be None.

    \n
    \n", "signature": "(\tself,\trecognition_api: main.askai.core.component.recorder.Recorder.RecognitionApi = GOOGLE,\tlanguage: askai.language.language.Language = English) -> tuple[pathlib.Path, typing.Optional[str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.recorder.Recorder.test_device", "modulename": "main.askai.core.component.recorder", "qualname": "Recorder.test_device", "kind": "function", "doc": "

    Test whether the input device specified by the given index can be used for speech-to-text (STT) input.

    \n\n
    Parameters
    \n\n
      \n
    • idx: The index of the device to be tested.
    • \n
    \n\n
    Returns
    \n\n
    \n

    True if the device at the specified index is suitable for STT, otherwise False.

    \n
    \n", "signature": "(self, idx: int) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.component.scheduler", "modulename": "main.askai.core.component.scheduler", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.component.scheduler\n @file: scheduler.py\n@created: Thu, 25 Mar 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.scheduler.Scheduler", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler", "kind": "class", "doc": "

    A singleton scheduler class for managing and executing scheduled tasks.\nInherits from:\n - Thread: Allows the scheduler to run tasks in a separate thread.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.__init__", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.INSTANCE", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.scheduler.Scheduler"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.every", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.every", "kind": "function", "doc": "

    Decorator to schedule a function to be run periodically. The decorated function will be executed every\ninterval_ms milliseconds, with an initial delay of delay_ms milliseconds before the first execution.\nCan be used with both instance methods (methods with self) and static or standalone functions.

    \n\n
    Parameters
    \n\n
      \n
    • interval_ms: The interval in milliseconds between consecutive executions of the decorated function.
    • \n
    • delay_ms: The initial delay in milliseconds before the first execution of the decorated function.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The decorated function.

    \n
    \n", "signature": "(interval_ms: int, delay_ms: int = 0):", "funcdef": "def"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.at", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.at", "kind": "function", "doc": "

    Decorator to schedule a function to be run periodically at a specific time each day. This can handle both\ninstance methods (with self) and standalone functions.

    \n\n
    Parameters
    \n\n
      \n
    • hour: The hour of the day (0-23) when the function should run.
    • \n
    • minute: The minute of the hour (0-59) when the function should run.
    • \n
    • second: The second of the minute (0-59) when the function should run.
    • \n
    • millis: The millisecond of the second (0-999) when the function should run.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A decorator that schedules the function to run at the specified time.

    \n
    \n", "signature": "(hour: int, minute: int, second: int, millis: int = 0):", "funcdef": "def"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.after", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.after", "kind": "function", "doc": "

    Decorator to schedule a function to be run after the specified hour, minute, second, and microsecond delay.\nCan be used for both instance methods (with self) and static or standalone functions.

    \n\n
    Parameters
    \n\n
      \n
    • hour: Hours to delay
    • \n
    • minute: Minutes to delay
    • \n
    • second: Seconds to delay
    • \n
    • microsecond: Microseconds to delay
    • \n
    \n\n
    Returns
    \n\n
    \n

    A decorator that schedules the function to run after the specified delay.

    \n
    \n", "signature": "(\thour: int = 0,\tminute: int = 0,\tsecond: int = 0,\tmicrosecond: int = 0):", "funcdef": "def"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.now", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.now", "kind": "variable", "doc": "

    \n", "annotation": ": datetime.datetime"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.alive", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.alive", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.run", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.run", "kind": "function", "doc": "

    Method representing the thread's activity.

    \n\n

    You may override this method in a subclass. The standard run() method\ninvokes the callable object passed to the object's constructor as the\ntarget argument, if any, with sequential and keyword arguments taken\nfrom the args and kwargs arguments, respectively.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.start", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.start", "kind": "function", "doc": "

    Start the thread's activity.

    \n\n

    It must be called at most once per thread object. It arranges for the\nobject's run() method to be invoked in a separate thread of control.

    \n\n

    This method will raise a RuntimeError if called more than once on the\nsame thread object.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.schedule", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.schedule", "kind": "function", "doc": "

    Schedule a task to run at a specific time in the future. This method schedules the provided callback\nfunction to execute at the specified hour, minute, second, and microsecond on the current day. The function\nwill be invoked with the specified arguments and keyword arguments.

    \n\n
    Parameters
    \n\n
      \n
    • hour: The hour of the day (0-23) when the task should run.
    • \n
    • minute: The minute of the hour (0-59) when the task should run.
    • \n
    • second: The second of the minute (0-59) when the task should run.
    • \n
    • microsecond: The microsecond of the second (0-999999) when the task should run.
    • \n
    • callback: The callback function to be executed at the scheduled time.
    • \n
    • cb_fn_args: The positional arguments to pass to the callback function. Defaults to None.
    • \n
    • cb_fn_kwargs: The keyword arguments to pass to the callback function. Defaults to None.
    • \n
    \n", "signature": "(\tself,\thour: int,\tminute: int,\tsecond: int,\tmicrosecond: int,\tcallback: Callable,\tcb_fn_args: Optional[Iterable] = None,\tcb_fn_kwargs: Optional[Mapping[str, Any]] = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.scheduler_after", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.scheduler_after", "kind": "function", "doc": "

    Schedule a function to be run after the specified hour, minute, second, and microsecond.

    \n\n
    Parameters
    \n\n
      \n
    • hh: Hours to delay
    • \n
    • mm: Minutes to delay
    • \n
    • ss: Seconds to delay
    • \n
    • us: microsecond delay
    • \n
    • callback: Function to be executed
    • \n
    • cb_fn_args: Optional arguments to pass to the callback function
    • \n
    • cb_fn_kwargs: Optional keyword arguments to pass to the callback function
    • \n
    \n", "signature": "(\tself,\thh: int,\tmm: int,\tss: int,\tus: int,\tcallback: Callable,\tcb_fn_args: Optional[Iterable] = None,\tcb_fn_kwargs: Optional[Mapping[str, Any]] = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.scheduler.Scheduler.set_interval", "modulename": "main.askai.core.component.scheduler", "qualname": "Scheduler.set_interval", "kind": "function", "doc": "

    Schedule a function to be run periodically at a specified interval (in milliseconds). This method schedules\nthe given callback function to be invoked repeatedly at the specified interval in milliseconds. The function\nwill be initially delayed by the specified amount of milliseconds before the first invocation.

    \n\n
    Parameters
    \n\n
      \n
    • interval_ms: The interval in milliseconds between each invocation of the callback function.
    • \n
    • callback: The callback function to be invoked periodically.
    • \n
    • delay_ms: The initial delay in milliseconds before starting the periodic invocations.
    • \n
    • cb_fn_args: The arguments to pass to the callback function.
    • \n
    • cb_fn_kwargs: The keyword arguments to pass to the callback function.
    • \n
    \n", "signature": "(\tself,\tinterval_ms: int,\tcallback: Callable,\tdelay_ms: int = 0,\tcb_fn_args: Optional[Iterable] = None,\tcb_fn_kwargs: Optional[Mapping[str, Any]] = None) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.component.summarizer", "modulename": "main.askai.core.component.summarizer", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.component\n @file: summarizer.py\n@created: Mon, 11 Mar 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.component.summarizer.Summarizer", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer", "kind": "class", "doc": "

    Provide a summarization service to complete queries that require summarization. This class is designed to\ngenerate concise summaries of text data, which can be used to condense lengthy content into more digestible\ninformation. It leverages natural language processing techniques to extract key points and present them in a\ncoherent manner.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.__init__", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.INSTANCE", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.summarizer.Summarizer"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.extract_result", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.extract_result", "kind": "function", "doc": "

    Extract the question and answer from the summarization result.

    \n\n
    Parameters
    \n\n
      \n
    • result: A dictionary containing the summarization result.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A tuple containing the question and the answer.

    \n
    \n", "signature": "(result: dict) -> tuple[str, str]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.persist_dir", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.persist_dir", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.folder", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.folder", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.glob", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.glob", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.sum_path", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.sum_path", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.text_splitter", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.text_splitter", "kind": "variable", "doc": "

    \n", "annotation": ": langchain_text_splitters.base.TextSplitter"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.retriever", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.retriever", "kind": "variable", "doc": "

    \n", "annotation": ": langchain.chains.retrieval_qa.base.RetrievalQA"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.generate", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.generate", "kind": "function", "doc": "

    Generate a summarization of the contents within a specified folder. This method analyzes files within the\ngiven folder that match the provided glob pattern and creates a summarization of their contents.

    \n\n
    Parameters
    \n\n
      \n
    • folder: The base folder where files are located.
    • \n
    • glob: The glob pattern or specific file name used to filter the files for summarization.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A boolean indicating the success or failure of the summarization process.

    \n
    \n", "signature": "(self, folder: Union[pathlib.Path, str, NoneType], glob: str) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.exists", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.exists", "kind": "function", "doc": "

    Check if a summarization exists for the specified folder and glob pattern. This method determines whether a\nsummarization has been created for the contents of the given folder that match the provided glob pattern.

    \n\n
    Parameters
    \n\n
      \n
    • folder: The base folder where files are located.
    • \n
    • glob: The glob pattern or file name used to filter the files for which the summarization was created.
    • \n
    \n\n
    Returns
    \n\n
    \n

    True if a summarization exists for the given folder and glob pattern; False otherwise.

    \n
    \n", "signature": "(self, folder: Union[pathlib.Path, str, NoneType], glob: str) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.component.summarizer.Summarizer.query", "modulename": "main.askai.core.component.summarizer", "qualname": "Summarizer.query", "kind": "function", "doc": "

    Answer questions about the summarized content.

    \n\n
    Parameters
    \n\n
      \n
    • queries: The list queries to ask the AI engine.
    • \n
    \n", "signature": "(\tself,\t*queries: str) -> Optional[list[askai.core.model.summary_result.SummaryResult]]:", "funcdef": "def"}, {"fullname": "main.askai.core.component.text_streamer", "modulename": "main.askai.core.component.text_streamer", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.component.text_streamer.TextStreamer", "modulename": "main.askai.core.component.text_streamer", "qualname": "TextStreamer", "kind": "class", "doc": "

    Provide a TypeWriter effect helper. This class can sync spoken language with streamed text.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.component.text_streamer.TextStreamer.__init__", "modulename": "main.askai.core.component.text_streamer", "qualname": "TextStreamer.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.component.text_streamer.TextStreamer.INSTANCE", "modulename": "main.askai.core.component.text_streamer", "qualname": "TextStreamer.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.component.text_streamer.TextStreamer"}, {"fullname": "main.askai.core.component.text_streamer.TextStreamer.DONE", "modulename": "main.askai.core.component.text_streamer", "qualname": "TextStreamer.DONE", "kind": "variable", "doc": "

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.core.component.text_streamer.TextStreamer.STREAMING", "modulename": "main.askai.core.component.text_streamer", "qualname": "TextStreamer.STREAMING", "kind": "variable", "doc": "

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.core.component.text_streamer.TextStreamer.STREAMER_THREAD", "modulename": "main.askai.core.component.text_streamer", "qualname": "TextStreamer.STREAMER_THREAD", "kind": "variable", "doc": "

    \n", "annotation": ": threading.Thread", "default_value": "None"}, {"fullname": "main.askai.core.component.text_streamer.TextStreamer.stream_text", "modulename": "main.askai.core.component.text_streamer", "qualname": "TextStreamer.stream_text", "kind": "function", "doc": "

    TODO

    \n", "signature": "(\tself,\ttext: ~AnyStr,\tprefix: ~AnyStr = '',\ttempo: int = 1,\tlanguage: askai.language.language.Language = English) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.engine", "modulename": "main.askai.core.engine", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.engine.ai_engine", "modulename": "main.askai.core.engine.ai_engine", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model\n @file: ai_engine.py\n@created: Fri, 5 May 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine", "kind": "class", "doc": "

    Provide an interface for AI engines.

    \n", "bases": "typing.Protocol"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.__init__", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.__init__", "kind": "function", "doc": "

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.configs", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.configs", "kind": "function", "doc": "

    Return the engine-specific configurations.

    \n", "signature": "(self) -> askai.core.askai_configs.AskAiConfigs:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.nickname", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.nickname", "kind": "function", "doc": "

    Get the AI engine nickname.

    \n\n
    Returns
    \n\n
    \n

    The nickname of the AI engine.

    \n
    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.models", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.models", "kind": "function", "doc": "

    Get the list of available models for the engine.

    \n\n
    Returns
    \n\n
    \n

    A list of available AI models.

    \n
    \n", "signature": "(self) -> list[askai.core.engine.ai_model.AIModel]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.voices", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.voices", "kind": "function", "doc": "

    Return the available model voices for speech to text.

    \n\n
    Returns
    \n\n
    \n

    A list of available voices.

    \n
    \n", "signature": "(self) -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.vision", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.vision", "kind": "function", "doc": "

    Return the engine's vision component.

    \n\n
    Returns
    \n\n
    \n

    The vision component of the engine.

    \n
    \n", "signature": "(self) -> askai.core.engine.ai_vision.AIVision:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.lc_model", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.lc_model", "kind": "function", "doc": "

    Create a LangChain LLM model instance using the current AI engine.

    \n\n
    Parameters
    \n\n
      \n
    • temperature: The LLM model temperature.
    • \n
    • top_p: The model engine top_p.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of BaseLLM.

    \n
    \n", "signature": "(\tself,\ttemperature: float,\ttop_p: float) -> langchain_core.language_models.llms.BaseLLM:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.lc_chat_model", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.lc_chat_model", "kind": "function", "doc": "

    Create a LangChain LLM chat model instance using the current AI engine.

    \n\n
    Parameters
    \n\n
      \n
    • temperature: The LLM chat model temperature.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of BaseChatModel.

    \n
    \n", "signature": "(\tself,\ttemperature: float = 0.0) -> langchain_core.language_models.chat_models.BaseChatModel:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.lc_embeddings", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.lc_embeddings", "kind": "function", "doc": "

    Create a LangChain LLM embeddings model instance.

    \n\n
    Parameters
    \n\n
      \n
    • model: The LLM embeddings model string.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of BaseEmbeddingsModel.

    \n
    \n", "signature": "(self, model: str) -> langchain_core.embeddings.embeddings.Embeddings:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.ai_name", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.ai_name", "kind": "function", "doc": "

    Get the AI engine name.

    \n\n
    Returns
    \n\n
    \n

    The name of the AI engine.

    \n
    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.ai_model_name", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.ai_model_name", "kind": "function", "doc": "

    Get the AI model name.

    \n\n
    Returns
    \n\n
    \n

    The name of the AI model.

    \n
    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.ai_token_limit", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.ai_token_limit", "kind": "function", "doc": "

    Get the AI model token limit.

    \n\n
    Returns
    \n\n
    \n

    The token limit of the AI model.

    \n
    \n", "signature": "(self) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.ask", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.ask", "kind": "function", "doc": "

    Ask AI assistance for the given question and expect a response.

    \n\n
    Parameters
    \n\n
      \n
    • chat_context: The chat history or context.
    • \n
    • temperature: The model engine temperature.
    • \n
    • top_p: The model engine top_p.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The AI's reply.

    \n
    \n", "signature": "(\tself,\tchat_context: list[dict],\ttemperature: float = 0.8,\ttop_p: float = 0.0) -> askai.core.model.ai_reply.AIReply:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.text_to_speech", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.text_to_speech", "kind": "function", "doc": "

    Convert the provided text to speech.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to convert to speech.
    • \n
    • prefix: The prefix of the streamed text.
    • \n
    • stream: Whether to stream the text into stdout.
    • \n
    • playback: Whether to play back the generated audio file.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The path to the generated audio file; or None if no file was generated.

    \n
    \n", "signature": "(\tself,\ttext: str,\tprefix: str = '',\tstream: bool = True,\tplayback: bool = True) -> Optional[pathlib.Path]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.speech_to_text", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.speech_to_text", "kind": "function", "doc": "

    Transcribe audio input from the microphone into text.

    \n\n
    Returns
    \n\n
    \n

    The transcribed text or None if transcription fails.

    \n
    \n", "signature": "(self) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_engine.AIEngine.calculate_tokens", "modulename": "main.askai.core.engine.ai_engine", "qualname": "AIEngine.calculate_tokens", "kind": "function", "doc": "

    Calculate the number of tokens for the given text.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text for which to calculate tokens.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The number of tokens in the text.

    \n
    \n", "signature": "(self, text: str) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_model", "modulename": "main.askai.core.engine.ai_model", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model\n @file: ai_model.py\n@created: Fri, 5 May 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.ai_model.AIModel", "modulename": "main.askai.core.engine.ai_model", "qualname": "AIModel", "kind": "class", "doc": "

    Provide an interface for AI models.

    \n", "bases": "typing.Protocol"}, {"fullname": "main.askai.core.engine.ai_model.AIModel.__init__", "modulename": "main.askai.core.engine.ai_model", "qualname": "AIModel.__init__", "kind": "function", "doc": "

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.engine.ai_model.AIModel.model_name", "modulename": "main.askai.core.engine.ai_model", "qualname": "AIModel.model_name", "kind": "function", "doc": "

    Get the official model's name.

    \n\n
    Returns
    \n\n
    \n

    The name of the model.

    \n
    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_model.AIModel.token_limit", "modulename": "main.askai.core.engine.ai_model", "qualname": "AIModel.token_limit", "kind": "function", "doc": "

    Get the official model's token limit.

    \n\n
    Returns
    \n\n
    \n

    The token limit of the model.

    \n
    \n", "signature": "(self) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.ai_vision", "modulename": "main.askai.core.engine.ai_vision", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model\n @file: ai_vision.py\n@created: Tue, 3 Sep 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.ai_vision.AIVision", "modulename": "main.askai.core.engine.ai_vision", "qualname": "AIVision", "kind": "class", "doc": "

    Provide an interface for AI vision.

    \n", "bases": "typing.Protocol"}, {"fullname": "main.askai.core.engine.ai_vision.AIVision.__init__", "modulename": "main.askai.core.engine.ai_vision", "qualname": "AIVision.__init__", "kind": "function", "doc": "

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.engine.ai_vision.AIVision.caption", "modulename": "main.askai.core.engine.ai_vision", "qualname": "AIVision.caption", "kind": "function", "doc": "

    Generate a caption for the provided image.

    \n\n
    Parameters
    \n\n
      \n
    • filename: File name of the image for which the caption is to be generated.
    • \n
    • load_dir: Optional directory path for loading related resources.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A dictionary containing the generated caption.

    \n
    \n", "signature": "(\tself,\tfilename: Union[pathlib.Path, str, NoneType],\tload_dir: Union[pathlib.Path, str, NoneType] = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.engine_factory", "modulename": "main.askai.core.engine.engine_factory", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.engine\n @file: engine_factory.py\n@created: Tue, 23 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.engine_factory.EngineFactory", "modulename": "main.askai.core.engine.engine_factory", "qualname": "EngineFactory", "kind": "class", "doc": "

    Factory class to create AI engines.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.engine.engine_factory.EngineFactory.__init__", "modulename": "main.askai.core.engine.engine_factory", "qualname": "EngineFactory.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.engine.engine_factory.EngineFactory.INSTANCE", "modulename": "main.askai.core.engine.engine_factory", "qualname": "EngineFactory.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.engine.engine_factory.EngineFactory"}, {"fullname": "main.askai.core.engine.engine_factory.EngineFactory.create_engine", "modulename": "main.askai.core.engine.engine_factory", "qualname": "EngineFactory.create_engine", "kind": "function", "doc": "

    Create the suitable AI engine according to the provided engine name.

    \n\n
    Parameters
    \n\n
      \n
    • engine_name: The AI engine name(s).
    • \n
    • engine_model: The AI engine model(s).
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of AIEngine.

    \n
    \n", "signature": "(\tcls,\tengine_name: str | list[str],\tengine_model: str | list[str]) -> askai.core.engine.ai_engine.AIEngine:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.engine_factory.EngineFactory.active_ai", "modulename": "main.askai.core.engine.engine_factory", "qualname": "EngineFactory.active_ai", "kind": "function", "doc": "

    Get the currently active AI engine.

    \n\n
    Returns
    \n\n
    \n

    The active AI engine.

    \n
    \n\n
    Raises
    \n\n
      \n
    • TypeError: If no AI engine has been created yet.
    • \n
    \n", "signature": "(cls) -> askai.core.engine.ai_engine.AIEngine:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai", "modulename": "main.askai.core.engine.openai", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.engine.openai.openai_configs", "modulename": "main.askai.core.engine.openai.openai_configs", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.engine.openai\n @file: openai_configs.py\n@created: Fri, 12 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs", "kind": "class", "doc": "

    Provides access to OpenAI configurations.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs.__init__", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs.INSTANCE", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.engine.openai.openai_configs.OpenAiConfigs", "default_value": "<askai.core.askai_configs.AskAiConfigs object>"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs.stt_model", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs.stt_model", "kind": "variable", "doc": "

    \n", "annotation": ": Literal['whisper-1']"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs.tts_model", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs.tts_model", "kind": "variable", "doc": "

    \n", "annotation": ": Literal['tts-1', 'tts-1-hd']"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs.tts_voice", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs.tts_voice", "kind": "variable", "doc": "

    \n", "annotation": ": Literal['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer']"}, {"fullname": "main.askai.core.engine.openai.openai_configs.OpenAiConfigs.tts_format", "modulename": "main.askai.core.engine.openai.openai_configs", "qualname": "OpenAiConfigs.tts_format", "kind": "variable", "doc": "

    \n", "annotation": ": Literal['mp3', 'opus', 'aac', 'flac']"}, {"fullname": "main.askai.core.engine.openai.openai_engine", "modulename": "main.askai.core.engine.openai.openai_engine", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.engine.openai\n @file: openai_engine.py\n@created: Fri, 12 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine", "kind": "class", "doc": "

    Provide a base class for OpenAI features. This class implements the AIEngine protocol.

    \n"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.__init__", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.__init__", "kind": "function", "doc": "

    \n", "signature": "(model: askai.core.engine.ai_model.AIModel = GPT_4_O_MINI)"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.url", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.url", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.client", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.client", "kind": "variable", "doc": "

    \n", "annotation": ": openai.OpenAI"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.configs", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.configs", "kind": "function", "doc": "

    Return the engine-specific configurations.

    \n", "signature": "(self) -> askai.core.engine.openai.openai_configs.OpenAiConfigs:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.nickname", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.nickname", "kind": "function", "doc": "

    Get the AI engine nickname.

    \n\n
    Returns
    \n\n
    \n

    The nickname of the AI engine.

    \n
    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.models", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.models", "kind": "function", "doc": "

    Get the list of available models for the engine.

    \n\n
    Returns
    \n\n
    \n

    A list of available AI models.

    \n
    \n", "signature": "(self) -> List[askai.core.engine.ai_model.AIModel]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.voices", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.voices", "kind": "function", "doc": "

    Return the available model voices for speech to text.

    \n\n
    Returns
    \n\n
    \n

    A list of available voices.

    \n
    \n", "signature": "(self) -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.vision", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.vision", "kind": "function", "doc": "

    Return the engine's vision component.

    \n\n
    Returns
    \n\n
    \n

    The vision component of the engine.

    \n
    \n", "signature": "(self) -> askai.core.engine.ai_vision.AIVision:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.lc_model", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.lc_model", "kind": "function", "doc": "

    Create a LangChain LLM model instance using the current AI engine.

    \n\n
    Parameters
    \n\n
      \n
    • temperature: The LLM model temperature.
    • \n
    • top_p: The model engine top_p.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of BaseLLM.

    \n
    \n", "signature": "(\tself,\ttemperature: float,\ttop_p: float) -> langchain_core.language_models.llms.BaseLLM:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.lc_chat_model", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.lc_chat_model", "kind": "function", "doc": "

    Create a LangChain LLM chat model instance using the current AI engine.

    \n\n
    Parameters
    \n\n
      \n
    • temperature: The LLM chat model temperature.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of BaseChatModel.

    \n
    \n", "signature": "(\tself,\ttemperature: float) -> langchain_core.language_models.chat_models.BaseChatModel:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.lc_embeddings", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.lc_embeddings", "kind": "function", "doc": "

    Create a LangChain LLM embeddings model instance.

    \n\n
    Parameters
    \n\n
      \n
    • model: The LLM embeddings model string.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of Embeddings.

    \n
    \n", "signature": "(self, model: str) -> langchain_core.embeddings.embeddings.Embeddings:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.ai_name", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.ai_name", "kind": "function", "doc": "

    Get the AI engine name.

    \n\n
    Returns
    \n\n
    \n

    The name of the AI engine.

    \n
    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.ai_model_name", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.ai_model_name", "kind": "function", "doc": "

    Get the AI model name.

    \n\n
    Returns
    \n\n
    \n

    The name of the AI model.

    \n
    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.ai_token_limit", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.ai_token_limit", "kind": "function", "doc": "

    Get the AI model token limit.

    \n\n
    Returns
    \n\n
    \n

    The token limit of the AI model.

    \n
    \n", "signature": "(self) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.ask", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.ask", "kind": "function", "doc": "

    Ask AI assistance for the given question and expect a response.

    \n\n
    Parameters
    \n\n
      \n
    • chat_context: The chat history or context.
    • \n
    • temperature: The model engine temperature.
    • \n
    • top_p: The model engine top_p.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The AI's reply.

    \n
    \n", "signature": "(\tself,\tchat_context: List[dict],\ttemperature: float = 0.8,\ttop_p: float = 0.0) -> askai.core.model.ai_reply.AIReply:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.text_to_speech", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.text_to_speech", "kind": "function", "doc": "

    Convert the provided text to speech.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to convert to speech.
    • \n
    • prefix: The prefix of the streamed text.
    • \n
    • stream: Whether to stream the text into stdout.
    • \n
    • playback: Whether to play back the generated audio file.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The path to the generated audio file; or None if no file was generated.

    \n
    \n", "signature": "(\tself,\ttext: str,\tprefix: str = '',\tstream: bool = True,\tplayback: bool = True) -> Optional[pathlib.Path]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.speech_to_text", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.speech_to_text", "kind": "function", "doc": "

    Transcribe audio input from the microphone into text.

    \n\n
    Returns
    \n\n
    \n

    The transcribed text or None if transcription fails.

    \n
    \n", "signature": "(self) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_engine.OpenAIEngine.calculate_tokens", "modulename": "main.askai.core.engine.openai.openai_engine", "qualname": "OpenAIEngine.calculate_tokens", "kind": "function", "doc": "

    Calculate the number of tokens for the given text.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text for which to calculate tokens.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The number of tokens in the text.

    \n
    \n", "signature": "(self, text: str) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_model", "modulename": "main.askai.core.engine.openai.openai_model", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.engine.openai\n @file: openai_model.py\n@created: Fri, 12 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel", "kind": "class", "doc": "

    Enumeration for the supported OpenAI models. This class implements the AIModel protocol.\nReference: https://www.pluralsight.com/resources/blog/data/ai-gpt-models-differences

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_3_5_TURBO", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_3_5_TURBO", "kind": "variable", "doc": "

    \n", "default_value": "GPT_3_5_TURBO"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_4", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_4", "kind": "variable", "doc": "

    \n", "default_value": "GPT_4"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_4_TURBO", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_4_TURBO", "kind": "variable", "doc": "

    \n", "default_value": "GPT_4_TURBO"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_4_O", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_4_O", "kind": "variable", "doc": "

    \n", "default_value": "GPT_4_O"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.GPT_4_O_MINI", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.GPT_4_O_MINI", "kind": "variable", "doc": "

    \n", "default_value": "GPT_4_O_MINI"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.O1_PREVIEW", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.O1_PREVIEW", "kind": "variable", "doc": "

    \n", "default_value": "O1_PREVIEW"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.O1_MINI", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.O1_MINI", "kind": "variable", "doc": "

    \n", "default_value": "O1_MINI"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.models", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.models", "kind": "function", "doc": "

    Get the list of available models for the engine.

    \n\n
    Returns
    \n\n
    \n

    A list of available AI models.

    \n
    \n", "signature": "() -> List[askai.core.engine.ai_model.AIModel]:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.of_name", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.of_name", "kind": "function", "doc": "

    Get the AIModel instance corresponding to the given model name.

    \n\n
    Parameters
    \n\n
      \n
    • model_name: The name of the AI model.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The corresponding AIModel instance.

    \n
    \n", "signature": "(model_name: str) -> askai.core.engine.ai_model.AIModel:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.model_name", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.model_name", "kind": "function", "doc": "

    Get the official model's name.

    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_model.OpenAIModel.token_limit", "modulename": "main.askai.core.engine.openai.openai_model", "qualname": "OpenAIModel.token_limit", "kind": "function", "doc": "

    Get the official model tokens limit.

    \n", "signature": "(self) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_vision", "modulename": "main.askai.core.engine.openai.openai_vision", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.engine.openai\n @file: openai_vision.py\n@created: Tue, 5 Sep 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.openai.openai_vision.Base64Image", "modulename": "main.askai.core.engine.openai.openai_vision", "qualname": "Base64Image", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "dict[str, str]"}, {"fullname": "main.askai.core.engine.openai.openai_vision.MessageContent", "modulename": "main.askai.core.engine.openai.openai_vision", "qualname": "MessageContent", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "str | list[str] | dict"}, {"fullname": "main.askai.core.engine.openai.openai_vision.OpenAIVision", "modulename": "main.askai.core.engine.openai.openai_vision", "qualname": "OpenAIVision", "kind": "class", "doc": "

    Provide a base class for OpenAI vision features. This class implements the AIVision protocol.

    \n"}, {"fullname": "main.askai.core.engine.openai.openai_vision.OpenAIVision.create_image_caption_chain", "modulename": "main.askai.core.engine.openai.openai_vision", "qualname": "OpenAIVision.create_image_caption_chain", "kind": "function", "doc": "

    RunnableLambda converts a python callable into a Runnable.

    \n\n

    Wrapping a callable in a RunnableLambda makes the callable usable\nwithin either a sync or async context.

    \n\n

    RunnableLambda can be composed as any other Runnable and provides\nseamless integration with LangChain tracing.

    \n\n

    RunnableLambda is best suited for code that does not need to support\nstreaming. If you need to support streaming (i.e., be able to operate\non chunks of inputs and yield chunks of outputs), use RunnableGenerator\ninstead.

    \n\n

    Note that if a RunnableLambda returns an instance of Runnable, that\ninstance is invoked (or streamed) during execution.

    \n\n

    Examples:

    \n\n
    ```python\n
    \n\n

    This is a RunnableLambda

    \n\n

    from langchain_core.runnables import RunnableLambda

    \n\n

    def add_one(x: int) -> int:\n return x + 1

    \n\n

    runnable = RunnableLambda(add_one)

    \n\n

    runnable.invoke(1) # returns 2\nrunnable.batch([1, 2, 3]) # returns [2, 3, 4]

    \n\n

    Async is supported by default by delegating to the sync implementation

    \n\n

    await runnable.ainvoke(1) # returns 2\nawait runnable.abatch([1, 2, 3]) # returns [2, 3, 4]

    \n\n

    Alternatively, can provide both synd and sync implementations

    \n\n

    async def add_one_async(x: int) -> int:\n return x + 1

    \n\n

    runnable = RunnableLambda(add_one, afunc=add_one_async)\nrunnable.invoke(1) # Uses add_one\nawait runnable.ainvoke(1) # Uses add_one_async\n```

    \n", "signature": "(unknown):", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_vision.OpenAIVision.template", "modulename": "main.askai.core.engine.openai.openai_vision", "qualname": "OpenAIVision.template", "kind": "function", "doc": "

    \n", "signature": "(self, question: str | None = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.openai_vision.OpenAIVision.caption", "modulename": "main.askai.core.engine.openai.openai_vision", "qualname": "OpenAIVision.caption", "kind": "function", "doc": "

    Generate a caption for the provided image.

    \n\n
    Parameters
    \n\n
      \n
    • filename: File name of the image for which the caption is to be generated.
    • \n
    • load_dir: Optional directory path for loading related resources.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A dictionary containing the generated caption.

    \n
    \n", "signature": "(\tself,\tfilename: Union[pathlib.Path, str, NoneType],\tload_dir: Union[pathlib.Path, str, NoneType],\tquestion: str | None = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.engine.openai.temperature", "modulename": "main.askai.core.engine.openai.temperature", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.engine.openai\n @file: temperature.py\n@created: Tue, 23 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature", "kind": "class", "doc": "

    Provides recommended temperature and top_p combinations for ChatGPT prompts. This enumeration is designed to\nhelp users select appropriate temperature settings for different types of prompts, balancing creativity and\nconsistency according to the task at hand.\nReference: https://community.openai.com/t/cheat-sheet-mastering-temperature-and-top-p-in-chatgpt-api/172683

    \n\n
      \n
    • Lower temperature (e.g., 0.1 - 0.4): Produces more focused, conservative, and consistent responses.\nUseful for scenarios requiring factual information, precise answers, or adherence to specific formats or brand\nguidelines.

    • \n
    • Moderate temperature (e.g., 0.5 - 0.7): Strikes a balance between creativity and consistency.\nIdeal for general content generation where a blend of accuracy and inventiveness is desired.

    • \n
    • Higher temperature (e.g., 0.8 - 1.0): Generates more creative, diverse, and unexpected outputs.\nSuitable for brainstorming innovative ideas, crafting engaging social media content, or exploring fresh\nperspectives on a topic.

    • \n
    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.COLDEST", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.COLDEST", "kind": "variable", "doc": "

    \n", "default_value": "COLDEST"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.DATA_ANALYSIS", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.DATA_ANALYSIS", "kind": "variable", "doc": "

    \n", "default_value": "DATA_ANALYSIS"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.CODE_GENERATION", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.CODE_GENERATION", "kind": "variable", "doc": "

    \n", "default_value": "DATA_ANALYSIS"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.CODE_COMMENT_GENERATION", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.CODE_COMMENT_GENERATION", "kind": "variable", "doc": "

    \n", "default_value": "CODE_COMMENT_GENERATION"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.CHATBOT_RESPONSES", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.CHATBOT_RESPONSES", "kind": "variable", "doc": "

    \n", "default_value": "CHATBOT_RESPONSES"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.EXPLORATORY_CODE_WRITING", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.EXPLORATORY_CODE_WRITING", "kind": "variable", "doc": "

    \n", "default_value": "EXPLORATORY_CODE_WRITING"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.CREATIVE_WRITING", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.CREATIVE_WRITING", "kind": "variable", "doc": "

    \n", "default_value": "CREATIVE_WRITING"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.HOTTEST", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.HOTTEST", "kind": "variable", "doc": "

    \n", "default_value": "HOTTEST"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.temp", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.temp", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.engine.openai.temperature.Temperature.top_p", "modulename": "main.askai.core.engine.openai.temperature", "qualname": "Temperature.top_p", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.enums", "modulename": "main.askai.core.enums", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.enums.acc_response", "modulename": "main.askai.core.enums.acc_response", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.enums.acc_response\n @file: acc_response.py\n@created: Tue, 23 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse", "kind": "class", "doc": "

    Track and classify accuracy responses based on color classifications. This class provides an enumeration of\npossible accuracy responses, which are typically represented by different colors.

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.EXCELLENT", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.EXCELLENT", "kind": "variable", "doc": "

    \n", "default_value": "EXCELLENT"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.GOOD", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.GOOD", "kind": "variable", "doc": "

    \n", "default_value": "GOOD"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.MODERATE", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.MODERATE", "kind": "variable", "doc": "

    \n", "default_value": "MODERATE"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.INCOMPLETE", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.INCOMPLETE", "kind": "variable", "doc": "

    \n", "default_value": "INCOMPLETE"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.BAD", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.BAD", "kind": "variable", "doc": "

    \n", "default_value": "BAD"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.INTERRUPT", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.INTERRUPT", "kind": "variable", "doc": "

    \n", "default_value": "INTERRUPT"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.matches", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.matches", "kind": "function", "doc": "

    Find a match in the given output string.

    \n\n
    Parameters
    \n\n
      \n
    • output: The string to search for a match.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A match object if a match is found.

    \n
    \n\n
    Raises
    \n\n
      \n
    • re.error if an error occurs during the matching process.
    • \n
    \n", "signature": "(cls, output: str) -> re.Match:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.strip_code", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.strip_code", "kind": "function", "doc": "

    Strip the color code from the message.

    \n\n
    Parameters
    \n\n
      \n
    • message: The message from which to strip color codes.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The message with color codes removed.

    \n
    \n", "signature": "(cls, message: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.of_status", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.of_status", "kind": "function", "doc": "

    Create an AccResponse instance based on status and optional reasoning.

    \n\n
    Parameters
    \n\n
      \n
    • status: The status as a string.
    • \n
    • reasoning: Optional reasoning for the status, formatted as '%
      '.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of AccResponse with the given status and reasoning.

    \n
    \n", "signature": "(\tcls,\tstatus: str,\treasoning: str | None) -> main.askai.core.enums.acc_response.AccResponse:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.color", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.color", "kind": "variable", "doc": "

    \n"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.reasoning", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.reasoning", "kind": "variable", "doc": "

    \n", "annotation": ": str | None"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.rate", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.rate", "kind": "variable", "doc": "

    \n", "annotation": ": float | None"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.is_bad", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.is_bad", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.is_moderate", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.is_moderate", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.is_good", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.is_good", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.is_interrupt", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.is_interrupt", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.enums.acc_response.AccResponse.passed", "modulename": "main.askai.core.enums.acc_response", "qualname": "AccResponse.passed", "kind": "function", "doc": "

    Determine whether the response matches a 'PASS' classification.

    \n\n
    Parameters
    \n\n
      \n
    • threshold: The threshold or criteria used to determine a 'PASS' classification.
    • \n
    \n\n
    Returns
    \n\n
    \n

    True if the response meets or exceeds the 'PASS' threshold, otherwise False.

    \n
    \n", "signature": "(self, threshold: main.askai.core.enums.acc_response.AccResponse) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.router_mode", "modulename": "main.askai.core.enums.router_mode", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.enums.router_mode\n @file: router_mode.py\n@created: Tue, 24 Jun 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode", "kind": "class", "doc": "

    Enumeration of available router modes used to determine the type of response provided to the user. This class\ndefines the different modes that the router can operate in, each affecting how answers are generated and delivered.

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.TASK_SPLIT", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.TASK_SPLIT", "kind": "variable", "doc": "

    \n", "default_value": "Task Splitter"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.QNA", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.QNA", "kind": "variable", "doc": "

    \n", "default_value": "Questions and Answers"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.QSTRING", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.QSTRING", "kind": "variable", "doc": "

    \n", "default_value": "Non-Interactive"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.RAG", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.RAG", "kind": "variable", "doc": "

    \n", "default_value": "Retrieval-Augmented Generation"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.modes", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.modes", "kind": "function", "doc": "

    Return a list containing all available agent modes.

    \n\n
    Returns
    \n\n
    \n

    A list of available agent modes as strings.

    \n
    \n", "signature": "(cls) -> list[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.default", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.default", "kind": "function", "doc": "

    Return the default routing mode.

    \n\n
    Returns
    \n\n
    \n

    The default RouterMode instance.

    \n
    \n", "signature": "() -> main.askai.core.enums.router_mode.RouterMode:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.of_name", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.of_name", "kind": "function", "doc": "

    Retrieve the RouterMode instance corresponding to the given name.

    \n\n
    Parameters
    \n\n
      \n
    • name: The name of the router mode to retrieve.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The RouterMode instance that matches the given name.

    \n
    \n\n
    Raises
    \n\n
      \n
    • ValueError: If no matching RouterMode is found.
    • \n
    \n", "signature": "(cls, name: str) -> main.askai.core.enums.router_mode.RouterMode:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.name", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.name", "kind": "variable", "doc": "

    The name of the Enum member.

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.processor", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.processor", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.features.processors.ai_processor.AIProcessor"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.is_default", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.is_default", "kind": "variable", "doc": "

    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.enums.router_mode.RouterMode.process", "modulename": "main.askai.core.enums.router_mode", "qualname": "RouterMode.process", "kind": "function", "doc": "

    Invoke the processor associated with the current mode to handle the given question.

    \n\n
    Parameters
    \n\n
      \n
    • question: The question to be processed.
    • \n
    • kwargs: Additional arguments to be passed to the processor.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The processed response as a string, or None if no response is generated.

    \n
    \n", "signature": "(self, question: str, **kwargs) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.routing_model", "modulename": "main.askai.core.enums.routing_model", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.enums.routing_model\n @file: routing_model.py\n@created: Tue, 11 Jun 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel", "kind": "class", "doc": "

    Enumeration representing the model used to provide the final answer to the user.\nThis class defines the different models that can be used in the routing process to generate and deliver the\nfinal response.

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.NEUTRAL", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.NEUTRAL", "kind": "variable", "doc": "

    \n", "default_value": "NEUTRAL::ASK_000"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.ASSISTIVE_TECH_HELPER", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.ASSISTIVE_TECH_HELPER", "kind": "variable", "doc": "

    \n", "default_value": "ASSISTIVE_TECH_HELPER::ASK_001"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.TERMINAL_COMMAND", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.TERMINAL_COMMAND", "kind": "variable", "doc": "

    \n", "default_value": "TERMINAL_COMMAND::ASK_002"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.CONTENT_MASTER", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.CONTENT_MASTER", "kind": "variable", "doc": "

    \n", "default_value": "CONTENT_MASTER::ASK_003"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.TEXT_ANALYZER", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.TEXT_ANALYZER", "kind": "variable", "doc": "

    \n", "default_value": "TEXT_ANALYZER::ASK_004"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.DATA_ANALYSIS", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.DATA_ANALYSIS", "kind": "variable", "doc": "

    \n", "default_value": "DATA_ANALYSIS::ASK_005"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.CHAT_MASTER", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.CHAT_MASTER", "kind": "variable", "doc": "

    \n", "default_value": "CHAT_MASTER::ASK_006"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.MEDIA_MANAGEMENT_AND_PLAYBACK", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.MEDIA_MANAGEMENT_AND_PLAYBACK", "kind": "variable", "doc": "

    \n", "default_value": "MEDIA_MANAGEMENT_AND_PLAYBACK::ASK_007"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.IMAGE_PROCESSOR", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.IMAGE_PROCESSOR", "kind": "variable", "doc": "

    \n", "default_value": "IMAGE_PROCESSOR::ASK_008"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.SUMMARIZE_AND_QUERY", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.SUMMARIZE_AND_QUERY", "kind": "variable", "doc": "

    \n", "default_value": "SUMMARIZE_AND_QUERY::ASK_009"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.WEB_FETCH", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.WEB_FETCH", "kind": "variable", "doc": "

    \n", "default_value": "WEB_FETCH::ASK_010"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.FINAL_ANSWER", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.FINAL_ANSWER", "kind": "variable", "doc": "

    \n", "default_value": "FINAL_ANSWER::ASK_011"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.REFINER", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.REFINER", "kind": "variable", "doc": "

    \n", "default_value": "REFINER::ASK_012"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.of_model", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.of_model", "kind": "function", "doc": "

    Return the RoutingModel instance that matches the given model ID.

    \n\n
    Parameters
    \n\n
      \n
    • model_id: The ID of the model to retrieve.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The RoutingModel instance corresponding to the specified model ID.

    \n
    \n\n
    Raises
    \n\n
      \n
    • ValueError: If no matching RoutingModel is found.
    • \n
    \n", "signature": "(cls, model_id: str) -> main.askai.core.enums.routing_model.RoutingModel:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.enlist", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.enlist", "kind": "function", "doc": "

    Return a list of selectable models as a formatted string.

    \n\n
    Parameters
    \n\n
      \n
    • separator: The separator used to delimit the models in the list (default is the system's line separator).
    • \n
    \n\n
    Returns
    \n\n
    \n

    A string containing the list of selectable models, separated by the specified separator.

    \n
    \n", "signature": "(cls, separator: str = '\\n') -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.model", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.model", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.enums.routing_model.RoutingModel.description", "modulename": "main.askai.core.enums.routing_model", "qualname": "RoutingModel.description", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.enums.verbosity", "modulename": "main.askai.core.enums.verbosity", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.enums.verbosity.Verbosity", "modulename": "main.askai.core.enums.verbosity", "qualname": "Verbosity", "kind": "class", "doc": "

    Represents different verbosity levels for logging or output.\nAttributes:\n MINIMUM (int): The minimum verbosity level.\n LOW (int): A low verbosity level.\n NORMAL (int): The normal verbosity level.\n DETAILED (int): A detailed verbosity level.\n FULL (int): The full verbosity level.

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.enums.verbosity.Verbosity.MINIMUM", "modulename": "main.askai.core.enums.verbosity", "qualname": "Verbosity.MINIMUM", "kind": "variable", "doc": "

    \n", "default_value": "MINIMUM"}, {"fullname": "main.askai.core.enums.verbosity.Verbosity.LOW", "modulename": "main.askai.core.enums.verbosity", "qualname": "Verbosity.LOW", "kind": "variable", "doc": "

    \n", "default_value": "LOW"}, {"fullname": "main.askai.core.enums.verbosity.Verbosity.NORMAL", "modulename": "main.askai.core.enums.verbosity", "qualname": "Verbosity.NORMAL", "kind": "variable", "doc": "

    \n", "default_value": "NORMAL"}, {"fullname": "main.askai.core.enums.verbosity.Verbosity.DETAILED", "modulename": "main.askai.core.enums.verbosity", "qualname": "Verbosity.DETAILED", "kind": "variable", "doc": "

    \n", "default_value": "DETAILED"}, {"fullname": "main.askai.core.enums.verbosity.Verbosity.FULL", "modulename": "main.askai.core.enums.verbosity", "qualname": "Verbosity.FULL", "kind": "variable", "doc": "

    \n", "default_value": "FULL"}, {"fullname": "main.askai.core.enums.verbosity.Verbosity.val", "modulename": "main.askai.core.enums.verbosity", "qualname": "Verbosity.val", "kind": "variable", "doc": "

    Gets the integer value of the verbosity level.

    \n\n
    Returns
    \n\n
    \n

    The integer representation of the verbosity level.

    \n
    \n", "annotation": ": int"}, {"fullname": "main.askai.core.enums.verbosity.Verbosity.match", "modulename": "main.askai.core.enums.verbosity", "qualname": "Verbosity.match", "kind": "function", "doc": "

    Checks if the current verbosity level is less than or equal to the given level.

    \n\n
    Parameters
    \n\n
      \n
    • level: The verbosity level to compare against.
    • \n
    \n\n
    Returns
    \n\n
    \n

    True if the current level is less than or equal to the given level, otherwise False.

    \n
    \n", "signature": "(self, level: main.askai.core.enums.verbosity.Verbosity) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.features", "modulename": "main.askai.core.features", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.features.processors", "modulename": "main.askai.core.features.processors", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.features.processors.ai_processor", "modulename": "main.askai.core.features.processors.ai_processor", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.features.router\n @file: ai_engine.py\n@created: Fri, 5 May 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.processors.ai_processor.AIProcessor", "modulename": "main.askai.core.features.processors.ai_processor", "qualname": "AIProcessor", "kind": "class", "doc": "

    Interface for AI processors, also known as routing modes. This protocol defines the required methods and\nbehaviors that AI processors must implement to handle specific routing tasks.

    \n", "bases": "typing.Protocol"}, {"fullname": "main.askai.core.features.processors.ai_processor.AIProcessor.__init__", "modulename": "main.askai.core.features.processors.ai_processor", "qualname": "AIProcessor.__init__", "kind": "function", "doc": "

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.processors.ai_processor.AIProcessor.process", "modulename": "main.askai.core.features.processors.ai_processor", "qualname": "AIProcessor.process", "kind": "function", "doc": "

    Process a user query and generate a response.

    \n\n
    Parameters
    \n\n
      \n
    • question: The user's query to be processed.
    • \n
    • kwargs: Additional arguments that may be used in the processing.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The generated response as a string, or None if no response is generated.

    \n
    \n", "signature": "(self, question: str, **kwargs) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.features.processors.qna", "modulename": "main.askai.core.features.processors.qna", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.features.processors.qna.QnA", "modulename": "main.askai.core.features.processors.qna", "qualname": "QnA", "kind": "class", "doc": "

    Processor to provide a questions and answers session about a summarized context.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.features.processors.qna.QnA.__init__", "modulename": "main.askai.core.features.processors.qna", "qualname": "QnA.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.processors.qna.QnA.INSTANCE", "modulename": "main.askai.core.features.processors.qna", "qualname": "QnA.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.features.processors.qna.QnA"}, {"fullname": "main.askai.core.features.processors.qna.QnA.process", "modulename": "main.askai.core.features.processors.qna", "qualname": "QnA.process", "kind": "function", "doc": "

    Process the user question against a summarized context to retrieve answers.

    \n\n
    Parameters
    \n\n
      \n
    • question: The user question to process.
    • \n
    \n", "signature": "(self, question: str, **_) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.features.processors.qstring", "modulename": "main.askai.core.features.processors.qstring", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.features.processors.qstring.NonInteractive", "modulename": "main.askai.core.features.processors.qstring", "qualname": "NonInteractive", "kind": "class", "doc": "

    Processor to provide a answers from custom prompts (non-interactive).

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.features.processors.qstring.NonInteractive.__init__", "modulename": "main.askai.core.features.processors.qstring", "qualname": "NonInteractive.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.processors.qstring.NonInteractive.INSTANCE", "modulename": "main.askai.core.features.processors.qstring", "qualname": "NonInteractive.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.features.processors.qstring.NonInteractive"}, {"fullname": "main.askai.core.features.processors.qstring.NonInteractive.DEFAULT_PROMPT", "modulename": "main.askai.core.features.processors.qstring", "qualname": "NonInteractive.DEFAULT_PROMPT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources/prompts/taius/taius-non-interactive'"}, {"fullname": "main.askai.core.features.processors.qstring.NonInteractive.DEFAULT_CONTEXT", "modulename": "main.askai.core.features.processors.qstring", "qualname": "NonInteractive.DEFAULT_CONTEXT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'No context has been provided'"}, {"fullname": "main.askai.core.features.processors.qstring.NonInteractive.DEFAULT_TEMPERATURE", "modulename": "main.askai.core.features.processors.qstring", "qualname": "NonInteractive.DEFAULT_TEMPERATURE", "kind": "variable", "doc": "

    \n", "annotation": ": int", "default_value": "0.7"}, {"fullname": "main.askai.core.features.processors.qstring.NonInteractive.process", "modulename": "main.askai.core.features.processors.qstring", "qualname": "NonInteractive.process", "kind": "function", "doc": "

    Process the user question to retrieve the final response.

    \n\n
    Parameters
    \n\n
      \n
    • question: The user question to process.
    • \n
    \n", "signature": "(self, question: str, **kwargs) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.features.processors.rag", "modulename": "main.askai.core.features.processors.rag", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.features.processors.rag.Rag", "modulename": "main.askai.core.features.processors.rag", "qualname": "Rag", "kind": "class", "doc": "

    Processor to provide a answers from a RAG datasource.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.features.processors.rag.Rag.__init__", "modulename": "main.askai.core.features.processors.rag", "qualname": "Rag.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.processors.rag.Rag.INSTANCE", "modulename": "main.askai.core.features.processors.rag", "qualname": "Rag.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.features.processors.rag.Rag"}, {"fullname": "main.askai.core.features.processors.rag.Rag.process", "modulename": "main.askai.core.features.processors.rag", "qualname": "Rag.process", "kind": "function", "doc": "

    Process the user question to retrieve the final response.

    \n\n
    Parameters
    \n\n
      \n
    • question: The user question to process.
    • \n
    \n", "signature": "(self, question: str, **_) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.processors.task_splitter", "modulename": "main.askai.core.features.processors.task_splitter", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.router\n @file: task_splitter.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.processors.task_splitter.AgentResponse", "modulename": "main.askai.core.features.processors.task_splitter", "qualname": "AgentResponse", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "dict[str, typing.Any]"}, {"fullname": "main.askai.core.features.processors.task_splitter.TaskSplitter", "modulename": "main.askai.core.features.processors.task_splitter", "qualname": "TaskSplitter", "kind": "class", "doc": "

    Processor to provide a divide and conquer set of tasks to fulfill an objective. This is responsible for the\norchestration and execution of the smaller tasks.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.features.processors.task_splitter.TaskSplitter.__init__", "modulename": "main.askai.core.features.processors.task_splitter", "qualname": "TaskSplitter.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.processors.task_splitter.TaskSplitter.INSTANCE", "modulename": "main.askai.core.features.processors.task_splitter", "qualname": "TaskSplitter.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.features.processors.task_splitter.TaskSplitter"}, {"fullname": "main.askai.core.features.processors.task_splitter.TaskSplitter.HUMAN_PROMPT", "modulename": "main.askai.core.features.processors.task_splitter", "qualname": "TaskSplitter.HUMAN_PROMPT", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": ""Human Question: '{input}'""}, {"fullname": "main.askai.core.features.processors.task_splitter.TaskSplitter.RETRIABLE_ERRORS", "modulename": "main.askai.core.features.processors.task_splitter", "qualname": "TaskSplitter.RETRIABLE_ERRORS", "kind": "variable", "doc": "

    \n", "annotation": ": tuple[typing.Type[Exception], ...]", "default_value": "(<class 'askai.exception.exceptions.InaccurateResponse'>, <class 'hspylib.core.exception.exceptions.InvalidArgumentError'>, <class 'pydantic_core._pydantic_core.ValidationError'>)"}, {"fullname": "main.askai.core.features.processors.task_splitter.TaskSplitter.wrap_answer", "modulename": "main.askai.core.features.processors.task_splitter", "qualname": "TaskSplitter.wrap_answer", "kind": "function", "doc": "

    Provide a final answer to the user by wrapping the AI response with additional context.

    \n\n
    Parameters
    \n\n
      \n
    • query: The user's question.
    • \n
    • answer: The AI's response to the question.
    • \n
    • model_result: The result from the selected routing model (default is ModelResult.default()).
    • \n
    • rag: The final accuracy check (RAG) response, if available.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A formatted string containing the final answer.

    \n
    \n", "signature": "(\tquery: str,\tanswer: str,\tmodel_result: askai.core.model.model_result.ModelResult = ModelResult(mid='ASK_000', goal='Default model', reason='Provide the answer as received by the AI'),\trag: askai.core.enums.acc_response.AccResponse | None = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.processors.task_splitter.TaskSplitter.template", "modulename": "main.askai.core.features.processors.task_splitter", "qualname": "TaskSplitter.template", "kind": "function", "doc": "

    Retrieve the processor Template.

    \n", "signature": "(self, query: str) -> langchain_core.prompts.chat.ChatPromptTemplate:", "funcdef": "def"}, {"fullname": "main.askai.core.features.processors.task_splitter.TaskSplitter.process", "modulename": "main.askai.core.features.processors.task_splitter", "qualname": "TaskSplitter.process", "kind": "function", "doc": "

    Process the user question by splitting complex tasks into smaller single actionable tasks.

    \n\n
    Parameters
    \n\n
      \n
    • question: The user question to process.
    • \n
    \n", "signature": "(self, question: str, **_) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router", "modulename": "main.askai.core.features.router", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.features.router.model_selector", "modulename": "main.askai.core.features.router.model_selector", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.router.model_selector\n @file: model_selector.py\n@created: Tue, 24 Jun 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.router.model_selector.ModelSelector", "modulename": "main.askai.core.features.router.model_selector", "qualname": "ModelSelector", "kind": "class", "doc": "

    Utility class to query the LLM for selecting the appropriate model to process user requests. This class\nfacilitates the selection of the most suitable model based on the nature of the user's query, ensuring optimal\nprocessing and response generation.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.features.router.model_selector.ModelSelector.__init__", "modulename": "main.askai.core.features.router.model_selector", "qualname": "ModelSelector.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.router.model_selector.ModelSelector.INSTANCE", "modulename": "main.askai.core.features.router.model_selector", "qualname": "ModelSelector.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.features.router.model_selector.ModelSelector", "default_value": "<main.askai.core.features.router.model_selector.ModelSelector object>"}, {"fullname": "main.askai.core.features.router.model_selector.ModelSelector.model_template", "modulename": "main.askai.core.features.router.model_selector", "qualname": "ModelSelector.model_template", "kind": "variable", "doc": "

    Retrieve the Routing Model Template.

    \n", "annotation": ": langchain_core.prompts.prompt.PromptTemplate"}, {"fullname": "main.askai.core.features.router.model_selector.ModelSelector.select_model", "modulename": "main.askai.core.features.router.model_selector", "qualname": "ModelSelector.select_model", "kind": "function", "doc": "

    Select the appropriate response model based on the given human question.

    \n\n
    Parameters
    \n\n
      \n
    • question: The user's query used to determine the most suitable model.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of ModelResult representing the selected model.

    \n
    \n", "signature": "(self, question: str) -> askai.core.model.model_result.ModelResult:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.model_selector.selector", "modulename": "main.askai.core.features.router.model_selector", "qualname": "selector", "kind": "variable", "doc": "

    \n", "default_value": "<main.askai.core.features.router.model_selector.ModelSelector object>"}, {"fullname": "main.askai.core.features.router.task_accuracy", "modulename": "main.askai.core.features.router.task_accuracy", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.rag.commons\n @file: analysis.py\n@created: Fri, 03 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.router.task_accuracy.EVALUATION_GUIDE", "modulename": "main.askai.core.features.router.task_accuracy", "qualname": "EVALUATION_GUIDE", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'**Accuracy Evaluation Guidelines:**\\n\\n1. Analyze past responses to ensure accuracy.\\n2. Regularly self-critique overall responses.\\n3. Reflect on past strategies to refine your approach.\\n4. Experiment with different methods or solutions.'"}, {"fullname": "main.askai.core.features.router.task_accuracy.RAG", "modulename": "main.askai.core.features.router.task_accuracy", "qualname": "RAG", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.support.rag_provider.RAGProvider", "default_value": "<askai.core.support.rag_provider.RAGProvider object>"}, {"fullname": "main.askai.core.features.router.task_accuracy.assert_accuracy", "modulename": "main.askai.core.features.router.task_accuracy", "qualname": "assert_accuracy", "kind": "function", "doc": "

    Assert that the AI's response to the question meets the required accuracy threshold.

    \n\n
    Parameters
    \n\n
      \n
    • question: The user's question.
    • \n
    • ai_response: The AI's response to be analyzed for accuracy.
    • \n
    • pass_threshold: The accuracy threshold, represented by a color, that must be met or exceeded for the\nresponse to be considered a pass (default is AccResponse.MODERATE).
    • \n
    \n\n
    Returns
    \n\n
    \n

    The accuracy classification of the AI's response as an AccResponse enum value.

    \n
    \n", "signature": "(\tquestion: str,\tai_response: str,\tpass_threshold: askai.core.enums.acc_response.AccResponse = MODERATE) -> askai.core.enums.acc_response.AccResponse:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_accuracy.resolve_x_refs", "modulename": "main.askai.core.features.router.task_accuracy", "qualname": "resolve_x_refs", "kind": "function", "doc": "

    Replace all cross-references with their actual values.

    \n\n
    Parameters
    \n\n
      \n
    • ref_name: The name of the cross-reference or variable to resolve.
    • \n
    • context: The context in which to analyze and resolve the references (optional).
    • \n
    \n\n
    Returns
    \n\n
    \n

    The string with all cross-references replaced by their corresponding values.

    \n
    \n", "signature": "(ref_name: str, context: str | None = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_agent", "modulename": "main.askai.core.features.router.task_agent", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.features.router.task_agent.TaskAgent", "modulename": "main.askai.core.features.router.task_agent", "qualname": "TaskAgent", "kind": "class", "doc": "

    A LangChain agent responsible for executing router tasks using the available tools. This agent manages and\nperforms tasks by leveraging various tools, ensuring efficient and accurate task execution in the routing process.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.features.router.task_agent.TaskAgent.__init__", "modulename": "main.askai.core.features.router.task_agent", "qualname": "TaskAgent.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.router.task_agent.TaskAgent.INSTANCE", "modulename": "main.askai.core.features.router.task_agent", "qualname": "TaskAgent.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.features.router.task_agent.TaskAgent"}, {"fullname": "main.askai.core.features.router.task_agent.TaskAgent.agent_template", "modulename": "main.askai.core.features.router.task_agent", "qualname": "TaskAgent.agent_template", "kind": "variable", "doc": "

    Retrieve the Structured Agent Template for use in the chat agent. This template is used to structure the\ninteractions of the chat agent.\nReference: https://smith.langchain.com/hub/hwchase17/structured-chat-agent

    \n\n
    Returns
    \n\n
    \n

    An instance of ChatPromptTemplate representing the structured agent template.

    \n
    \n", "annotation": ": langchain_core.prompts.chat.ChatPromptTemplate"}, {"fullname": "main.askai.core.features.router.task_agent.TaskAgent.invoke", "modulename": "main.askai.core.features.router.task_agent", "qualname": "TaskAgent.invoke", "kind": "function", "doc": "

    Invoke the agent to respond to the given query using the specified action plan.

    \n\n
    Parameters
    \n\n
      \n
    • task: The AI task that outlines the steps to generate the response.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The agent's response as a string.

    \n
    \n", "signature": "(self, task: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit", "modulename": "main.askai.core.features.router.task_toolkit", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.actions\n @file: task_toolkit.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit", "kind": "class", "doc": "

    This class serves as the toolkit for AskAI task agents, providing essential tools and functionalities required\nfor their tasks.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.__init__", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.INSTANCE", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.features.router.task_toolkit.AgentToolkit"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.RESERVED", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.RESERVED", "kind": "variable", "doc": "

    \n", "annotation": ": list[str]", "default_value": "['tools']"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.tools", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.tools", "kind": "function", "doc": "

    Return a cached list of LangChain base tools.

    \n\n
    Returns
    \n\n
    \n

    A list of BaseTool's instances available for use.

    \n
    \n", "signature": "(self) -> list[langchain_core.tools.base.BaseTool]:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.browse", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.browse", "kind": "function", "doc": "

    Use this tool to browse the internet for the latest news and current events, particularly when up-to-date\ninformation is needed quickly. This tool is especially effective for accessing the most recent data available\nonline.\nUsage: browse(search_query)

    \n\n
    Parameters
    \n\n
      \n
    • search_query: The web search query as a string.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A string containing the results of the web search, or None if no relevant results are found.

    \n
    \n", "signature": "(self, search_query: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.query_output", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.query_output", "kind": "function", "doc": "

    Use this tool to analyze textual content and identify the presence of files, folders, and applications. This\ntool is designed to process and analyze content that is already available in textual form, but it does not\ndirectly read or extract file contents.\nUsage: query_output(output_query)

    \n\n
    Parameters
    \n\n
      \n
    • output_query: The query regarding the output. Use \"Identify \".
    • \n
    \n\n
    Returns
    \n\n
    \n

    A string containing the results of the analysis based on the query.

    \n
    \n", "signature": "(self, output_query: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.image_captioner", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.image_captioner", "kind": "function", "doc": "

    Use this tool to generate a textual description of visual content, such as image files.\nUsage: image_captioner(image_path)

    \n\n
    Parameters
    \n\n
      \n
    • image_path: The absolute path of the image file to be analyzed.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A string containing the generated caption describing the image.

    \n
    \n", "signature": "(self, image_path: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.webcam_capturer", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.webcam_capturer", "kind": "function", "doc": "

    Capture a photo using the webcam, and save it locally. This tool is useful for taking photos, detect people's\nfaces, and, describing what is in front of the webcam.\nUsage: webcam_capturer(photo_name, detect_faces)

    \n\n
    Parameters
    \n\n
      \n
    • photo_name: The name of the photo file (without the extension). If None, a default name will be used.
    • \n
    • detect_faces: Whether to detect and describe all faces in the photo (default is False).
    • \n
    \n\n
    Returns
    \n\n
    \n

    The file path of the saved JPEG image.

    \n
    \n", "signature": "(self, photo_name: str | None, detect_faces: bool = False) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.webcam_identifier", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.webcam_identifier", "kind": "function", "doc": "

    Identify the person in front of the webcam using a pre-stored set of faces and provide a description. This\ntool is useful for recognizing individuals and generating descriptions based on pre-stored facial data.\nUsage: webcam_identifier()

    \n\n
    Returns
    \n\n
    \n

    A string containing the identification and description of the person.

    \n
    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.generate_content", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.generate_content", "kind": "function", "doc": "

    Use this tool to generate various types of content, such as code, text, images, etc. This tool processes\ndescriptive instructions to create the specified content type and can optionally save it to a file.\nUsage: generate_content(instructions, mime_type, filepath)

    \n\n
    Parameters
    \n\n
      \n
    • instructions: Descriptive instructions on how to create the content (not the content itself).
    • \n
    • mime_type: The MIME type representing the type of content to generate.
    • \n
    • filepath: The optional file path where the generated content will be saved.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The generated content as a string.

    \n
    \n", "signature": "(\tself,\tinstructions: str,\tmime_type: str,\tfilepath: Union[pathlib.Path, str, NoneType]) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.save_content", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.save_content", "kind": "function", "doc": "

    Save previously generated content to disk, such as programs, scripts, text, images, etc. This tool is used\nto persist content that was generated using the generate_content tool.\nUsage: save_content(filepath)

    \n\n
    Parameters
    \n\n
      \n
    • filepath: The path where you want to save the content.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The absolute path name of the saved file.

    \n
    \n", "signature": "(self, filepath: Union[pathlib.Path, str, NoneType]) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.direct_answer", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.direct_answer", "kind": "function", "doc": "

    Provide and display text as a direct answer to the user. This tool is used to present one or more pieces of\ntext directly to the user.\nUsage: direct_answer(text, ...repeat N times)

    \n\n
    Parameters
    \n\n
      \n
    • texts: A list of texts or a single text string to be displayed.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A string containing all provided texts concatenated together.

    \n
    \n", "signature": "(self, texts: list[str] | str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.list_tool", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.list_tool", "kind": "function", "doc": "

    Access and list the contents of a specified folder. This tool is used to retrieve the contents of a folder,\noptionally filtering the results based on specified criteria.\nUsage: list_tool(folder, filters)

    \n\n
    Parameters
    \n\n
      \n
    • folder: The absolute path of the folder whose contents you wish to list or access.
    • \n
    • filters: Optional parameter: A comma-separated list of file globs to filter results (e.g., \"., *.txt\").
    • \n
    \n\n
    Returns
    \n\n
    \n

    A string listing the contents of the folder, filtered by the provided criteria if applicable.

    \n
    \n", "signature": "(self, folder: str, filters: str | None = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.open_tool", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.open_tool", "kind": "function", "doc": "

    Open and display the content of files, or playback media files, and also execute applications. This tool is\nused to open a file, folder, or application, read its contents, or play back media files.\nUsage: open_tool(path_name)

    \n\n
    Parameters
    \n\n
      \n
    • path_name: The absolute path of the file, folder, or application to be opened.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The output generated by the open command, such as file content or playback status.

    \n
    \n", "signature": "(self, path_name: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.summarize", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.summarize", "kind": "function", "doc": "

    Summarize the contents of files and folders based on user requests. This tool should be used only when the\nuser explicitly requests a summary of files and folders, not for summarizing textual content.\nUsage: summarize(folder, glob)

    \n\n
    Parameters
    \n\n
      \n
    • folder: The base path of the folder containing the files to be summarized.
    • \n
    • glob: The glob expression to specify which files should be included in the summary.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A string containing the summary of the specified files and folders.

    \n
    \n", "signature": "(self, folder: str, glob: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.terminal", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.terminal", "kind": "function", "doc": "

    Execute terminal commands or process user-provided commands using the specified shell. This tool is used to\nrun commands in a terminal environment based on the provided shell type.\nUsage: terminal(shell_type, command)

    \n\n
    Parameters
    \n\n
      \n
    • shell_type: The type of shell to use (e.g., bash, zsh, powershell, etc.).
    • \n
    • command: The command or set of commands to execute in the terminal.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The output of the executed terminal command(s) as a string.

    \n
    \n", "signature": "(self, shell_type: str, command: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.router.task_toolkit.AgentToolkit.shutdown", "modulename": "main.askai.core.features.router.task_toolkit", "qualname": "AgentToolkit.shutdown", "kind": "function", "doc": "

    Conclude the interaction based on the user's intent to end the session (e.g., bye, exit). This tool is used\nto gracefully shut down the interaction when the user indicates a desire to terminate the session.\nUsage: shutdown(reason)

    \n\n
    Parameters
    \n\n
      \n
    • reason: The reason for terminating the session.
    • \n
    \n", "signature": "(self, reason: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools", "modulename": "main.askai.core.features.tools", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.features.tools.analysis", "modulename": "main.askai.core.features.tools.analysis", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.tools.analysis\n @file: analysis.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.tools.analysis.query_output", "modulename": "main.askai.core.features.tools.analysis", "qualname": "query_output", "kind": "function", "doc": "

    Handle 'Text analysis', invoking: analyze(question: str). Analyze the context and answer the question.

    \n\n
    Parameters
    \n\n
      \n
    • query: The question about the content to be analyzed.
    • \n
    • context: The context of the question.
    • \n
    \n", "signature": "(query: str, context: str = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.browser", "modulename": "main.askai.core.features.tools.browser", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.tools.browser\n @file: browser.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.tools.browser.browse", "modulename": "main.askai.core.features.tools.browser", "qualname": "browse", "kind": "function", "doc": "

    Fetch the information from the Internet.

    \n\n
    Parameters
    \n\n
      \n
    • query: The search query.
    • \n
    \n", "signature": "(query: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.general", "modulename": "main.askai.core.features.tools.general", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.tools.general\n @file: general.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.tools.general.display_tool", "modulename": "main.askai.core.features.tools.general", "qualname": "display_tool", "kind": "function", "doc": "

    Display the given texts using markdown.

    \n\n
    Parameters
    \n\n
      \n
    • texts: The list of texts to be displayed.
    • \n
    \n", "signature": "(*texts: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.general.final_answer", "modulename": "main.askai.core.features.tools.general", "qualname": "final_answer", "kind": "function", "doc": "

    Provide the final response to the user.

    \n\n
    Parameters
    \n\n
      \n
    • persona_prompt: The persona prompt to be used.
    • \n
    • input_variables: The prompt input variables.
    • \n
    • prompt_args: The prompt input arguments.
    • \n
    \n", "signature": "(\tpersona_prompt: str | None = None,\tinput_variables: list[str] | None = None,\t**prompt_args) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.generation", "modulename": "main.askai.core.features.tools.generation", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.tools.generation\n @file: generation.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.tools.generation.generate_content", "modulename": "main.askai.core.features.tools.generation", "qualname": "generate_content", "kind": "function", "doc": "

    Generate content using the AI. It's a general function now, but it can be specialized afterwards.

    \n\n
    Parameters
    \n\n
      \n
    • instructions: The instructions for generating the content.
    • \n
    • mime_type: The generated content type (use MIME types).
    • \n
    • filepath: Optional file path for saving the content.
    • \n
    \n", "signature": "(\tinstructions: str,\tmime_type: str,\tfilepath: Union[pathlib.Path, str, NoneType] = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.generation.save_content", "modulename": "main.askai.core.features.tools.generation", "qualname": "save_content", "kind": "function", "doc": "

    Save any generated context into a file.

    \n\n
    Parameters
    \n\n
      \n
    • filepath: The path where you want to save the content.
    • \n
    • content: Optional content to be saved. If not provided, it will get from the last generated context.
    • \n
    \n", "signature": "(\tfilepath: Union[pathlib.Path, str, NoneType],\tcontent: Optional[~AnyStr] = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.summarization", "modulename": "main.askai.core.features.tools.summarization", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.tools.summarization\n @file: summarization.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.tools.summarization.summarize", "modulename": "main.askai.core.features.tools.summarization", "qualname": "summarize", "kind": "function", "doc": "

    Summarize files and folders.

    \n\n
    Parameters
    \n\n
      \n
    • base_folder: The base folder to be summarized.
    • \n
    • glob: The glob to match the files to be summarized.
    • \n
    \n", "signature": "(base_folder: str, glob: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.terminal", "modulename": "main.askai.core.features.tools.terminal", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.features.tools.terminal\n @file: terminal.py\n@created: Mon, 01 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.features.tools.terminal.list_contents", "modulename": "main.askai.core.features.tools.terminal", "qualname": "list_contents", "kind": "function", "doc": "

    List the contents of a folder.

    \n\n
    Parameters
    \n\n
      \n
    • folder: The folder to list contents from.
    • \n
    • filters: The optional listing filters (file glob).
    • \n
    \n", "signature": "(folder: str, filters: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.terminal.open_command", "modulename": "main.askai.core.features.tools.terminal", "qualname": "open_command", "kind": "function", "doc": "

    Open the specified path, regardless if it's a file, folder or application.

    \n\n
    Parameters
    \n\n
      \n
    • path_name: The file path to open.
    • \n
    \n", "signature": "(path_name: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.terminal.execute_command", "modulename": "main.askai.core.features.tools.terminal", "qualname": "execute_command", "kind": "function", "doc": "

    Execute a terminal command using the specified language.

    \n\n
    Parameters
    \n\n
      \n
    • shell: The shell type to be used.
    • \n
    • command_line: The command line to be executed.
    • \n
    \n", "signature": "(shell: str, command_line: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.vision", "modulename": "main.askai.core.features.tools.vision", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.features.tools.vision.HFModel", "modulename": "main.askai.core.features.tools.vision", "qualname": "HFModel", "kind": "class", "doc": "

    Available Hugging Face models

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.core.features.tools.vision.HFModel.SF_BLIP_BASE", "modulename": "main.askai.core.features.tools.vision", "qualname": "HFModel.SF_BLIP_BASE", "kind": "variable", "doc": "

    \n", "default_value": "SF_BLIP_BASE"}, {"fullname": "main.askai.core.features.tools.vision.HFModel.SF_BLIP_LARGE", "modulename": "main.askai.core.features.tools.vision", "qualname": "HFModel.SF_BLIP_LARGE", "kind": "variable", "doc": "

    \n", "default_value": "SF_BLIP_LARGE"}, {"fullname": "main.askai.core.features.tools.vision.HFModel.default", "modulename": "main.askai.core.features.tools.vision", "qualname": "HFModel.default", "kind": "function", "doc": "

    Return the default HF model.

    \n", "signature": "() -> main.askai.core.features.tools.vision.HFModel:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.vision.offline_captioner", "modulename": "main.askai.core.features.tools.vision", "qualname": "offline_captioner", "kind": "function", "doc": "

    This tool is used to describe an image.

    \n\n
    Parameters
    \n\n
      \n
    • path_name: The path of the image to describe.
    • \n
    \n", "signature": "(path_name: Union[pathlib.Path, str, NoneType]) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.vision.image_captioner", "modulename": "main.askai.core.features.tools.vision", "qualname": "image_captioner", "kind": "function", "doc": "

    This tool is used to describe an image.

    \n\n
    Parameters
    \n\n
      \n
    • path_name: The path of the image to describe.
    • \n
    • load_dir: Optional directory path for loading related resources.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A string containing the description of the image, or None if the description could not be generated.

    \n
    \n", "signature": "(\tpath_name: Union[pathlib.Path, str, NoneType],\tload_dir: Union[pathlib.Path, str, NoneType] = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.vision.parse_caption", "modulename": "main.askai.core.features.tools.vision", "qualname": "parse_caption", "kind": "function", "doc": "

    Parse the given image caption.

    \n\n
    Parameters
    \n\n
      \n
    • image_caption: The caption to parse.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The parsed caption as a string.

    \n
    \n", "signature": "(image_caption: str) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.vision.take_screenshot", "modulename": "main.askai.core.features.tools.vision", "qualname": "take_screenshot", "kind": "function", "doc": "

    Takes a screenshot and saves it to the specified path.

    \n\n
    Parameters
    \n\n
      \n
    • path_name: The path where the screenshot will be saved.
    • \n
    • load_dir: Optional directory to save the screenshot.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The path to the saved screenshot.

    \n
    \n", "signature": "(\tpath_name: Union[pathlib.Path, str, NoneType],\tload_dir: Union[pathlib.Path, str, NoneType] = None) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.webcam", "modulename": "main.askai.core.features.tools.webcam", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.features.tools.webcam.webcam_capturer", "modulename": "main.askai.core.features.tools.webcam", "qualname": "webcam_capturer", "kind": "function", "doc": "

    Capture a photo using the webcam and save it locally. Optionally detect faces in the photo.

    \n\n
    Parameters
    \n\n
      \n
    • photo_name: The name of the photo file. If None, a default name will be used.
    • \n
    • detect_faces: Whether to detect faces in the photo.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The file path of the saved photo.

    \n
    \n", "signature": "(photo_name: str | None, detect_faces: bool = False) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.features.tools.webcam.webcam_identifier", "modulename": "main.askai.core.features.tools.webcam", "qualname": "webcam_identifier", "kind": "function", "doc": "

    Identifies the person in front of the webcam and provides a description of them.

    \n\n
    Parameters
    \n\n
      \n
    • max_distance: The maximum distance for identifying the person based on image similarity.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A description of the identified person.

    \n
    \n", "signature": "(max_distance: int = 0.7) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.model", "modulename": "main.askai.core.model", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.model.action_plan", "modulename": "main.askai.core.model.action_plan", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model.action_plan\n @file: action_plan.py\n@created: Fri, 19 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan", "kind": "class", "doc": "

    Represents and tracks the action plan for a router.\nThis class is used to keep track of the sequence of actions or steps to be executed by the router.

    \n"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.__init__", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tquestion: str = None,\tprimary_goal: str = None,\tsub_goals: list[str] = None,\tthoughts: types.SimpleNamespace = None,\ttasks: list[types.SimpleNamespace] = None,\tmodel: askai.core.model.model_result.ModelResult = <factory>)"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.question", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.question", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.primary_goal", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.primary_goal", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.sub_goals", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.sub_goals", "kind": "variable", "doc": "

    \n", "annotation": ": list[str]", "default_value": "None"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.thoughts", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.thoughts", "kind": "variable", "doc": "

    \n", "annotation": ": types.SimpleNamespace", "default_value": "None"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.tasks", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.tasks", "kind": "variable", "doc": "

    \n", "annotation": ": list[types.SimpleNamespace]", "default_value": "None"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.model", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.model", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.model.model_result.ModelResult"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.create", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.create", "kind": "function", "doc": "

    Create an ActionPlan based on the provided question, AI message, and result model.

    \n\n
    Parameters
    \n\n
      \n
    • question: The original question or command that was sent to the AI.
    • \n
    • message: The AIMessage object containing the AI's response and metadata.
    • \n
    • model: The result model.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of ActionPlan created from the provided inputs.

    \n
    \n", "signature": "(\tquestion: str,\tmessage: langchain_core.messages.ai.AIMessage,\tmodel: askai.core.model.model_result.ModelResult) -> main.askai.core.model.action_plan.ActionPlan:", "funcdef": "def"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.reasoning", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.reasoning", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[str]"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.observations", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.observations", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[str]"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.criticism", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.criticism", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[str]"}, {"fullname": "main.askai.core.model.action_plan.ActionPlan.speak", "modulename": "main.askai.core.model.action_plan", "qualname": "ActionPlan.speak", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[str]"}, {"fullname": "main.askai.core.model.ai_reply", "modulename": "main.askai.core.model.ai_reply", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.engine.model\n @file: ai_reply.py\n@created: Fri, 12 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.model.ai_reply.AIReply", "modulename": "main.askai.core.model.ai_reply", "qualname": "AIReply", "kind": "class", "doc": "

    Data class that represents AI replies.

    \n\n
    Parameters
    \n\n
      \n
    • message: The reply message.
    • \n
    • is_success: Indicates whether the reply is successful.
    • \n
    • is_debug: Indicates whether the reply is for debugging.
    • \n
    • verbosity: The verbosity level of the reply.
    • \n
    • is_speakable: Indicates whether the reply is speakable.
    • \n
    \n"}, {"fullname": "main.askai.core.model.ai_reply.AIReply.__init__", "modulename": "main.askai.core.model.ai_reply", "qualname": "AIReply.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tmessage: str = '',\tis_success: bool = True,\tis_debug: bool = False,\tverbosity: askai.core.enums.verbosity.Verbosity = MINIMUM,\tis_speakable: bool = True)"}, {"fullname": "main.askai.core.model.ai_reply.AIReply.message", "modulename": "main.askai.core.model.ai_reply", "qualname": "AIReply.message", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "''"}, {"fullname": "main.askai.core.model.ai_reply.AIReply.is_success", "modulename": "main.askai.core.model.ai_reply", "qualname": "AIReply.is_success", "kind": "variable", "doc": "

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.core.model.ai_reply.AIReply.is_debug", "modulename": "main.askai.core.model.ai_reply", "qualname": "AIReply.is_debug", "kind": "variable", "doc": "

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.core.model.ai_reply.AIReply.verbosity", "modulename": "main.askai.core.model.ai_reply", "qualname": "AIReply.verbosity", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.enums.verbosity.Verbosity", "default_value": "MINIMUM"}, {"fullname": "main.askai.core.model.ai_reply.AIReply.is_speakable", "modulename": "main.askai.core.model.ai_reply", "qualname": "AIReply.is_speakable", "kind": "variable", "doc": "

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.core.model.ai_reply.AIReply.info", "modulename": "main.askai.core.model.ai_reply", "qualname": "AIReply.info", "kind": "function", "doc": "

    Creates an info reply.

    \n\n
    Parameters
    \n\n
      \n
    • message: The reply message.
    • \n
    • verbosity: The verbosity level of the reply.
    • \n
    • speakable: Indicates whether the reply is speakable.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An AIReply instance with info settings.

    \n
    \n", "signature": "(\tmessage: ~AnyStr,\tverbosity: askai.core.enums.verbosity.Verbosity = NORMAL,\tspeakable: bool = True) -> main.askai.core.model.ai_reply.AIReply:", "funcdef": "def"}, {"fullname": "main.askai.core.model.ai_reply.AIReply.detailed", "modulename": "main.askai.core.model.ai_reply", "qualname": "AIReply.detailed", "kind": "function", "doc": "

    Creates a detailed verbosity reply.

    \n\n
    Parameters
    \n\n
      \n
    • message: The reply message.
    • \n
    • speakable: Indicates whether the reply is speakable.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An AIReply instance with detailed settings.

    \n
    \n", "signature": "(\tmessage: ~AnyStr,\tspeakable: bool = False) -> main.askai.core.model.ai_reply.AIReply:", "funcdef": "def"}, {"fullname": "main.askai.core.model.ai_reply.AIReply.full", "modulename": "main.askai.core.model.ai_reply", "qualname": "AIReply.full", "kind": "function", "doc": "

    Creates a full verbose reply.

    \n\n
    Parameters
    \n\n
      \n
    • message: The reply message.
    • \n
    • speakable: Indicates whether the reply is speakable.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An AIReply instance with full settings.

    \n
    \n", "signature": "(\tmessage: ~AnyStr,\tspeakable: bool = False) -> main.askai.core.model.ai_reply.AIReply:", "funcdef": "def"}, {"fullname": "main.askai.core.model.ai_reply.AIReply.error", "modulename": "main.askai.core.model.ai_reply", "qualname": "AIReply.error", "kind": "function", "doc": "

    Creates an error reply.

    \n\n
    Parameters
    \n\n
      \n
    • message: The reply message.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An AIReply instance with error settings.

    \n
    \n", "signature": "(message: ~AnyStr) -> main.askai.core.model.ai_reply.AIReply:", "funcdef": "def"}, {"fullname": "main.askai.core.model.ai_reply.AIReply.debug", "modulename": "main.askai.core.model.ai_reply", "qualname": "AIReply.debug", "kind": "function", "doc": "

    Creates a debug reply.

    \n\n
    Parameters
    \n\n
      \n
    • message: The reply message.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An AIReply instance with debug settings.

    \n
    \n", "signature": "(message: ~AnyStr) -> main.askai.core.model.ai_reply.AIReply:", "funcdef": "def"}, {"fullname": "main.askai.core.model.ai_reply.AIReply.mute", "modulename": "main.askai.core.model.ai_reply", "qualname": "AIReply.mute", "kind": "function", "doc": "

    Creates a mute reply.

    \n\n
    Parameters
    \n\n
      \n
    • message: The reply message.
    • \n
    • verbosity: The verbosity level of the reply.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An AIReply instance with mute settings.

    \n
    \n", "signature": "(\tmessage: ~AnyStr,\tverbosity: askai.core.enums.verbosity.Verbosity = NORMAL) -> main.askai.core.model.ai_reply.AIReply:", "funcdef": "def"}, {"fullname": "main.askai.core.model.ai_reply.AIReply.is_error", "modulename": "main.askai.core.model.ai_reply", "qualname": "AIReply.is_error", "kind": "variable", "doc": "

    Checks if the reply indicates an error.

    \n\n
    Returns
    \n\n
    \n

    True if the reply is not successful, otherwise False.

    \n
    \n", "annotation": ": bool"}, {"fullname": "main.askai.core.model.api_keys", "modulename": "main.askai.core.model.api_keys", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.askai_configs\n @file: askai_configs.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.model.api_keys.API_KEY_FILE", "modulename": "main.askai.core.model.api_keys", "qualname": "API_KEY_FILE", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "$HHS_ENV_FILE"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys", "kind": "class", "doc": "

    A class to manage and handle the required API keys.\nThis class provides a structured way to store, access, and manage API keys necessary for various services.\nIt inherits from BaseSettings to facilitate environment-based configuration.

    \n", "bases": "pydantic.v1.env_settings.BaseSettings"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.OPENAI_API_KEY", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.OPENAI_API_KEY", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.GOOGLE_API_KEY", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.GOOGLE_API_KEY", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.DEEPL_API_KEY", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.DEEPL_API_KEY", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.not_empty", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.not_empty", "kind": "function", "doc": "

    Pydantic validator to ensure that API key fields are not empty.

    \n\n
    Parameters
    \n\n
      \n
    • value: The value of the API key being validated.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The value if it is not empty.

    \n
    \n\n
    Raises
    \n\n
      \n
    • ValueError: If the value is empty or None.
    • \n
    \n", "signature": "(cls, value: ~AnyStr) -> ~AnyStr:", "funcdef": "def"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.has_key", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.has_key", "kind": "function", "doc": "

    Check if the specified API key exists and is not empty.

    \n\n
    Parameters
    \n\n
      \n
    • key_name: The name of the API key to check.
    • \n
    \n\n
    Returns
    \n\n
    \n

    True if the API key exists and is not empty, otherwise False.

    \n
    \n", "signature": "(self, key_name: str) -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.Config", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.Config", "kind": "class", "doc": "

    Configuration class for setting environment variables related to API keys.

    \n"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.Config.env_file", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.Config.env_file", "kind": "variable", "doc": "

    \n", "default_value": "$HHS_ENV_FILE"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.Config.env_file_encoding", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.Config.env_file_encoding", "kind": "variable", "doc": "

    \n", "default_value": "'utf-8'"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.prompt", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.prompt", "kind": "function", "doc": "

    Prompt the user to input the required API keys.

    \n\n
    Returns
    \n\n
    \n

    True if all required API keys are successfully provided, otherwise False.

    \n
    \n", "signature": "() -> bool:", "funcdef": "def"}, {"fullname": "main.askai.core.model.api_keys.ApiKeys.ensure", "modulename": "main.askai.core.model.api_keys", "qualname": "ApiKeys.ensure", "kind": "function", "doc": "

    Ensure that the provided API key is valid for the required method.

    \n\n
    Parameters
    \n\n
      \n
    • api_key: The API key to check.
    • \n
    • feature: The feature for which the API key is required.
    • \n
    \n\n
    Raises
    \n\n
      \n
    • MissingApiKeyError: If the API key is not valid.
    • \n
    \n", "signature": "(self, api_key: str, feature: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.model.image_result", "modulename": "main.askai.core.model.image_result", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.core.model.image_result.ImageResult", "modulename": "main.askai.core.model.image_result", "qualname": "ImageResult", "kind": "class", "doc": "

    Information about an image. This class provides a schema for storing and validating image-related information\nusing Pydantic's data validation features.

    \n", "bases": "pydantic.main.BaseModel"}, {"fullname": "main.askai.core.model.image_result.ImageResult.people_count", "modulename": "main.askai.core.model.image_result", "qualname": "ImageResult.people_count", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.model.image_result.ImageResult.main_objects", "modulename": "main.askai.core.model.image_result", "qualname": "ImageResult.main_objects", "kind": "variable", "doc": "

    \n", "annotation": ": list[str]"}, {"fullname": "main.askai.core.model.image_result.ImageResult.env_description", "modulename": "main.askai.core.model.image_result", "qualname": "ImageResult.env_description", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.model.image_result.ImageResult.people_description", "modulename": "main.askai.core.model.image_result", "qualname": "ImageResult.people_description", "kind": "variable", "doc": "

    \n", "annotation": ": list[str]"}, {"fullname": "main.askai.core.model.image_result.ImageResult.of", "modulename": "main.askai.core.model.image_result", "qualname": "ImageResult.of", "kind": "function", "doc": "

    \n", "signature": "(image_caption: ~AnyStr) -> main.askai.core.model.image_result.ImageResult:", "funcdef": "def"}, {"fullname": "main.askai.core.model.image_result.ImageResult.model_config", "modulename": "main.askai.core.model.image_result", "qualname": "ImageResult.model_config", "kind": "variable", "doc": "

    \n", "default_value": "{}"}, {"fullname": "main.askai.core.model.image_result.ImageResult.model_fields", "modulename": "main.askai.core.model.image_result", "qualname": "ImageResult.model_fields", "kind": "variable", "doc": "

    \n", "default_value": "{'people_count': FieldInfo(annotation=int, required=True, description='Number of beings on the picture'), 'main_objects': FieldInfo(annotation=list[str], required=True, description='List of the main objects on the picture'), 'env_description': FieldInfo(annotation=str, required=True, description='Description of the atmosphere of the environment'), 'people_description': FieldInfo(annotation=list[str], required=True, description='List of people description')}"}, {"fullname": "main.askai.core.model.image_result.ImageResult.model_computed_fields", "modulename": "main.askai.core.model.image_result", "qualname": "ImageResult.model_computed_fields", "kind": "variable", "doc": "

    \n", "default_value": "{}"}, {"fullname": "main.askai.core.model.model_result", "modulename": "main.askai.core.model.model_result", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model\n @file: summary_result.py\n@created: Tue, 11 Mar 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.model.model_result.ModelResult", "modulename": "main.askai.core.model.model_result", "qualname": "ModelResult", "kind": "class", "doc": "

    Keep track of the model select responses.

    \n"}, {"fullname": "main.askai.core.model.model_result.ModelResult.__init__", "modulename": "main.askai.core.model.model_result", "qualname": "ModelResult.__init__", "kind": "function", "doc": "

    \n", "signature": "(mid: str = None, goal: str = None, reason: str = None)"}, {"fullname": "main.askai.core.model.model_result.ModelResult.mid", "modulename": "main.askai.core.model.model_result", "qualname": "ModelResult.mid", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.model_result.ModelResult.goal", "modulename": "main.askai.core.model.model_result", "qualname": "ModelResult.goal", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.model_result.ModelResult.reason", "modulename": "main.askai.core.model.model_result", "qualname": "ModelResult.reason", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.model_result.ModelResult.default", "modulename": "main.askai.core.model.model_result", "qualname": "ModelResult.default", "kind": "function", "doc": "

    Track and store the responses from the selected model.\nThis class is used to encapsulate the model selection returned by the LLM, including any relevant data\nassociated with the model's response.

    \n", "signature": "() -> main.askai.core.model.model_result.ModelResult:", "funcdef": "def"}, {"fullname": "main.askai.core.model.search_result", "modulename": "main.askai.core.model.search_result", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model\n @file: search_result.py\n@created: Sun, 12 Mar 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.model.search_result.SearchResult", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult", "kind": "class", "doc": "

    Track and store responses from internet searches.\nThis class encapsulates the results returned from an internet search, including any relevant data\nassociated with the search response.

    \n"}, {"fullname": "main.askai.core.model.search_result.SearchResult.__init__", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tquestion: str = None,\tdatetime: str = None,\tcategory: str = None,\tkeywords: List[str] = None,\tsites: List[str] = None,\tfilters: List[str] = None,\tresponse: str = None)"}, {"fullname": "main.askai.core.model.search_result.SearchResult.question", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.question", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.search_result.SearchResult.datetime", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.datetime", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.search_result.SearchResult.category", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.category", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.search_result.SearchResult.keywords", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.keywords", "kind": "variable", "doc": "

    \n", "annotation": ": List[str]", "default_value": "None"}, {"fullname": "main.askai.core.model.search_result.SearchResult.sites", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.sites", "kind": "variable", "doc": "

    \n", "annotation": ": List[str]", "default_value": "None"}, {"fullname": "main.askai.core.model.search_result.SearchResult.filters", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.filters", "kind": "variable", "doc": "

    \n", "annotation": ": List[str]", "default_value": "None"}, {"fullname": "main.askai.core.model.search_result.SearchResult.response", "modulename": "main.askai.core.model.search_result", "qualname": "SearchResult.response", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.summary_result", "modulename": "main.askai.core.model.summary_result", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model\n @file: summary_result.py\n@created: Sun, 10 Mar 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.model.summary_result.SummaryResult", "modulename": "main.askai.core.model.summary_result", "qualname": "SummaryResult", "kind": "class", "doc": "

    Track and store the responses from summarization tasks. This class encapsulates the results of text\nsummarization, including the summarized content and any relevant data associated with the summarization\nprocess.

    \n"}, {"fullname": "main.askai.core.model.summary_result.SummaryResult.__init__", "modulename": "main.askai.core.model.summary_result", "qualname": "SummaryResult.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tfolder: str = None,\tglob: str = None,\tquestion: str = None,\tanswer: str = None)"}, {"fullname": "main.askai.core.model.summary_result.SummaryResult.folder", "modulename": "main.askai.core.model.summary_result", "qualname": "SummaryResult.folder", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.summary_result.SummaryResult.glob", "modulename": "main.askai.core.model.summary_result", "qualname": "SummaryResult.glob", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.summary_result.SummaryResult.question", "modulename": "main.askai.core.model.summary_result", "qualname": "SummaryResult.question", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.model.summary_result.SummaryResult.answer", "modulename": "main.askai.core.model.summary_result", "qualname": "SummaryResult.answer", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "None"}, {"fullname": "main.askai.core.support", "modulename": "main.askai.core.support", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.core.support.chat_context", "modulename": "main.askai.core.support.chat_context", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.support.chat_context\n @file: chat_context.py\n@created: Fri, 28 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.chat_context.ChatRoles", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatRoles", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "Literal['system', 'human', 'assistant']"}, {"fullname": "main.askai.core.support.chat_context.ContextRaw", "modulename": "main.askai.core.support.chat_context", "qualname": "ContextRaw", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "list[dict[str, str]]"}, {"fullname": "main.askai.core.support.chat_context.LangChainContext", "modulename": "main.askai.core.support.chat_context", "qualname": "LangChainContext", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "list[tuple[str, str]]"}, {"fullname": "main.askai.core.support.chat_context.ContextEntry", "modulename": "main.askai.core.support.chat_context", "qualname": "ContextEntry", "kind": "class", "doc": "

    ContextEntry(role, content)

    \n", "bases": "builtins.tuple"}, {"fullname": "main.askai.core.support.chat_context.ContextEntry.__init__", "modulename": "main.askai.core.support.chat_context", "qualname": "ContextEntry.__init__", "kind": "function", "doc": "

    Create new instance of ContextEntry(role, content)

    \n", "signature": "(role, content)"}, {"fullname": "main.askai.core.support.chat_context.ContextEntry.role", "modulename": "main.askai.core.support.chat_context", "qualname": "ContextEntry.role", "kind": "variable", "doc": "

    Alias for field number 0

    \n"}, {"fullname": "main.askai.core.support.chat_context.ContextEntry.content", "modulename": "main.askai.core.support.chat_context", "qualname": "ContextEntry.content", "kind": "variable", "doc": "

    Alias for field number 1

    \n"}, {"fullname": "main.askai.core.support.chat_context.ChatContext", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext", "kind": "class", "doc": "

    Provide a chat context helper for AI engines.

    \n"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.__init__", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.__init__", "kind": "function", "doc": "

    \n", "signature": "(token_limit: int, max_context_size: int)"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.LANGCHAIN_ROLE_MAP", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.LANGCHAIN_ROLE_MAP", "kind": "variable", "doc": "

    \n", "annotation": ": dict", "default_value": "{'human': <class 'langchain_core.messages.human.HumanMessage'>, 'system': <class 'langchain_core.messages.system.SystemMessage'>, 'assistant': <class 'langchain_core.messages.ai.AIMessage'>}"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.of", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.of", "kind": "function", "doc": "

    Create a chat context from a list of context entries formatted as .

    \n\n
    Parameters
    \n\n
      \n
    • context: The initial list of chat context entries.
    • \n
    • token_limit: The maximum number of tokens allowed by the active engine's model.
    • \n
    • max_context_size: The maximum allowable size of the context (window size).
    • \n
    \n\n
    Returns
    \n\n
    \n

    A ChatContext instance created from the provided parameters.

    \n
    \n", "signature": "(\tcontext: list[str],\ttoken_limit: int,\tmax_context_size: int) -> main.askai.core.support.chat_context.ChatContext:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.keys", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.keys", "kind": "variable", "doc": "

    \n", "annotation": ": list[~AnyStr]"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.max_context_size", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.max_context_size", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.token_limit", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.token_limit", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.push", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.push", "kind": "function", "doc": "

    Push a context message to the chat with the specified role.

    \n\n
    Parameters
    \n\n
      \n
    • key: The identifier for the context message.
    • \n
    • content: The content of the message to push.
    • \n
    • role: The role associated with the message (default is \"human\").
    • \n
    \n\n
    Returns
    \n\n
    \n

    The updated chat context.

    \n
    \n", "signature": "(\tself,\tkey: str,\tcontent: Any,\trole: Literal['system', 'human', 'assistant'] = 'human') -> list[dict[str, str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.get", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.get", "kind": "function", "doc": "

    Retrieve a context message identified by the specified key.

    \n\n
    Parameters
    \n\n
      \n
    • key: The identifier for the context message.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The context message associated with the key.

    \n
    \n", "signature": "(self, key: str) -> list[dict[str, str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.set", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.set", "kind": "function", "doc": "

    Set the context message in the chat with the specified role.

    \n\n
    Parameters
    \n\n
      \n
    • key: The identifier for the context message.
    • \n
    • content: The content of the message to set.
    • \n
    • role: The role associated with the message (default is \"human\").
    • \n
    \n\n
    Returns
    \n\n
    \n

    The updated chat context.

    \n
    \n", "signature": "(\tself,\tkey: str,\tcontent: Any,\trole: Literal['system', 'human', 'assistant'] = 'human') -> list[dict[str, str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.remove", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.remove", "kind": "function", "doc": "

    Remove a context message from the chat at the specified index.

    \n\n
    Parameters
    \n\n
      \n
    • key: The identifier for the context message list.
    • \n
    • index: The position of the message to remove within the list.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The removed message if successful, otherwise None.

    \n
    \n", "signature": "(self, key: str, index: int) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.length", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.length", "kind": "function", "doc": "

    Return the length of the context identified by the specified key.

    \n\n
    Parameters
    \n\n
      \n
    • key: The identifier for the context.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The length of the context (e.g., number of content entries).

    \n
    \n", "signature": "(self, key: str):", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.join", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.join", "kind": "function", "doc": "

    Join multiple contexts identified by the specified keys.

    \n\n
    Parameters
    \n\n
      \n
    • keys: The identifiers for the contexts to join.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The combined chat context.

    \n
    \n", "signature": "(self, *keys: str) -> list[tuple[str, str]]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.flat", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.flat", "kind": "function", "doc": "

    Flatten multiple contexts identified by the specified keys into a single chat history.

    \n\n
    Parameters
    \n\n
      \n
    • keys: The identifiers for the contexts to flatten.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The flattened chat message history.

    \n
    \n", "signature": "(\tself,\t*keys: str) -> langchain_core.chat_history.InMemoryChatMessageHistory:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.clear", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.clear", "kind": "function", "doc": "

    Clear all chat contexts specified by the provided keys.

    \n\n
    Parameters
    \n\n
      \n
    • keys: The identifiers for the contexts to clear.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The number of contexts that were cleared.

    \n
    \n", "signature": "(self, *keys: str) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.forget", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.forget", "kind": "function", "doc": "

    Forget all entries pushed to the chat context for the specified keys.

    \n\n
    Parameters
    \n\n
      \n
    • keys: The identifiers for the contexts to forget.
    • \n
    \n", "signature": "(self, *keys: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.size", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.size", "kind": "function", "doc": "

    Return the number of entries in the context specified by the given key.

    \n\n
    Parameters
    \n\n
      \n
    • key: The identifier for the context.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The number of entries in the context.

    \n
    \n", "signature": "(self, key: str) -> int:", "funcdef": "def"}, {"fullname": "main.askai.core.support.chat_context.ChatContext.save", "modulename": "main.askai.core.support.chat_context", "qualname": "ChatContext.save", "kind": "function", "doc": "

    Save the current context window to the cache.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.support.langchain_support", "modulename": "main.askai.core.support.langchain_support", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.support.langchain_support\n @file: langchain_support.py\n@created: Fri, 28 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.langchain_support.LangChainSupport", "modulename": "main.askai.core.support.langchain_support", "qualname": "LangChainSupport", "kind": "class", "doc": "

    Helper class to support the use of langchain framework.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.support.langchain_support.LangChainSupport.__init__", "modulename": "main.askai.core.support.langchain_support", "qualname": "LangChainSupport.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.support.langchain_support.LangChainSupport.INSTANCE", "modulename": "main.askai.core.support.langchain_support", "qualname": "LangChainSupport.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.support.langchain_support.LangChainSupport"}, {"fullname": "main.askai.core.support.langchain_support.LangChainSupport.create_model", "modulename": "main.askai.core.support.langchain_support", "qualname": "LangChainSupport.create_model", "kind": "function", "doc": "

    Create a LangChain LLM model instance using the current AI engine.

    \n\n
    Parameters
    \n\n
      \n
    • temperature: The temperature setting for the LLM model, which controls the randomness of the output.
    • \n
    • top_p: The top-p setting for the LLM model, which controls the diversity of the output by limiting the\ncumulative probability of token selection.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of the LLM model.

    \n
    \n", "signature": "(\ttemperature: float = 0.0,\ttop_p: float = 0.0) -> langchain_core.language_models.llms.BaseLLM:", "funcdef": "def"}, {"fullname": "main.askai.core.support.langchain_support.LangChainSupport.create_chat_model", "modulename": "main.askai.core.support.langchain_support", "qualname": "LangChainSupport.create_chat_model", "kind": "function", "doc": "

    Create a LangChain LLM chat model instance using the current AI engine.

    \n\n
    Parameters
    \n\n
      \n
    • temperature: The temperature setting for the LLM chat model, which controls the randomness of the\nresponses.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of the LLM chat model.

    \n
    \n", "signature": "(\ttemperature: float = 0.0) -> langchain_core.language_models.chat_models.BaseChatModel:", "funcdef": "def"}, {"fullname": "main.askai.core.support.langchain_support.LangChainSupport.create_embeddings", "modulename": "main.askai.core.support.langchain_support", "qualname": "LangChainSupport.create_embeddings", "kind": "function", "doc": "

    Create a LangChain LLM embeddings model instance using the current AI engine.

    \n\n
    Parameters
    \n\n
      \n
    • model: The name of the embeddings model to use (default is \"text-embedding-3-small\").
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of the embeddings model.

    \n
    \n", "signature": "(\tmodel: str = 'text-embedding-3-small') -> langchain_core.embeddings.embeddings.Embeddings:", "funcdef": "def"}, {"fullname": "main.askai.core.support.platform", "modulename": "main.askai.core.support.platform", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.model\n @file: platform.py\n@created: Thu, 29 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.platform.SupportedPlatforms", "modulename": "main.askai.core.support.platform", "qualname": "SupportedPlatforms", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "Optional[Literal['linux', 'windows', 'darwin']]"}, {"fullname": "main.askai.core.support.platform.SupportedShells", "modulename": "main.askai.core.support.platform", "qualname": "SupportedShells", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "Optional[Literal['bash', 'csh', 'dash', 'ksh', 'tcsh', 'zsh', 'sh']]"}, {"fullname": "main.askai.core.support.platform.get_os", "modulename": "main.askai.core.support.platform", "qualname": "get_os", "kind": "function", "doc": "

    Retrieve the current operating system platform.

    \n\n
    Returns
    \n\n
    \n

    The current operating system as a SupportedPlatforms literal value.

    \n
    \n", "signature": "() -> Optional[Literal['linux', 'windows', 'darwin']]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.platform.get_shell", "modulename": "main.askai.core.support.platform", "qualname": "get_shell", "kind": "function", "doc": "

    Retrieve the current shell being used.

    \n\n
    Returns
    \n\n
    \n

    The current shell as a SupportedShells literal value.

    \n
    \n", "signature": "() -> Optional[Literal['bash', 'csh', 'dash', 'ksh', 'tcsh', 'zsh', 'sh']]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.platform.get_user", "modulename": "main.askai.core.support.platform", "qualname": "get_user", "kind": "function", "doc": "

    Retrieve the current user's username.

    \n\n
    Returns
    \n\n
    \n

    The username of the current user as a string. Returns \"user\" if the username is not found.

    \n
    \n", "signature": "() -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.support.presets", "modulename": "main.askai.core.support.presets", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.support\n @file: presets.py\n@created: Tue, 16 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.presets.Presets", "modulename": "main.askai.core.support.presets", "qualname": "Presets", "kind": "class", "doc": "

    Provides text streaming presets according to the language used.

    \n"}, {"fullname": "main.askai.core.support.presets.Presets.__init__", "modulename": "main.askai.core.support.presets", "qualname": "Presets.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tlang: str,\twords_per_breath: int,\tbase_speed: float,\twords_interval: float,\tbreath_interval: float,\tnumber_interval: float,\tcomma_interval: float,\tpunct_interval: float,\tenum_interval: float,\tperiod_interval: float)"}, {"fullname": "main.askai.core.support.presets.Presets.get", "modulename": "main.askai.core.support.presets", "qualname": "Presets.get", "kind": "function", "doc": "

    Retrieve the text-to-speech preset based on the specified parameters.

    \n\n
    Parameters
    \n\n
      \n
    • lang: The language code for the preset (default is \"en\" -> English).
    • \n
    • tempo: The tempo of the speech, where 1 is the default speed (natural).
    • \n
    • base_interval: The base interval between speech units (default is 0.010).
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of the Presets class configured with the specified parameters.

    \n
    \n", "signature": "(\tcls,\tlang: str = 'en',\ttempo: int = 1,\tbase_interval: float = 0.01) -> main.askai.core.support.presets.Presets:", "funcdef": "def"}, {"fullname": "main.askai.core.support.presets.Presets.words_per_breath", "modulename": "main.askai.core.support.presets", "qualname": "Presets.words_per_breath", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.support.presets.Presets.base_speed", "modulename": "main.askai.core.support.presets", "qualname": "Presets.base_speed", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.presets.Presets.words_interval", "modulename": "main.askai.core.support.presets", "qualname": "Presets.words_interval", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.presets.Presets.breath_interval", "modulename": "main.askai.core.support.presets", "qualname": "Presets.breath_interval", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.presets.Presets.number_interval", "modulename": "main.askai.core.support.presets", "qualname": "Presets.number_interval", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.presets.Presets.comma_interval", "modulename": "main.askai.core.support.presets", "qualname": "Presets.comma_interval", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.presets.Presets.punct_interval", "modulename": "main.askai.core.support.presets", "qualname": "Presets.punct_interval", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.presets.Presets.enum_interval", "modulename": "main.askai.core.support.presets", "qualname": "Presets.enum_interval", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.presets.Presets.period_interval", "modulename": "main.askai.core.support.presets", "qualname": "Presets.period_interval", "kind": "variable", "doc": "

    \n", "annotation": ": float"}, {"fullname": "main.askai.core.support.rag_provider", "modulename": "main.askai.core.support.rag_provider", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.support\n @file: rag_provider.py\n@created: Wed, 28 Aug 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.rag_provider.RAGProvider", "modulename": "main.askai.core.support.rag_provider", "qualname": "RAGProvider", "kind": "class", "doc": "

    A class responsible for implementing the Retrieval-Augmented Generation (RAG) mechanism.

    \n"}, {"fullname": "main.askai.core.support.rag_provider.RAGProvider.__init__", "modulename": "main.askai.core.support.rag_provider", "qualname": "RAGProvider.__init__", "kind": "function", "doc": "

    \n", "signature": "(rag_filepath: str)"}, {"fullname": "main.askai.core.support.rag_provider.RAGProvider.RAG_DIR", "modulename": "main.askai.core.support.rag_provider", "qualname": "RAGProvider.RAG_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources/rag')"}, {"fullname": "main.askai.core.support.rag_provider.RAGProvider.get_rag_examples", "modulename": "main.askai.core.support.rag_provider", "qualname": "RAGProvider.get_rag_examples", "kind": "function", "doc": "

    Retrieve a list of relevant examples based on the provided query.

    \n\n
    Parameters
    \n\n
      \n
    • query: The search query used to retrieve examples.
    • \n
    • k: The number of examples to retrieve (default is 3).
    • \n
    \n\n
    Returns
    \n\n
    \n

    A list of strings representing the retrieved examples.

    \n
    \n", "signature": "(self, query: str, k: int = 3) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.support.shared_instances", "modulename": "main.askai.core.support.shared_instances", "kind": "module", "doc": "

    @project: \"askai\"\n@package: \"askai\".main.askai.core.support\n @file: shared_instances.py\n@created: Tue, 23 Apr 2024\n @author: \"Hugo Saporetti Junior\n @site: \"https://github.com/yorevs/askai\")\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances", "kind": "class", "doc": "

    Provides access to shared instances.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.__init__", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.INSTANCE", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.support.shared_instances.SharedInstances"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.UNCERTAIN_ID", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.UNCERTAIN_ID", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'bde6f44d-c1a0-4b0c-bd74-8278e468e50c'"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.engine", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.engine", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[askai.core.engine.ai_engine.AIEngine]"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.context", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.context", "kind": "variable", "doc": "

    \n", "annotation": ": Optional[askai.core.support.chat_context.ChatContext]"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.nickname", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.nickname", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.username", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.username", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.nickname_md", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.nickname_md", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.username_md", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.username_md", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.idiom", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.idiom", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.memory", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.memory", "kind": "variable", "doc": "

    \n", "annotation": ": langchain.memory.chat_memory.BaseChatMemory"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.max_short_memory_size", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.max_short_memory_size", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.max_iteractions", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.max_iteractions", "kind": "variable", "doc": "

    \n", "annotation": ": int"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.app_info", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.app_info", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.create_engine", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.create_engine", "kind": "function", "doc": "

    Create or retrieve an AI engine instance based on the specified engine and model names.

    \n\n
    Parameters
    \n\n
      \n
    • engine_name: The name of the AI engine to create or retrieve.
    • \n
    • model_name: The name of the model to use with the AI engine.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of the AIEngine configured with the specified engine and model.

    \n
    \n", "signature": "(\tself,\tengine_name: str,\tmodel_name: str) -> askai.core.engine.ai_engine.AIEngine:", "funcdef": "def"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.create_context", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.create_context", "kind": "function", "doc": "

    Create or retrieve a chat context with the specified token limit.

    \n\n
    Parameters
    \n\n
      \n
    • token_limit: The maximum number of tokens allowed in the chat context.
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of the ChatContext configured with the specified token limit.

    \n
    \n", "signature": "(self, token_limit: int) -> askai.core.support.chat_context.ChatContext:", "funcdef": "def"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.create_memory", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.create_memory", "kind": "function", "doc": "

    Create or retrieve the conversation window memory.

    \n\n
    Parameters
    \n\n
      \n
    • memory_key: The key used to identify the memory (default is \"chat_history\").
    • \n
    \n\n
    Returns
    \n\n
    \n

    An instance of BaseChatMemory associated with the specified memory key.

    \n
    \n", "signature": "(\tself,\tmemory_key: str = 'chat_history') -> langchain.memory.chat_memory.BaseChatMemory:", "funcdef": "def"}, {"fullname": "main.askai.core.support.shared_instances.SharedInstances.input_text", "modulename": "main.askai.core.support.shared_instances", "qualname": "SharedInstances.input_text", "kind": "function", "doc": "

    Prompt the user for input.

    \n\n
    Parameters
    \n\n
      \n
    • input_prompt: The text prompt to display to the user.
    • \n
    • placeholder: The placeholder text to display in the input field (optional).
    • \n
    \n\n
    Returns
    \n\n
    \n

    The user's input as a string, or None if no input is provided.

    \n
    \n", "signature": "(self, input_prompt: str, placeholder: str | None = None) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.text_formatter", "modulename": "main.askai.core.support.text_formatter", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.support.text_formatter\n @file: text_formatter.py\n@created: Fri, 28 Feb 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter", "kind": "class", "doc": "

    A utility class for formatting text according to specified rules or styles.\nThis class provides various methods for transforming and formatting text,\nsuch as adjusting indentation, line breaks, or applying specific text styles.\nThe Singleton metaclass ensures that only one instance of this class exists throughout the application.

    \n", "bases": "typing.Type"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.__init__", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.__init__", "kind": "function", "doc": "

    Invoke the class constructor or return the instance if it exists.

    \n", "signature": "(*args, **kwargs)"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.INSTANCE", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.INSTANCE", "kind": "variable", "doc": "

    \n", "annotation": ": main.askai.core.support.text_formatter.TextFormatter"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.RE_URL", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.RE_URL", "kind": "variable", "doc": "

    \n", "default_value": ""(https?:\\\\/\\\\/(?:www\\\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\\\.[^\\\\s')]{2,}|www\\\\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\\\.[^\\\\s')]{2,}|https?:\\\\/\\\\/(?:www\\\\.|(?!www))[a-zA-Z0-9]+\\\\.[^\\\\s')]{2,}|www\\\\.[a-zA-Z0-9]+\\\\.[^\\\\s')]{2,})""}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.RE_MD_CODE_BLOCK", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.RE_MD_CODE_BLOCK", "kind": "variable", "doc": "

    \n", "default_value": "'(```.+```)'"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.CHAT_ICONS", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.CHAT_ICONS", "kind": "variable", "doc": "

    \n", "default_value": "{'\\uf659': ' Oops!\\n> \\uf659 An Exception Occurred: \\n#### ', '\\uf400': '\\n> \\uf400 *Tip:* ', '\\uf691': '\\n> \\uf691 *Analysis:* ', '\\uf006': '\\n> \\uf006 *Summary:* ', '\\uf6f6': '\\n> \\uf6f6 *Joke:* ', '\\uf6f2': '\\n> \\uf6f2 *Fun-Fact:* ', '\\uf421': '\\n> \\uf421 *Advice:* ', '\\ufb3d': '\\n> \\ufb3d *Conclusion:* '}"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.RE_TYPES", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.RE_TYPES", "kind": "variable", "doc": "

    \n", "default_value": "{'MD': '(```.+```)', '\\uf0c1': "(https?:\\\\/\\\\/(?:www\\\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\\\.[^\\\\s')]{2,}|www\\\\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\\\.[^\\\\s')]{2,}|https?:\\\\/\\\\/(?:www\\\\.|(?!www))[a-zA-Z0-9]+\\\\.[^\\\\s')]{2,}|www\\\\.[a-zA-Z0-9]+\\\\.[^\\\\s')]{2,})", '\\uf659': '[\\\\s*_]*Errors?[_*-:\\\\s]+', '\\uf400': '[\\\\s*_]*Hints?( ([Aa]nd|&) [Tt]ips?)?[_*-:\\\\s]+', '\\uf691': '[\\\\s*_]*Analysis[_*-:\\\\s]+', '\\uf006': '[\\\\s*_]*Summary[_*-:\\\\s]+', '\\uf6f2': '[\\\\s*_]*Fun[\\\\s-]+[Ff]acts?[_*-:\\\\s]+', '\\uf6f6': '[\\\\s*_]*(Jokes?(\\\\s+[Tt]ime)?)[_*-:\\\\s]+', '\\uf421': '[\\\\s*_]*Advice[_*-:\\\\s]+', '\\ufb3d': '[\\\\s*_]*Conclusion[_*-:\\\\s]+'}"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.ensure_ln", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.ensure_ln", "kind": "function", "doc": "

    Ensure the text starts and ends with a line separator.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to be formatted.
    • \n
    • separator: The line separator to use (default is the system's line separator).
    • \n
    \n\n
    Returns
    \n\n
    \n

    The formatted text with the specified line separator at the beginning and end.

    \n
    \n", "signature": "(text: str, separator: str = '\\n') -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.beautify", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.beautify", "kind": "function", "doc": "

    Beautify the provided text with icons and other formatting enhancements.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to be beautified.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The beautified text as a string with applied icons and formatting improvements.

    \n
    \n", "signature": "(self, text: Any) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.display_markdown", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.display_markdown", "kind": "function", "doc": "

    Display a markdown-formatted text.

    \n\n
    Parameters
    \n\n
      \n
    • text: The markdown-formatted text to be displayed.
    • \n
    \n", "signature": "(self, text: ~AnyStr) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.display_text", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.display_text", "kind": "function", "doc": "

    Display a VT100 formatted text.

    \n\n
    Parameters
    \n\n
      \n
    • text: The VT100 formatted text to be displayed.
    • \n
    \n", "signature": "(self, text: ~AnyStr) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.cmd_print", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.cmd_print", "kind": "function", "doc": "

    Display an AskAI commander formatted text.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to be displayed.
    • \n
    \n", "signature": "(self, text: ~AnyStr):", "funcdef": "def"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.remove_markdown", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.remove_markdown", "kind": "function", "doc": "

    Remove Markdown formatting from a string.

    \n", "signature": "(self, text: ~AnyStr) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.support.text_formatter.TextFormatter.strip_format", "modulename": "main.askai.core.support.text_formatter", "qualname": "TextFormatter.strip_format", "kind": "function", "doc": "

    Remove the markdown code block formatting from the text.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text containing the markdown code block formatting.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The text with the markdown code block formatting stripped away.

    \n
    \n", "signature": "(self, text: ~AnyStr) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities", "modulename": "main.askai.core.support.utilities", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.core.support.utilities\n @file: utilities.py\n@created: Wed, 10 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.core.support.utilities.read_stdin", "modulename": "main.askai.core.support.utilities", "qualname": "read_stdin", "kind": "function", "doc": "

    Read input from the standard input (stdin).

    \n\n
    Returns
    \n\n
    \n

    The input read from stdin as a string, or None if no input is provided.

    \n
    \n", "signature": "() -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.display_text", "modulename": "main.askai.core.support.utilities", "qualname": "display_text", "kind": "function", "doc": "

    Display the provided text in a formatted way.

    \n\n
    Parameters
    \n\n
      \n
    • text: The text to be displayed.
    • \n
    • prefix: A prefix to prepend to the text (optional).
    • \n
    • markdown: Whether to render the text using markdown formatting (default is True).
    • \n
    • erase_last: Whether to erase the last displayed line before displaying the new text (default is False).
    • \n
    \n", "signature": "(\ttext: ~AnyStr,\tprefix: ~AnyStr = '',\tmarkdown: bool = True,\terase_last=False) -> None:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.find_file", "modulename": "main.askai.core.support.utilities", "qualname": "find_file", "kind": "function", "doc": "

    Find the specified file by name in the most common locations.

    \n\n
    Parameters
    \n\n
      \n
    • filename: The name or path of the file to find.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The full path to the file if found, otherwise None.

    \n
    \n", "signature": "(filename: Union[pathlib.Path, str, NoneType]) -> Optional[pathlib.Path]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.copy_file", "modulename": "main.askai.core.support.utilities", "qualname": "copy_file", "kind": "function", "doc": "

    Copy the specified file to the given destination path.

    \n\n
    Parameters
    \n\n
      \n
    • srcfile: The path of the source file to be copied.
    • \n
    • destfile: The destination path where the file should be copied.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The path of the copied file as a string.

    \n
    \n", "signature": "(\tsrcfile: Union[pathlib.Path, str, NoneType],\tdestfile: Union[pathlib.Path, str, NoneType]) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.build_img_path", "modulename": "main.askai.core.support.utilities", "qualname": "build_img_path", "kind": "function", "doc": "

    Construct the full path for an image file based on the base directory, filename, and suffix.

    \n\n
    Parameters
    \n\n
      \n
    • base_dir: The base directory where the image file is located.
    • \n
    • filename: The name of the image file (without the suffix).
    • \n
    • suffix: The suffix or extension to append to the filename (e.g., \".jpg\", \".png\").
    • \n
    \n\n
    Returns
    \n\n
    \n

    The full path to the image file as a string, or None if the path cannot be constructed.

    \n
    \n", "signature": "(base_dir: pathlib.Path, filename: str, suffix: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.join_path", "modulename": "main.askai.core.support.utilities", "qualname": "join_path", "kind": "function", "doc": "

    Join the the given path name with the specified directory name.

    \n\n
    Parameters
    \n\n
      \n
    • dir_name: The new directory name to replace the existing one.
    • \n
    • path_name: The original path where the directory will be replaced.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The modified path with the new directory name.

    \n
    \n", "signature": "(\tdir_name: Union[pathlib.Path, str, NoneType],\tpath_name: Union[pathlib.Path, str, NoneType]) -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.read_resource", "modulename": "main.askai.core.support.utilities", "qualname": "read_resource", "kind": "function", "doc": "

    Read the resource file specified by the filename and return its content.

    \n\n
    Parameters
    \n\n
      \n
    • base_dir: The base directory, relative to the resources folder.
    • \n
    • filename: The name of the file to read.
    • \n
    • file_ext: The file extension of the file (default is \".txt\").
    • \n
    \n\n
    Returns
    \n\n
    \n

    The content of the file as a string.

    \n
    \n", "signature": "(\tbase_dir: Union[pathlib.Path, str, NoneType],\tfilename: Union[pathlib.Path, str, NoneType],\tfile_ext: str = '.txt') -> str:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.read_file", "modulename": "main.askai.core.support.utilities", "qualname": "read_file", "kind": "function", "doc": "

    Reads the contents of a file from the specified directory.

    \n\n
    Parameters
    \n\n
      \n
    • load_dir: The directory where the file is located.
    • \n
    • path_name: The path name of the file to read.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The contents of the file as a string, or None if the file cannot be read.

    \n
    \n", "signature": "(\tload_dir: Union[pathlib.Path, str, NoneType],\tpath_name: str) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.encode_image", "modulename": "main.askai.core.support.utilities", "qualname": "encode_image", "kind": "function", "doc": "

    Encode an image file to a base64 string.

    \n\n
    Parameters
    \n\n
      \n
    • file_path: Path to the image file to be encoded.
    • \n
    \n\n
    Returns
    \n\n
    \n

    Base64 encoded string of the image file.

    \n
    \n", "signature": "(file_path: str):", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.extract_path", "modulename": "main.askai.core.support.utilities", "qualname": "extract_path", "kind": "function", "doc": "

    Extract the first identifiable path from the provided command line text.

    \n\n
    Parameters
    \n\n
      \n
    • command_line: The command line text from which to extract the path.
    • \n
    • flags: Regex match flags to control the extraction process (default is re.IGNORECASE | re.MULTILINE).
    • \n
    \n\n
    Returns
    \n\n
    \n

    The first identified path as a string, or None if no path could be extracted.

    \n
    \n", "signature": "(\tcommand_line: str,\tflags: int = re.IGNORECASE|re.MULTILINE) -> Optional[str]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.extract_codeblock", "modulename": "main.askai.core.support.utilities", "qualname": "extract_codeblock", "kind": "function", "doc": "

    Extract the programming language and actual code from a markdown multi-line code block.

    \n\n
    Parameters
    \n\n
      \n
    • text: The markdown-formatted text containing the code block.
    • \n
    • flags: Regex match flags to control the extraction process (default is re.IGNORECASE | re.MULTILINE).
    • \n
    \n\n
    Returns
    \n\n
    \n

    A tuple where the first element is the programming language (or None if not specified),\n and the second element is the extracted code as a string.

    \n
    \n", "signature": "(\ttext: str,\tflags: int = re.IGNORECASE|re.MULTILINE) -> tuple[typing.Optional[str], str]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.media_type_of", "modulename": "main.askai.core.support.utilities", "qualname": "media_type_of", "kind": "function", "doc": "

    Return the media type of the specified file, or None if the type could not be determined.

    \n\n
    Parameters
    \n\n
      \n
    • pathname: The file path to check.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A tuple representing the media type (e.g., (\"image\", \"jpeg\")), or None if guessing was not possible.

    \n
    \n", "signature": "(pathname: str) -> Optional[tuple[str, ...]]:", "funcdef": "def"}, {"fullname": "main.askai.core.support.utilities.seconds", "modulename": "main.askai.core.support.utilities", "qualname": "seconds", "kind": "function", "doc": "

    Convert milliseconds to seconds.

    \n\n
    Parameters
    \n\n
      \n
    • millis: The time in milliseconds.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The equivalent time in seconds as a float.

    \n
    \n", "signature": "(millis: int) -> float:", "funcdef": "def"}, {"fullname": "main.askai.exception", "modulename": "main.askai.exception", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.exception.exceptions", "modulename": "main.askai.exception.exceptions", "kind": "module", "doc": "

    @project: HsPyLib-CFMan\n@package: askai.exception.exceptions\n @file: exceptions.py\n@created: Fri, 12 May 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.exception.exceptions.NoSuchEngineError", "modulename": "main.askai.exception.exceptions", "qualname": "NoSuchEngineError", "kind": "class", "doc": "

    Raised when the provided engine does not exist

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.InvalidRecognitionApiError", "modulename": "main.askai.exception.exceptions", "qualname": "InvalidRecognitionApiError", "kind": "class", "doc": "

    Raised when an invalid recognition API callback is provided.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.IntelligibleAudioError", "modulename": "main.askai.exception.exceptions", "qualname": "IntelligibleAudioError", "kind": "class", "doc": "

    Raised when an the provided audio was not recognized by the API.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.RecognitionApiRequestError", "modulename": "main.askai.exception.exceptions", "qualname": "RecognitionApiRequestError", "kind": "class", "doc": "

    Raised when an there was an error calling the recognition API.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.TranslationPackageError", "modulename": "main.askai.exception.exceptions", "qualname": "TranslationPackageError", "kind": "class", "doc": "

    Raised when an there was an error installing an Argos translation package.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.TokenLengthExceeded", "modulename": "main.askai.exception.exceptions", "qualname": "TokenLengthExceeded", "kind": "class", "doc": "

    Raised when the token is too big to fit the token context window.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.InvalidInputDevice", "modulename": "main.askai.exception.exceptions", "qualname": "InvalidInputDevice", "kind": "class", "doc": "

    Raised when an invalid recording input device is used.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.DocumentsNotFound", "modulename": "main.askai.exception.exceptions", "qualname": "DocumentsNotFound", "kind": "class", "doc": "

    Raised when no documents are found for summarization.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.InaccurateResponse", "modulename": "main.askai.exception.exceptions", "qualname": "InaccurateResponse", "kind": "class", "doc": "

    Raised when detected an inaccurate response form the AI.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.MaxInteractionsReached", "modulename": "main.askai.exception.exceptions", "qualname": "MaxInteractionsReached", "kind": "class", "doc": "

    Raised when the number of executed actions exceeded the limit.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.ImpossibleQuery", "modulename": "main.askai.exception.exceptions", "qualname": "ImpossibleQuery", "kind": "class", "doc": "

    Raised when tracing or executing an action plan is/was not possible.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.TerminatingQuery", "modulename": "main.askai.exception.exceptions", "qualname": "TerminatingQuery", "kind": "class", "doc": "

    Raised when received a terminating question.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.InvalidStructuredResponse", "modulename": "main.askai.exception.exceptions", "qualname": "InvalidStructuredResponse", "kind": "class", "doc": "

    Raised when received an invalid structured response.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.MissingApiKeyError", "modulename": "main.askai.exception.exceptions", "qualname": "MissingApiKeyError", "kind": "class", "doc": "

    Raised when received an invalid structured response.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.WebCamInitializationFailure", "modulename": "main.askai.exception.exceptions", "qualname": "WebCamInitializationFailure", "kind": "class", "doc": "

    Raised when failed to initialize the computer Webcam.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.CameraAccessFailure", "modulename": "main.askai.exception.exceptions", "qualname": "CameraAccessFailure", "kind": "class", "doc": "

    Raised when failed to operate with the computer Webcam.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.exception.exceptions.InterruptionRequest", "modulename": "main.askai.exception.exceptions", "qualname": "InterruptionRequest", "kind": "class", "doc": "

    Raised when the AI flags to interrupt the execution of the action plan.

    \n", "bases": "hspylib.core.exception.exceptions.HSBaseException"}, {"fullname": "main.askai.language", "modulename": "main.askai.language", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.language.ai_translator", "modulename": "main.askai.language.ai_translator", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.language.argos_translator\n @file: translator.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.language.ai_translator.AITranslator", "modulename": "main.askai.language.ai_translator", "qualname": "AITranslator", "kind": "class", "doc": "

    Provides a base class for multilingual offline translation engines. Various implementations can be used.

    \n", "bases": "typing.Protocol"}, {"fullname": "main.askai.language.ai_translator.AITranslator.__init__", "modulename": "main.askai.language.ai_translator", "qualname": "AITranslator.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tsource_lang: askai.language.language.Language,\ttarget_lang: askai.language.language.Language)"}, {"fullname": "main.askai.language.ai_translator.AITranslator.translate", "modulename": "main.askai.language.ai_translator", "qualname": "AITranslator.translate", "kind": "function", "doc": "

    Translate text from the source language to the target language.

    \n\n
    Parameters
    \n\n
      \n
    • text: Text to translate.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The translated text.

    \n
    \n", "signature": "(self, text: str, **kwargs) -> str:", "funcdef": "def"}, {"fullname": "main.askai.language.ai_translator.AITranslator.name", "modulename": "main.askai.language.ai_translator", "qualname": "AITranslator.name", "kind": "function", "doc": "

    Return the translator name or model.

    \n\n
    Returns
    \n\n
    \n

    The name or model of the translator.

    \n
    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.language.language", "modulename": "main.askai.language.language", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.language.language\n @file: language.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.language.language.AnyLocale", "modulename": "main.askai.language.language", "qualname": "AnyLocale", "kind": "variable", "doc": "

    \n", "annotation": ": TypeAlias", "default_value": "str | tuple[str | None, ...]"}, {"fullname": "main.askai.language.language.Language", "modulename": "main.askai.language.language", "qualname": "Language", "kind": "class", "doc": "

    Enumeration to wrap all standard languages.\nReference: https://docs.oracle.com/cd/E23824_01/html/E26033/glset.html

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.language.language.Language.AF_ZA", "modulename": "main.askai.language.language", "qualname": "Language.AF_ZA", "kind": "variable", "doc": "

    \n", "default_value": "Afrikaans"}, {"fullname": "main.askai.language.language.Language.AR_AE", "modulename": "main.askai.language.language", "qualname": "Language.AR_AE", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_BH", "modulename": "main.askai.language.language", "qualname": "Language.AR_BH", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_DZ", "modulename": "main.askai.language.language", "qualname": "Language.AR_DZ", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_EG", "modulename": "main.askai.language.language", "qualname": "Language.AR_EG", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_IQ", "modulename": "main.askai.language.language", "qualname": "Language.AR_IQ", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_JO", "modulename": "main.askai.language.language", "qualname": "Language.AR_JO", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_KW", "modulename": "main.askai.language.language", "qualname": "Language.AR_KW", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_LY", "modulename": "main.askai.language.language", "qualname": "Language.AR_LY", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_MA", "modulename": "main.askai.language.language", "qualname": "Language.AR_MA", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_OM", "modulename": "main.askai.language.language", "qualname": "Language.AR_OM", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_QA", "modulename": "main.askai.language.language", "qualname": "Language.AR_QA", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_SA", "modulename": "main.askai.language.language", "qualname": "Language.AR_SA", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_TN", "modulename": "main.askai.language.language", "qualname": "Language.AR_TN", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AR_YE", "modulename": "main.askai.language.language", "qualname": "Language.AR_YE", "kind": "variable", "doc": "

    \n", "default_value": "Arabic"}, {"fullname": "main.askai.language.language.Language.AS_IN", "modulename": "main.askai.language.language", "qualname": "Language.AS_IN", "kind": "variable", "doc": "

    \n", "default_value": "Assamese"}, {"fullname": "main.askai.language.language.Language.AZ_AZ", "modulename": "main.askai.language.language", "qualname": "Language.AZ_AZ", "kind": "variable", "doc": "

    \n", "default_value": "Azerbaijani"}, {"fullname": "main.askai.language.language.Language.BE_BY", "modulename": "main.askai.language.language", "qualname": "Language.BE_BY", "kind": "variable", "doc": "

    \n", "default_value": "Belarusian"}, {"fullname": "main.askai.language.language.Language.BG_BG", "modulename": "main.askai.language.language", "qualname": "Language.BG_BG", "kind": "variable", "doc": "

    \n", "default_value": "Bulgarian"}, {"fullname": "main.askai.language.language.Language.BN_IN", "modulename": "main.askai.language.language", "qualname": "Language.BN_IN", "kind": "variable", "doc": "

    \n", "default_value": "Bengali"}, {"fullname": "main.askai.language.language.Language.BS_BA", "modulename": "main.askai.language.language", "qualname": "Language.BS_BA", "kind": "variable", "doc": "

    \n", "default_value": "Bosnian"}, {"fullname": "main.askai.language.language.Language.CA_ES", "modulename": "main.askai.language.language", "qualname": "Language.CA_ES", "kind": "variable", "doc": "

    \n", "default_value": "Catalan"}, {"fullname": "main.askai.language.language.Language.CS_CZ", "modulename": "main.askai.language.language", "qualname": "Language.CS_CZ", "kind": "variable", "doc": "

    \n", "default_value": "Czech"}, {"fullname": "main.askai.language.language.Language.DA_DK", "modulename": "main.askai.language.language", "qualname": "Language.DA_DK", "kind": "variable", "doc": "

    \n", "default_value": "Danish"}, {"fullname": "main.askai.language.language.Language.DE_AT", "modulename": "main.askai.language.language", "qualname": "Language.DE_AT", "kind": "variable", "doc": "

    \n", "default_value": "German"}, {"fullname": "main.askai.language.language.Language.DE_BE", "modulename": "main.askai.language.language", "qualname": "Language.DE_BE", "kind": "variable", "doc": "

    \n", "default_value": "German"}, {"fullname": "main.askai.language.language.Language.DE_CH", "modulename": "main.askai.language.language", "qualname": "Language.DE_CH", "kind": "variable", "doc": "

    \n", "default_value": "German"}, {"fullname": "main.askai.language.language.Language.DE_DE", "modulename": "main.askai.language.language", "qualname": "Language.DE_DE", "kind": "variable", "doc": "

    \n", "default_value": "German"}, {"fullname": "main.askai.language.language.Language.DE_LI", "modulename": "main.askai.language.language", "qualname": "Language.DE_LI", "kind": "variable", "doc": "

    \n", "default_value": "German"}, {"fullname": "main.askai.language.language.Language.DE_LU", "modulename": "main.askai.language.language", "qualname": "Language.DE_LU", "kind": "variable", "doc": "

    \n", "default_value": "German"}, {"fullname": "main.askai.language.language.Language.EL_CY", "modulename": "main.askai.language.language", "qualname": "Language.EL_CY", "kind": "variable", "doc": "

    \n", "default_value": "Greek"}, {"fullname": "main.askai.language.language.Language.EL_GR", "modulename": "main.askai.language.language", "qualname": "Language.EL_GR", "kind": "variable", "doc": "

    \n", "default_value": "Greek"}, {"fullname": "main.askai.language.language.Language.EN_AU", "modulename": "main.askai.language.language", "qualname": "Language.EN_AU", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_BW", "modulename": "main.askai.language.language", "qualname": "Language.EN_BW", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_CA", "modulename": "main.askai.language.language", "qualname": "Language.EN_CA", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_GB", "modulename": "main.askai.language.language", "qualname": "Language.EN_GB", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_HK", "modulename": "main.askai.language.language", "qualname": "Language.EN_HK", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_IE", "modulename": "main.askai.language.language", "qualname": "Language.EN_IE", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_IN", "modulename": "main.askai.language.language", "qualname": "Language.EN_IN", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_MT", "modulename": "main.askai.language.language", "qualname": "Language.EN_MT", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_NZ", "modulename": "main.askai.language.language", "qualname": "Language.EN_NZ", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_PH", "modulename": "main.askai.language.language", "qualname": "Language.EN_PH", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_SG", "modulename": "main.askai.language.language", "qualname": "Language.EN_SG", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_US", "modulename": "main.askai.language.language", "qualname": "Language.EN_US", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.EN_ZW", "modulename": "main.askai.language.language", "qualname": "Language.EN_ZW", "kind": "variable", "doc": "

    \n", "default_value": "English"}, {"fullname": "main.askai.language.language.Language.ES_AR", "modulename": "main.askai.language.language", "qualname": "Language.ES_AR", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_BO", "modulename": "main.askai.language.language", "qualname": "Language.ES_BO", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_CL", "modulename": "main.askai.language.language", "qualname": "Language.ES_CL", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_CO", "modulename": "main.askai.language.language", "qualname": "Language.ES_CO", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_CR", "modulename": "main.askai.language.language", "qualname": "Language.ES_CR", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_DO", "modulename": "main.askai.language.language", "qualname": "Language.ES_DO", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_EC", "modulename": "main.askai.language.language", "qualname": "Language.ES_EC", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_ES", "modulename": "main.askai.language.language", "qualname": "Language.ES_ES", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_GT", "modulename": "main.askai.language.language", "qualname": "Language.ES_GT", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_HN", "modulename": "main.askai.language.language", "qualname": "Language.ES_HN", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_MX", "modulename": "main.askai.language.language", "qualname": "Language.ES_MX", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_NI", "modulename": "main.askai.language.language", "qualname": "Language.ES_NI", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_PA", "modulename": "main.askai.language.language", "qualname": "Language.ES_PA", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_PE", "modulename": "main.askai.language.language", "qualname": "Language.ES_PE", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_PR", "modulename": "main.askai.language.language", "qualname": "Language.ES_PR", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_PY", "modulename": "main.askai.language.language", "qualname": "Language.ES_PY", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_SV", "modulename": "main.askai.language.language", "qualname": "Language.ES_SV", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_US", "modulename": "main.askai.language.language", "qualname": "Language.ES_US", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_UY", "modulename": "main.askai.language.language", "qualname": "Language.ES_UY", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ES_VE", "modulename": "main.askai.language.language", "qualname": "Language.ES_VE", "kind": "variable", "doc": "

    \n", "default_value": "Spanish"}, {"fullname": "main.askai.language.language.Language.ET_EE", "modulename": "main.askai.language.language", "qualname": "Language.ET_EE", "kind": "variable", "doc": "

    \n", "default_value": "Estonian"}, {"fullname": "main.askai.language.language.Language.FI_FI", "modulename": "main.askai.language.language", "qualname": "Language.FI_FI", "kind": "variable", "doc": "

    \n", "default_value": "Finnish"}, {"fullname": "main.askai.language.language.Language.FR_BE", "modulename": "main.askai.language.language", "qualname": "Language.FR_BE", "kind": "variable", "doc": "

    \n", "default_value": "French"}, {"fullname": "main.askai.language.language.Language.FR_CA", "modulename": "main.askai.language.language", "qualname": "Language.FR_CA", "kind": "variable", "doc": "

    \n", "default_value": "French"}, {"fullname": "main.askai.language.language.Language.FR_CH", "modulename": "main.askai.language.language", "qualname": "Language.FR_CH", "kind": "variable", "doc": "

    \n", "default_value": "French"}, {"fullname": "main.askai.language.language.Language.FR_FR", "modulename": "main.askai.language.language", "qualname": "Language.FR_FR", "kind": "variable", "doc": "

    \n", "default_value": "French"}, {"fullname": "main.askai.language.language.Language.FR_LU", "modulename": "main.askai.language.language", "qualname": "Language.FR_LU", "kind": "variable", "doc": "

    \n", "default_value": "French"}, {"fullname": "main.askai.language.language.Language.GU_IN", "modulename": "main.askai.language.language", "qualname": "Language.GU_IN", "kind": "variable", "doc": "

    \n", "default_value": "Gujarati"}, {"fullname": "main.askai.language.language.Language.HE_IL", "modulename": "main.askai.language.language", "qualname": "Language.HE_IL", "kind": "variable", "doc": "

    \n", "default_value": "Hebrew"}, {"fullname": "main.askai.language.language.Language.HI_IN", "modulename": "main.askai.language.language", "qualname": "Language.HI_IN", "kind": "variable", "doc": "

    \n", "default_value": "Hindi"}, {"fullname": "main.askai.language.language.Language.HR_HR", "modulename": "main.askai.language.language", "qualname": "Language.HR_HR", "kind": "variable", "doc": "

    \n", "default_value": "Croatian"}, {"fullname": "main.askai.language.language.Language.HU_HU", "modulename": "main.askai.language.language", "qualname": "Language.HU_HU", "kind": "variable", "doc": "

    \n", "default_value": "Hungarian"}, {"fullname": "main.askai.language.language.Language.HY_AM", "modulename": "main.askai.language.language", "qualname": "Language.HY_AM", "kind": "variable", "doc": "

    \n", "default_value": "Armenian"}, {"fullname": "main.askai.language.language.Language.ID_ID", "modulename": "main.askai.language.language", "qualname": "Language.ID_ID", "kind": "variable", "doc": "

    \n", "default_value": "Indonesian"}, {"fullname": "main.askai.language.language.Language.IS_IS", "modulename": "main.askai.language.language", "qualname": "Language.IS_IS", "kind": "variable", "doc": "

    \n", "default_value": "Icelandic"}, {"fullname": "main.askai.language.language.Language.IT_CH", "modulename": "main.askai.language.language", "qualname": "Language.IT_CH", "kind": "variable", "doc": "

    \n", "default_value": "Italian"}, {"fullname": "main.askai.language.language.Language.IT_IT", "modulename": "main.askai.language.language", "qualname": "Language.IT_IT", "kind": "variable", "doc": "

    \n", "default_value": "Italian"}, {"fullname": "main.askai.language.language.Language.JA_JP", "modulename": "main.askai.language.language", "qualname": "Language.JA_JP", "kind": "variable", "doc": "

    \n", "default_value": "Japanese"}, {"fullname": "main.askai.language.language.Language.KA_GE", "modulename": "main.askai.language.language", "qualname": "Language.KA_GE", "kind": "variable", "doc": "

    \n", "default_value": "Georgian"}, {"fullname": "main.askai.language.language.Language.KK_KZ", "modulename": "main.askai.language.language", "qualname": "Language.KK_KZ", "kind": "variable", "doc": "

    \n", "default_value": "Kazakh"}, {"fullname": "main.askai.language.language.Language.KN_IN", "modulename": "main.askai.language.language", "qualname": "Language.KN_IN", "kind": "variable", "doc": "

    \n", "default_value": "Kannada"}, {"fullname": "main.askai.language.language.Language.KO_KR", "modulename": "main.askai.language.language", "qualname": "Language.KO_KR", "kind": "variable", "doc": "

    \n", "default_value": "Korean"}, {"fullname": "main.askai.language.language.Language.KS_IN", "modulename": "main.askai.language.language", "qualname": "Language.KS_IN", "kind": "variable", "doc": "

    \n", "default_value": "Kashmiri"}, {"fullname": "main.askai.language.language.Language.KU_TR", "modulename": "main.askai.language.language", "qualname": "Language.KU_TR", "kind": "variable", "doc": "

    \n", "default_value": "Kurdish"}, {"fullname": "main.askai.language.language.Language.KY_KG", "modulename": "main.askai.language.language", "qualname": "Language.KY_KG", "kind": "variable", "doc": "

    \n", "default_value": "Kirghiz"}, {"fullname": "main.askai.language.language.Language.LT_LT", "modulename": "main.askai.language.language", "qualname": "Language.LT_LT", "kind": "variable", "doc": "

    \n", "default_value": "Lithuanian"}, {"fullname": "main.askai.language.language.Language.LV_LV", "modulename": "main.askai.language.language", "qualname": "Language.LV_LV", "kind": "variable", "doc": "

    \n", "default_value": "Latvian"}, {"fullname": "main.askai.language.language.Language.MK_MK", "modulename": "main.askai.language.language", "qualname": "Language.MK_MK", "kind": "variable", "doc": "

    \n", "default_value": "Macedonian"}, {"fullname": "main.askai.language.language.Language.ML_IN", "modulename": "main.askai.language.language", "qualname": "Language.ML_IN", "kind": "variable", "doc": "

    \n", "default_value": "Malayalam"}, {"fullname": "main.askai.language.language.Language.MR_IN", "modulename": "main.askai.language.language", "qualname": "Language.MR_IN", "kind": "variable", "doc": "

    \n", "default_value": "Marathi"}, {"fullname": "main.askai.language.language.Language.MS_MY", "modulename": "main.askai.language.language", "qualname": "Language.MS_MY", "kind": "variable", "doc": "

    \n", "default_value": "Malay"}, {"fullname": "main.askai.language.language.Language.MT_MT", "modulename": "main.askai.language.language", "qualname": "Language.MT_MT", "kind": "variable", "doc": "

    \n", "default_value": "Maltese"}, {"fullname": "main.askai.language.language.Language.NB_NO", "modulename": "main.askai.language.language", "qualname": "Language.NB_NO", "kind": "variable", "doc": "

    \n", "default_value": "Bokmal"}, {"fullname": "main.askai.language.language.Language.NL_BE", "modulename": "main.askai.language.language", "qualname": "Language.NL_BE", "kind": "variable", "doc": "

    \n", "default_value": "Dutch"}, {"fullname": "main.askai.language.language.Language.NL_NL", "modulename": "main.askai.language.language", "qualname": "Language.NL_NL", "kind": "variable", "doc": "

    \n", "default_value": "Dutch"}, {"fullname": "main.askai.language.language.Language.NN_NO", "modulename": "main.askai.language.language", "qualname": "Language.NN_NO", "kind": "variable", "doc": "

    \n", "default_value": "Nynorsk"}, {"fullname": "main.askai.language.language.Language.OR_IN", "modulename": "main.askai.language.language", "qualname": "Language.OR_IN", "kind": "variable", "doc": "

    \n", "default_value": "Oriya"}, {"fullname": "main.askai.language.language.Language.PA_IN", "modulename": "main.askai.language.language", "qualname": "Language.PA_IN", "kind": "variable", "doc": "

    \n", "default_value": "Punjabi"}, {"fullname": "main.askai.language.language.Language.PL_PL", "modulename": "main.askai.language.language", "qualname": "Language.PL_PL", "kind": "variable", "doc": "

    \n", "default_value": "Polish"}, {"fullname": "main.askai.language.language.Language.PT_BR", "modulename": "main.askai.language.language", "qualname": "Language.PT_BR", "kind": "variable", "doc": "

    \n", "default_value": "Portuguese"}, {"fullname": "main.askai.language.language.Language.PT_PT", "modulename": "main.askai.language.language", "qualname": "Language.PT_PT", "kind": "variable", "doc": "

    \n", "default_value": "Portuguese"}, {"fullname": "main.askai.language.language.Language.RO_RO", "modulename": "main.askai.language.language", "qualname": "Language.RO_RO", "kind": "variable", "doc": "

    \n", "default_value": "Romanian"}, {"fullname": "main.askai.language.language.Language.RU_RU", "modulename": "main.askai.language.language", "qualname": "Language.RU_RU", "kind": "variable", "doc": "

    \n", "default_value": "Russian"}, {"fullname": "main.askai.language.language.Language.RU_UA", "modulename": "main.askai.language.language", "qualname": "Language.RU_UA", "kind": "variable", "doc": "

    \n", "default_value": "Russian"}, {"fullname": "main.askai.language.language.Language.SA_IN", "modulename": "main.askai.language.language", "qualname": "Language.SA_IN", "kind": "variable", "doc": "

    \n", "default_value": "Sanskrit"}, {"fullname": "main.askai.language.language.Language.SK_SK", "modulename": "main.askai.language.language", "qualname": "Language.SK_SK", "kind": "variable", "doc": "

    \n", "default_value": "Slovak"}, {"fullname": "main.askai.language.language.Language.SL_SI", "modulename": "main.askai.language.language", "qualname": "Language.SL_SI", "kind": "variable", "doc": "

    \n", "default_value": "Slovenian"}, {"fullname": "main.askai.language.language.Language.SQ_AL", "modulename": "main.askai.language.language", "qualname": "Language.SQ_AL", "kind": "variable", "doc": "

    \n", "default_value": "Albanian"}, {"fullname": "main.askai.language.language.Language.SR_ME", "modulename": "main.askai.language.language", "qualname": "Language.SR_ME", "kind": "variable", "doc": "

    \n", "default_value": "Serbian"}, {"fullname": "main.askai.language.language.Language.SR_RS", "modulename": "main.askai.language.language", "qualname": "Language.SR_RS", "kind": "variable", "doc": "

    \n", "default_value": "Serbian"}, {"fullname": "main.askai.language.language.Language.SV_SE", "modulename": "main.askai.language.language", "qualname": "Language.SV_SE", "kind": "variable", "doc": "

    \n", "default_value": "Swedish"}, {"fullname": "main.askai.language.language.Language.TA_IN", "modulename": "main.askai.language.language", "qualname": "Language.TA_IN", "kind": "variable", "doc": "

    \n", "default_value": "Tamil"}, {"fullname": "main.askai.language.language.Language.TE_IN", "modulename": "main.askai.language.language", "qualname": "Language.TE_IN", "kind": "variable", "doc": "

    \n", "default_value": "Telugu"}, {"fullname": "main.askai.language.language.Language.TH_TH", "modulename": "main.askai.language.language", "qualname": "Language.TH_TH", "kind": "variable", "doc": "

    \n", "default_value": "Thai"}, {"fullname": "main.askai.language.language.Language.TR_TR", "modulename": "main.askai.language.language", "qualname": "Language.TR_TR", "kind": "variable", "doc": "

    \n", "default_value": "Turkish"}, {"fullname": "main.askai.language.language.Language.UK_UA", "modulename": "main.askai.language.language", "qualname": "Language.UK_UA", "kind": "variable", "doc": "

    \n", "default_value": "Ukrainian"}, {"fullname": "main.askai.language.language.Language.VI_VN", "modulename": "main.askai.language.language", "qualname": "Language.VI_VN", "kind": "variable", "doc": "

    \n", "default_value": "Vietnamese"}, {"fullname": "main.askai.language.language.Language.ZH_CN", "modulename": "main.askai.language.language", "qualname": "Language.ZH_CN", "kind": "variable", "doc": "

    \n", "default_value": "Simplified Chinese"}, {"fullname": "main.askai.language.language.Language.ZH_HK", "modulename": "main.askai.language.language", "qualname": "Language.ZH_HK", "kind": "variable", "doc": "

    \n", "default_value": "Traditional Chinese"}, {"fullname": "main.askai.language.language.Language.ZH_SG", "modulename": "main.askai.language.language", "qualname": "Language.ZH_SG", "kind": "variable", "doc": "

    \n", "default_value": "Chinese"}, {"fullname": "main.askai.language.language.Language.ZH_TW", "modulename": "main.askai.language.language", "qualname": "Language.ZH_TW", "kind": "variable", "doc": "

    \n", "default_value": "Traditional Chinese"}, {"fullname": "main.askai.language.language.Language.of_locale", "modulename": "main.askai.language.language", "qualname": "Language.of_locale", "kind": "function", "doc": "

    Create a Language object based on a locale string or tuple containing the language code and encoding.

    \n\n
    Parameters
    \n\n
      \n
    • loc: The locale to parse.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A Language instance corresponding to the provided locale.

    \n
    \n", "signature": "(\tloc: str | tuple[str | None, ...]) -> main.askai.language.language.Language:", "funcdef": "def"}, {"fullname": "main.askai.language.language.Language.locale", "modulename": "main.askai.language.language", "qualname": "Language.locale", "kind": "variable", "doc": "

    Return a tuple containing the locale attributes.

    \n\n
    Returns
    \n\n
    \n

    A tuple with locale attributes, e.g., (en_US, utf-8).

    \n
    \n", "annotation": ": tuple[str, hspylib.core.enums.charset.Charset]"}, {"fullname": "main.askai.language.language.Language.idiom", "modulename": "main.askai.language.language", "qualname": "Language.idiom", "kind": "variable", "doc": "

    Return a string representing the idiom.

    \n\n
    Returns
    \n\n
    \n

    The idiom as a string, e.g., en_US.

    \n
    \n", "annotation": ": str"}, {"fullname": "main.askai.language.language.Language.encoding", "modulename": "main.askai.language.language", "qualname": "Language.encoding", "kind": "variable", "doc": "

    Return the charset (encoding) required for the language to be properly displayed.

    \n\n
    Returns
    \n\n
    \n

    The charset (encoding), e.g., utf-8.

    \n
    \n", "annotation": ": hspylib.core.enums.charset.Charset"}, {"fullname": "main.askai.language.language.Language.name", "modulename": "main.askai.language.language", "qualname": "Language.name", "kind": "variable", "doc": "

    Return the language name.

    \n\n
    Returns
    \n\n
    \n

    The name of the language, e.g., English.

    \n
    \n", "annotation": ": str"}, {"fullname": "main.askai.language.language.Language.country", "modulename": "main.askai.language.language", "qualname": "Language.country", "kind": "variable", "doc": "

    Return the country where the language is spoken.

    \n\n
    Returns
    \n\n
    \n

    The country where the language is spoken, e.g., U.S.A.

    \n
    \n", "annotation": ": str"}, {"fullname": "main.askai.language.language.Language.language", "modulename": "main.askai.language.language", "qualname": "Language.language", "kind": "variable", "doc": "

    Return a mnemonic representing the language.

    \n\n
    Returns
    \n\n
    \n

    The mnemonic representing the language, e.g., en.

    \n
    \n", "annotation": ": str"}, {"fullname": "main.askai.language.language.Language.territory", "modulename": "main.askai.language.language", "qualname": "Language.territory", "kind": "variable", "doc": "

    Return a mnemonic representing the territory (Alpha-2 code).

    \n\n
    Returns
    \n\n
    \n

    The mnemonic representing the territory, e.g., US.

    \n
    \n", "annotation": ": str"}, {"fullname": "main.askai.language.translators", "modulename": "main.askai.language.translators", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.language.translators.argos_translator", "modulename": "main.askai.language.translators.argos_translator", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.language.argos_translator\n @file: argos_translator.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.language.translators.argos_translator.ArgosTranslator", "modulename": "main.askai.language.translators.argos_translator", "qualname": "ArgosTranslator", "kind": "class", "doc": "

    Provides a multilingual offline translation engine using ARGOS translate.\nReference: https://github.com/argosopentech/argos-translate\nLanguage packages are downloaded at: ~/.local/share/argos-translate/packages

    \n", "bases": "askai.language.ai_translator.AITranslator"}, {"fullname": "main.askai.language.translators.argos_translator.ArgosTranslator.__init__", "modulename": "main.askai.language.translators.argos_translator", "qualname": "ArgosTranslator.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tfrom_idiom: askai.language.language.Language,\tto_idiom: askai.language.language.Language)"}, {"fullname": "main.askai.language.translators.argos_translator.ArgosTranslator.translate", "modulename": "main.askai.language.translators.argos_translator", "qualname": "ArgosTranslator.translate", "kind": "function", "doc": "

    Translate text from the source language to the target language.

    \n\n
    Parameters
    \n\n
      \n
    • text: Text to translate.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The translated text.

    \n
    \n", "signature": "(self, text: str, **kwargs) -> str:", "funcdef": "def"}, {"fullname": "main.askai.language.translators.argos_translator.ArgosTranslator.name", "modulename": "main.askai.language.translators.argos_translator", "qualname": "ArgosTranslator.name", "kind": "function", "doc": "

    Return the translator name or model.

    \n\n
    Returns
    \n\n
    \n

    The name or model of the translator.

    \n
    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.language.translators.deepl_translator", "modulename": "main.askai.language.translators.deepl_translator", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.language.argos_translator\n @file: argos_translator.py\n@created: Fri, 5 Jan 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.language.translators.deepl_translator.DeepLTranslator", "modulename": "main.askai.language.translators.deepl_translator", "qualname": "DeepLTranslator", "kind": "class", "doc": "

    Provides a multilingual online translation engine using DeepL translator.\nReference: https://hnd.www.deepl.com/en/translator

    \n", "bases": "askai.language.ai_translator.AITranslator"}, {"fullname": "main.askai.language.translators.deepl_translator.DeepLTranslator.__init__", "modulename": "main.askai.language.translators.deepl_translator", "qualname": "DeepLTranslator.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tsource_lang: askai.language.language.Language,\ttarget_lang: askai.language.language.Language)"}, {"fullname": "main.askai.language.translators.deepl_translator.DeepLTranslator.translate", "modulename": "main.askai.language.translators.deepl_translator", "qualname": "DeepLTranslator.translate", "kind": "function", "doc": "

    Translate text from the source language to the target language.

    \n\n
    Parameters
    \n\n
      \n
    • text: Text to translate.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The translated text.

    \n
    \n", "signature": "(self, text: str, **kwargs) -> str:", "funcdef": "def"}, {"fullname": "main.askai.language.translators.deepl_translator.DeepLTranslator.name", "modulename": "main.askai.language.translators.deepl_translator", "qualname": "DeepLTranslator.name", "kind": "function", "doc": "

    Return the translator name or model.

    \n\n
    Returns
    \n\n
    \n

    The name or model of the translator.

    \n
    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.language.translators.marian_translator", "modulename": "main.askai.language.translators.marian_translator", "kind": "module", "doc": "

    \n"}, {"fullname": "main.askai.language.translators.marian_translator.MarianTranslator", "modulename": "main.askai.language.translators.marian_translator", "qualname": "MarianTranslator", "kind": "class", "doc": "

    Provides a multilingual offline translation engine using Marian translator.\nReference: https://marian-nmt.github.io/

    \n", "bases": "askai.language.ai_translator.AITranslator"}, {"fullname": "main.askai.language.translators.marian_translator.MarianTranslator.__init__", "modulename": "main.askai.language.translators.marian_translator", "qualname": "MarianTranslator.__init__", "kind": "function", "doc": "

    \n", "signature": "(\tfrom_idiom: askai.language.language.Language,\tto_idiom: askai.language.language.Language)"}, {"fullname": "main.askai.language.translators.marian_translator.MarianTranslator.MODEL_NAME", "modulename": "main.askai.language.translators.marian_translator", "qualname": "MarianTranslator.MODEL_NAME", "kind": "variable", "doc": "

    \n", "default_value": "'Helsinki-NLP/opus-mt-en-ROMANCE'"}, {"fullname": "main.askai.language.translators.marian_translator.MarianTranslator.translate", "modulename": "main.askai.language.translators.marian_translator", "qualname": "MarianTranslator.translate", "kind": "function", "doc": "

    Translate text from the source language to the target language.

    \n\n
    Parameters
    \n\n
      \n
    • text: Text to translate.
    • \n
    \n\n
    Returns
    \n\n
    \n

    The translated text.

    \n
    \n", "signature": "(self, text: str, **kwargs) -> str:", "funcdef": "def"}, {"fullname": "main.askai.language.translators.marian_translator.MarianTranslator.name", "modulename": "main.askai.language.translators.marian_translator", "qualname": "MarianTranslator.name", "kind": "function", "doc": "

    Return the translator name or model.

    \n\n
    Returns
    \n\n
    \n

    The name or model of the translator.

    \n
    \n", "signature": "(self) -> str:", "funcdef": "def"}, {"fullname": "main.askai.tui", "modulename": "main.askai.tui", "kind": "module", "doc": "

    Package initialization.

    \n"}, {"fullname": "main.askai.tui.app_header", "modulename": "main.askai.tui.app_header", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.tui.header\n @file: app_header.py\n@created: Mon, 29 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.tui.app_header.Header", "modulename": "main.askai.tui.app_header", "qualname": "Header", "kind": "class", "doc": "

    A header widget with icon and notifications.

    \n", "bases": "textual.widget.Widget"}, {"fullname": "main.askai.tui.app_header.Header.__init__", "modulename": "main.askai.tui.app_header", "qualname": "Header.__init__", "kind": "function", "doc": "

    Initialize a Widget.

    \n\n

    Args:\n *children: Child widgets.\n name: The name of the widget.\n id: The ID of the widget in the DOM.\n classes: The CSS classes for the widget.\n disabled: Whether the widget is disabled or not.

    \n", "signature": "(**kwargs)"}, {"fullname": "main.askai.tui.app_header.Header.notifications", "modulename": "main.askai.tui.app_header", "qualname": "Header.notifications", "kind": "variable", "doc": "

    \n"}, {"fullname": "main.askai.tui.app_header.Header.screen_title", "modulename": "main.askai.tui.app_header", "qualname": "Header.screen_title", "kind": "variable", "doc": "

    The title that this header will display.

    \n", "annotation": ": str"}, {"fullname": "main.askai.tui.app_header.Header.screen_sub_title", "modulename": "main.askai.tui.app_header", "qualname": "Header.screen_sub_title", "kind": "variable", "doc": "

    The sub-title that this header will display.

    \n", "annotation": ": str"}, {"fullname": "main.askai.tui.app_header.Header.compose", "modulename": "main.askai.tui.app_header", "qualname": "Header.compose", "kind": "function", "doc": "

    Compose the Header Widget.

    \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "main.askai.tui.app_header.Header.can_focus", "modulename": "main.askai.tui.app_header", "qualname": "Header.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_header.Header.can_focus_children", "modulename": "main.askai.tui.app_header", "qualname": "Header.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.app_header.HeaderTitle", "modulename": "main.askai.tui.app_header", "qualname": "HeaderTitle", "kind": "class", "doc": "

    Display the title / subtitle in the header.

    \n", "bases": "textual.widget.Widget"}, {"fullname": "main.askai.tui.app_header.HeaderTitle.text", "modulename": "main.askai.tui.app_header", "qualname": "HeaderTitle.text", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderTitle.sub_text", "modulename": "main.askai.tui.app_header", "qualname": "HeaderTitle.sub_text", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderTitle.render", "modulename": "main.askai.tui.app_header", "qualname": "HeaderTitle.render", "kind": "function", "doc": "

    Render the title and sub-title.

    \n", "signature": "(\tself) -> Union[rich.console.ConsoleRenderable, rich.console.RichCast, str]:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_header.HeaderTitle.can_focus", "modulename": "main.askai.tui.app_header", "qualname": "HeaderTitle.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_header.HeaderTitle.can_focus_children", "modulename": "main.askai.tui.app_header", "qualname": "HeaderTitle.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications", "kind": "class", "doc": "

    Display a notification widget on the right of the header.

    \n", "bases": "textual.widget.Widget"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.__init__", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.__init__", "kind": "function", "doc": "

    Initialize a Widget.

    \n\n

    Args:\n *children: Child widgets.\n name: The name of the widget.\n id: The ID of the widget in the DOM.\n classes: The CSS classes for the widget.\n disabled: Whether the widget is disabled or not.

    \n", "signature": "()"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.speaking", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.speaking", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.debugging", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.debugging", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.caching", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.caching", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.listening", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.listening", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.headphones", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.headphones", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.idiom", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.idiom", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.tooltip", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.tooltip", "kind": "variable", "doc": "

    Tooltip for the widget, or None for no tooltip.

    \n", "annotation": ": Union[rich.console.ConsoleRenderable, rich.console.RichCast, str, NoneType]"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.render", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.render", "kind": "function", "doc": "

    Render the header notifications.

    \n", "signature": "(\tself) -> Union[rich.console.ConsoleRenderable, rich.console.RichCast, str]:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.refresh_icons", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.refresh_icons", "kind": "function", "doc": "

    Update the application widgets. This callback is required because ask_and_reply is async.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.watch_speaking", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.watch_speaking", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.watch_debugging", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.watch_debugging", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.watch_caching", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.watch_caching", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.watch_listening", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.watch_listening", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.watch_headphones", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.watch_headphones", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.watch_idiom", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.watch_idiom", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.can_focus", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_header.HeaderNotifications.can_focus_children", "modulename": "main.askai.tui.app_header", "qualname": "HeaderNotifications.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.app_icons", "modulename": "main.askai.tui.app_icons", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.tui.app_icons\n @file: app_icons.py\n@created: Mon, 29 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.tui.app_icons.AppIcons", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons", "kind": "class", "doc": "

    Enumerated icons of the new AskAI UI application.

    \n", "bases": "hspylib.core.enums.enumeration.Enumeration"}, {"fullname": "main.askai.tui.app_icons.AppIcons.DEFAULT", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.DEFAULT", "kind": "variable", "doc": "

    \n", "default_value": "DEFAULT"}, {"fullname": "main.askai.tui.app_icons.AppIcons.STARTED", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.STARTED", "kind": "variable", "doc": "

    \n", "default_value": "STARTED"}, {"fullname": "main.askai.tui.app_icons.AppIcons.MENU", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.MENU", "kind": "variable", "doc": "

    \n", "default_value": "MENU"}, {"fullname": "main.askai.tui.app_icons.AppIcons.TOC", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.TOC", "kind": "variable", "doc": "

    \n", "default_value": "TOC"}, {"fullname": "main.askai.tui.app_icons.AppIcons.EXIT", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.EXIT", "kind": "variable", "doc": "

    \n", "default_value": "EXIT"}, {"fullname": "main.askai.tui.app_icons.AppIcons.HELP", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.HELP", "kind": "variable", "doc": "

    \n", "default_value": "HELP"}, {"fullname": "main.askai.tui.app_icons.AppIcons.SETTINGS", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.SETTINGS", "kind": "variable", "doc": "

    \n", "default_value": "SETTINGS"}, {"fullname": "main.askai.tui.app_icons.AppIcons.INFO", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.INFO", "kind": "variable", "doc": "

    \n", "default_value": "INFO"}, {"fullname": "main.askai.tui.app_icons.AppIcons.CONSOLE", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.CONSOLE", "kind": "variable", "doc": "

    \n", "default_value": "CONSOLE"}, {"fullname": "main.askai.tui.app_icons.AppIcons.DEBUG_ON", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.DEBUG_ON", "kind": "variable", "doc": "

    \n", "default_value": "DEBUG_ON"}, {"fullname": "main.askai.tui.app_icons.AppIcons.DEBUG_OFF", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.DEBUG_OFF", "kind": "variable", "doc": "

    \n", "default_value": "DEBUG_OFF"}, {"fullname": "main.askai.tui.app_icons.AppIcons.SPEAKING_ON", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.SPEAKING_ON", "kind": "variable", "doc": "

    \n", "default_value": "SPEAKING_ON"}, {"fullname": "main.askai.tui.app_icons.AppIcons.SPEAKING_OFF", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.SPEAKING_OFF", "kind": "variable", "doc": "

    \n", "default_value": "SPEAKING_OFF"}, {"fullname": "main.askai.tui.app_icons.AppIcons.CACHING_ON", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.CACHING_ON", "kind": "variable", "doc": "

    \n", "default_value": "CACHING_ON"}, {"fullname": "main.askai.tui.app_icons.AppIcons.CACHING_OFF", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.CACHING_OFF", "kind": "variable", "doc": "

    \n", "default_value": "CACHING_OFF"}, {"fullname": "main.askai.tui.app_icons.AppIcons.SEPARATOR_V", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.SEPARATOR_V", "kind": "variable", "doc": "

    \n", "default_value": "SEPARATOR_V"}, {"fullname": "main.askai.tui.app_icons.AppIcons.SEPARATOR_H", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.SEPARATOR_H", "kind": "variable", "doc": "

    \n", "default_value": "SEPARATOR_H"}, {"fullname": "main.askai.tui.app_icons.AppIcons.LISTENING_ON", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.LISTENING_ON", "kind": "variable", "doc": "

    \n", "default_value": "LISTENING_ON"}, {"fullname": "main.askai.tui.app_icons.AppIcons.LISTENING_OFF", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.LISTENING_OFF", "kind": "variable", "doc": "

    \n", "default_value": "LISTENING_OFF"}, {"fullname": "main.askai.tui.app_icons.AppIcons.BUILT_IN_SPEAKER", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.BUILT_IN_SPEAKER", "kind": "variable", "doc": "

    \n", "default_value": "BUILT_IN_SPEAKER"}, {"fullname": "main.askai.tui.app_icons.AppIcons.HEADPHONES", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.HEADPHONES", "kind": "variable", "doc": "

    \n", "default_value": "HEADPHONES"}, {"fullname": "main.askai.tui.app_icons.AppIcons.CLOCK", "modulename": "main.askai.tui.app_icons", "qualname": "AppIcons.CLOCK", "kind": "variable", "doc": "

    \n", "default_value": "CLOCK"}, {"fullname": "main.askai.tui.app_suggester", "modulename": "main.askai.tui.app_suggester", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.tui.app_suggester\n @file: app_suggester.py\n@created: Wed, 19 Jun 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.tui.app_suggester.InputSuggester", "modulename": "main.askai.tui.app_suggester", "qualname": "InputSuggester", "kind": "class", "doc": "

    Implement a list-based Input suggester.

    \n", "bases": "textual.suggester.Suggester"}, {"fullname": "main.askai.tui.app_suggester.InputSuggester.__init__", "modulename": "main.askai.tui.app_suggester", "qualname": "InputSuggester.__init__", "kind": "function", "doc": "

    Create a suggester object.

    \n\n

    Args:\n use_cache: Whether to cache suggestion results.\n case_sensitive: Whether suggestions are case sensitive or not.\n If they are not, incoming values are casefolded before generating\n the suggestion.

    \n", "signature": "(*, case_sensitive: bool = True)"}, {"fullname": "main.askai.tui.app_suggester.InputSuggester.suggestions", "modulename": "main.askai.tui.app_suggester", "qualname": "InputSuggester.suggestions", "kind": "function", "doc": "

    Return all available suggestions.

    \n", "signature": "(self) -> list[str]:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_suggester.InputSuggester.add_suggestion", "modulename": "main.askai.tui.app_suggester", "qualname": "InputSuggester.add_suggestion", "kind": "function", "doc": "

    Add a new suggestion.

    \n", "signature": "(self, value: str) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_suggester.InputSuggester.get_suggestion", "modulename": "main.askai.tui.app_suggester", "qualname": "InputSuggester.get_suggestion", "kind": "function", "doc": "

    Get a suggestion from the list.

    \n", "signature": "(self, value: str) -> Optional[str]:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_widgets", "modulename": "main.askai.tui.app_widgets", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.tui.app_widgets\n @file: app_widgets.py\n@created: Mon, 29 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon", "kind": "class", "doc": "

    Display an 'icon' on the left of the header.

    \n", "bases": "textual.widget.Widget"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.__init__", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.__init__", "kind": "function", "doc": "

    Initialize a Widget.

    \n\n

    Args:\n *children: Child widgets.\n name: The name of the widget.\n id: The ID of the widget in the DOM.\n classes: The CSS classes for the widget.\n disabled: Whether the widget is disabled or not.

    \n", "signature": "(\tmenu_icon: str = '\\uf128',\ttooltip: str = None,\ton_click: Optional[Callable] = None,\t**kwargs)"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.menu_icon", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.menu_icon", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.click_cb", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.click_cb", "kind": "variable", "doc": "

    \n", "annotation": ": Callable"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.tooltip", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.tooltip", "kind": "variable", "doc": "

    Tooltip for the widget, or None for no tooltip.

    \n", "annotation": ": Union[rich.console.ConsoleRenderable, rich.console.RichCast, str, NoneType]"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.on_click", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.on_click", "kind": "function", "doc": "

    \n", "signature": "(self, event: textual.events.Click) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.render", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.render", "kind": "function", "doc": "

    Get text or Rich renderable for this widget.

    \n\n

    Implement this for custom widgets.

    \n\n

    Example:

    \n\n
    \n
    from textual.app import RenderableType\nfrom textual.widget import Widget\n\nclass CustomWidget(Widget):\n    def render(self) -> RenderableType:\n        return "Welcome to [bold red]Textual[/]!"\n
    \n
    \n
    \n\n

    Returns:\n Any renderable.

    \n", "signature": "(\tself) -> Union[rich.console.ConsoleRenderable, rich.console.RichCast, str]:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.can_focus", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_widgets.MenuIcon.can_focus_children", "modulename": "main.askai.tui.app_widgets", "qualname": "MenuIcon.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.app_widgets.Splash", "modulename": "main.askai.tui.app_widgets", "qualname": "Splash", "kind": "class", "doc": "

    Splash widget that extends Container.

    \n", "bases": "textual.containers.Container"}, {"fullname": "main.askai.tui.app_widgets.Splash.__init__", "modulename": "main.askai.tui.app_widgets", "qualname": "Splash.__init__", "kind": "function", "doc": "

    Initialize a Widget.

    \n\n

    Args:\n *children: Child widgets.\n name: The name of the widget.\n id: The ID of the widget in the DOM.\n classes: The CSS classes for the widget.\n disabled: Whether the widget is disabled or not.

    \n", "signature": "(splash_image: str)"}, {"fullname": "main.askai.tui.app_widgets.Splash.splash_image", "modulename": "main.askai.tui.app_widgets", "qualname": "Splash.splash_image", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n", "annotation": ": str"}, {"fullname": "main.askai.tui.app_widgets.Splash.compose", "modulename": "main.askai.tui.app_widgets", "qualname": "Splash.compose", "kind": "function", "doc": "

    Called by Textual to create child widgets.

    \n\n

    This method is called when a widget is mounted or by setting recompose=True when\ncalling [refresh()][textual.widget.Widget.refresh].

    \n\n

    Note that you don't typically need to explicitly call this method.

    \n\n

    Example:

    \n\n
    \n
    def compose(self) -> ComposeResult:\n    yield Header()\n    yield Label("Press the button below:")\n    yield Button()\n    yield Footer()\n
    \n
    \n
    \n", "signature": "(self) -> Iterable[textual.widget.Widget]:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_widgets.Splash.on_mount", "modulename": "main.askai.tui.app_widgets", "qualname": "Splash.on_mount", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_widgets.Splash.can_focus", "modulename": "main.askai.tui.app_widgets", "qualname": "Splash.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_widgets.Splash.can_focus_children", "modulename": "main.askai.tui.app_widgets", "qualname": "Splash.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.app_widgets.AppHelp", "modulename": "main.askai.tui.app_widgets", "qualname": "AppHelp", "kind": "class", "doc": "

    Application Help Widget.

    \n", "bases": "textual.widgets._markdown.Markdown"}, {"fullname": "main.askai.tui.app_widgets.AppHelp.__init__", "modulename": "main.askai.tui.app_widgets", "qualname": "AppHelp.__init__", "kind": "function", "doc": "

    A Markdown widget.

    \n\n

    Args:\n markdown: String containing Markdown or None to leave blank for now.\n name: The name of the widget.\n id: The ID of the widget in the DOM.\n classes: The CSS classes of the widget.\n parser_factory: A factory function to return a configured MarkdownIt instance. If None, a \"gfm-like\" parser is used.

    \n", "signature": "(help_text: str)"}, {"fullname": "main.askai.tui.app_widgets.AppHelp.help_text", "modulename": "main.askai.tui.app_widgets", "qualname": "AppHelp.help_text", "kind": "variable", "doc": "

    \n", "annotation": ": str"}, {"fullname": "main.askai.tui.app_widgets.AppHelp.on_mount", "modulename": "main.askai.tui.app_widgets", "qualname": "AppHelp.on_mount", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_widgets.AppHelp.can_focus", "modulename": "main.askai.tui.app_widgets", "qualname": "AppHelp.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_widgets.AppHelp.can_focus_children", "modulename": "main.askai.tui.app_widgets", "qualname": "AppHelp.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.app_widgets.AppInfo", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo", "kind": "class", "doc": "

    Application Information Widget.

    \n", "bases": "textual.widgets._static.Static"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.__init__", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.__init__", "kind": "function", "doc": "

    Initialize a Widget.

    \n\n

    Args:\n *children: Child widgets.\n name: The name of the widget.\n id: The ID of the widget in the DOM.\n classes: The CSS classes for the widget.\n disabled: Whether the widget is disabled or not.

    \n", "signature": "(app_info: str)"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.info_text", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.info_text", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.credits", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.credits", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'\\n Author: \\uf486 Hugo Saporetti Junior\\n GitHub: \\uf408 https://github.com/yorevs/askai\\nLinkedIn: \\uf83a https://www.linkedin.com/in/yorevs/\\n Demo: \\uf16a https://www.youtube.com/watch?v=ZlVOisiUEvs&t=69s\\n--------------------------------------------------------------------------------\\n\\uf004 Thanks for using AskAI \\uf004\\n'"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.info", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.info", "kind": "variable", "doc": "

    \n", "annotation": ": textual.widgets._static.Static"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.compose", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.compose", "kind": "function", "doc": "

    Called by Textual to create child widgets.

    \n\n

    This method is called when a widget is mounted or by setting recompose=True when\ncalling [refresh()][textual.widget.Widget.refresh].

    \n\n

    Note that you don't typically need to explicitly call this method.

    \n\n

    Example:

    \n\n
    \n
    def compose(self) -> ComposeResult:\n    yield Header()\n    yield Label("Press the button below:")\n    yield Button()\n    yield Footer()\n
    \n
    \n
    \n", "signature": "(self) -> Iterable[textual.widget.Widget]:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.on_mount", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.on_mount", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.watch_info_text", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.watch_info_text", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.can_focus", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_widgets.AppInfo.can_focus_children", "modulename": "main.askai.tui.app_widgets", "qualname": "AppInfo.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.app_widgets.AppSettings", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings", "kind": "class", "doc": "

    Application DataTable Widget.

    \n", "bases": "textual.widgets._static.Static"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.__init__", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.__init__", "kind": "function", "doc": "

    Initialize a Widget.

    \n\n

    Args:\n *children: Child widgets.\n name: The name of the widget.\n id: The ID of the widget in the DOM.\n classes: The CSS classes for the widget.\n disabled: Whether the widget is disabled or not.

    \n", "signature": "(data: list[tuple[str, ...]] = None)"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.data", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.data", "kind": "variable", "doc": "

    Reactive descriptor.

    \n\n

    Args:\n default: A default value or callable that returns a default.\n layout: Perform a layout on change.\n repaint: Perform a repaint on change.\n init: Call watchers on initialize (post mount).\n always_update: Call watchers even when the new value equals the old value.\n compute: Run compute methods when attribute is changed.\n recompose: Compose the widget again when the attribute changes.\n bindings: Refresh bindings when the reactive changes.

    \n"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.table", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.table", "kind": "variable", "doc": "

    \n"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.compose", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.compose", "kind": "function", "doc": "

    Called by Textual to create child widgets.

    \n\n

    This method is called when a widget is mounted or by setting recompose=True when\ncalling [refresh()][textual.widget.Widget.refresh].

    \n\n

    Note that you don't typically need to explicitly call this method.

    \n\n

    Example:

    \n\n
    \n
    def compose(self) -> ComposeResult:\n    yield Header()\n    yield Label("Press the button below:")\n    yield Button()\n    yield Footer()\n
    \n
    \n
    \n", "signature": "(self) -> Iterable[textual.widget.Widget]:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.on_mount", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.on_mount", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.watch_data", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.watch_data", "kind": "function", "doc": "

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.can_focus", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.can_focus", "kind": "variable", "doc": "

    Widget may receive focus.

    \n", "annotation": ": bool", "default_value": "False"}, {"fullname": "main.askai.tui.app_widgets.AppSettings.can_focus_children", "modulename": "main.askai.tui.app_widgets", "qualname": "AppSettings.can_focus_children", "kind": "variable", "doc": "

    Widget's children may receive focus.

    \n", "annotation": ": bool", "default_value": "True"}, {"fullname": "main.askai.tui.askai_app", "modulename": "main.askai.tui.askai_app", "kind": "module", "doc": "

    @project: HsPyLib-AskAI\n@package: askai.tui.askai_app\n @file: askai_app.py\n@created: Mon, 29 Apr 2024\n @author: Hugo Saporetti Junior\n @site: https://github.com/yorevs/askai\n@license: MIT - Please refer to https://opensource.org/licenses/MIT

    \n\n

    Copyright (c) 2024, HomeSetup

    \n"}, {"fullname": "main.askai.tui.askai_app.SOURCE_DIR", "modulename": "main.askai.tui.askai_app", "qualname": "SOURCE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai')"}, {"fullname": "main.askai.tui.askai_app.RESOURCE_DIR", "modulename": "main.askai.tui.askai_app", "qualname": "RESOURCE_DIR", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path", "default_value": "PosixPath('/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources')"}, {"fullname": "main.askai.tui.askai_app.AskAiApp", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp", "kind": "class", "doc": "

    The AskAI Textual application.

    \n", "bases": "textual.app.App[NoneType]"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.__init__", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.__init__", "kind": "function", "doc": "

    Create an instance of an app.

    \n\n

    Args:\n driver_class: Driver class or None to auto-detect.\n This will be used by some Textual tools.\n css_path: Path to CSS or None to use the CSS_PATH class variable.\n To load multiple CSS files, pass a list of strings or paths which\n will be loaded in order.\n watch_css: Reload CSS if the files changed. This is set automatically if\n you are using textual run with the dev switch.

    \n\n

    Raises:\n CssPathError: When the supplied CSS path(s) are an unexpected type.

    \n", "signature": "(\tspeak: bool,\tdebug: bool,\tcacheable: bool,\ttempo: int,\tengine_name: str,\tmodel_name: str)"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.APP_TITLE", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.APP_TITLE", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "'AskAI v1.0.13'"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.CSS_PATH", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.CSS_PATH", "kind": "variable", "doc": "

    File paths to load CSS from.

    \n", "default_value": "'/Users/hjunior/GIT-Repository/GitHub/askai/src/main/askai/resources/askai.tcss'"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.BINDINGS", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.BINDINGS", "kind": "variable", "doc": "

    The default key bindings.

    \n", "default_value": "[('q', 'quit', ' Quit'), ('c', 'clear', ' Clear'), ('d', 'debugging', ' Debugging'), ('s', 'speaking', ' Speaking'), ('ctrl+l', 'ptt', ' Push-to-Talk')]"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.ENABLE_COMMAND_PALETTE", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.ENABLE_COMMAND_PALETTE", "kind": "variable", "doc": "

    Should the [command palette][textual.command.CommandPalette] be enabled for the application?

    \n", "default_value": "False"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.askai", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.askai", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.askai.AskAi"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.engine", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.engine", "kind": "variable", "doc": "

    \n", "annotation": ": askai.core.engine.ai_engine.AIEngine"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.app_settings", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.app_settings", "kind": "variable", "doc": "

    \n", "annotation": ": list[tuple[str, ...]]"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.console_path", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.console_path", "kind": "variable", "doc": "

    \n", "annotation": ": pathlib.Path"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.md_console", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.md_console", "kind": "variable", "doc": "

    Get the MarkdownViewer widget.

    \n", "annotation": ": textual.widgets._markdown.MarkdownViewer"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.info", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.info", "kind": "variable", "doc": "

    Get the AppInfo widget.

    \n", "annotation": ": askai.tui.app_widgets.AppInfo"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.help", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.help", "kind": "variable", "doc": "

    Get the AppHelp widget.

    \n", "annotation": ": askai.tui.app_widgets.AppHelp"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.settings", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.settings", "kind": "variable", "doc": "

    Get the AppSettings widget.

    \n", "annotation": ": askai.tui.app_widgets.AppSettings"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.splash", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.splash", "kind": "variable", "doc": "

    Get the Splash widget.

    \n", "annotation": ": askai.tui.app_widgets.Splash"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.line_input", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.line_input", "kind": "variable", "doc": "

    Get the Input widget.

    \n", "annotation": ": textual.widgets._input.Input"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.suggester", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.suggester", "kind": "variable", "doc": "

    Get the Input Suggester.

    \n", "annotation": ": Optional[askai.tui.app_suggester.InputSuggester]"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.header", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.header", "kind": "variable", "doc": "

    Get the Input widget.

    \n", "annotation": ": askai.tui.app_header.Header"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.footer", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.footer", "kind": "variable", "doc": "

    Get the Input widget.

    \n", "annotation": ": textual.widgets._footer.Footer"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.compose", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.compose", "kind": "function", "doc": "

    Called to add widgets to the app.

    \n", "signature": "(self) -> Iterable[textual.widget.Widget]:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.on_mount", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.on_mount", "kind": "function", "doc": "

    Called application is mounted.

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.on_markdown_viewer_navigator_updated", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.on_markdown_viewer_navigator_updated", "kind": "function", "doc": "

    Refresh bindings for forward / back when the document changes.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.action_back", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.action_back", "kind": "function", "doc": "

    Navigate backwards.

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.action_forward", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.action_forward", "kind": "function", "doc": "

    Navigate forwards.

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.action_toggle_table_of_contents", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.action_toggle_table_of_contents", "kind": "function", "doc": "

    Toggles display of the table of contents.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.check_action", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.check_action", "kind": "function", "doc": "

    Check if a specific action can be performed.

    \n\n
    Parameters
    \n\n
      \n
    • action: The name of the action to check.
    • \n
    • _: Unused parameter, typically a placeholder for additional context.
    • \n
    \n\n
    Returns
    \n\n
    \n

    True if the action can be performed, None if it cannot.

    \n
    \n", "signature": "(self, action: str, _) -> Optional[bool]:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.enable_controls", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.enable_controls", "kind": "function", "doc": "

    Enable or disable all UI controls, including the header, input, and footer.

    \n\n
    Parameters
    \n\n
      \n
    • enable: Whether to enable (True) or disable (False) the UI controls (default is True).
    • \n
    \n", "signature": "(self, enable: bool = True) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.activate_markdown", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.activate_markdown", "kind": "function", "doc": "

    Activate the markdown console widget.

    \n", "signature": "(self) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.action_clear", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.action_clear", "kind": "function", "doc": "

    Clear the output console.

    \n\n
    Parameters
    \n\n
      \n
    • overwrite: Whether to overwrite the existing content in the console (default is True).
    • \n
    \n", "signature": "(self, overwrite: bool = True) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.action_speaking", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.action_speaking", "kind": "function", "doc": "

    Toggle Speaking ON/OFF.

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.action_debugging", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.action_debugging", "kind": "function", "doc": "

    Toggle Debugging ON/OFF.

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.action_ptt", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.action_ptt", "kind": "function", "doc": "

    Handle the Push-To-Talk (PTT) action for Speech-To-Text (STT) input. This method allows the user to use\nPush-To-Talk as an input method, converting spoken words into text.

    \n", "signature": "(self) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.on_submit", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.on_submit", "kind": "function", "doc": "

    A coroutine to handle input submission events.

    \n\n
    Parameters
    \n\n
      \n
    • submitted: The event that contains the submitted data.
    • \n
    \n", "signature": "(self, submitted: textual.widgets._input.Input.Submitted) -> None:", "funcdef": "async def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.display_text", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.display_text", "kind": "function", "doc": "

    Send the text to the Markdown console.

    \n\n
    Parameters
    \n\n
      \n
    • markdown_text: the text to be displayed.
    • \n
    \n", "signature": "(self, markdown_text: str) -> None:", "funcdef": "def"}, {"fullname": "main.askai.tui.askai_app.AskAiApp.ask_and_reply", "modulename": "main.askai.tui.askai_app", "qualname": "AskAiApp.ask_and_reply", "kind": "function", "doc": "

    Ask the specified question to the AI and provide the reply.

    \n\n
    Parameters
    \n\n
      \n
    • question: The question to ask the AI engine.
    • \n
    \n\n
    Returns
    \n\n
    \n

    A tuple containing a boolean indicating success or failure, and the AI's reply as an optional string.

    \n
    \n", "signature": "(self, question: str) -> tuple[bool, typing.Optional[str]]:", "funcdef": "def"}]; // mirrored in build-search-index.js (part 1) // Also split on html tags. this is a cheap heuristic, but good enough. diff --git a/src/demo/__init__.py b/src/demo/__init__.py index e650c81f..77b8b226 100644 --- a/src/demo/__init__.py +++ b/src/demo/__init__.py @@ -5,5 +5,10 @@ # Package: demo """Package initialization.""" -__all__ = ["components", "features", "others", "utils"] -__version__ = "1.0.13" +__all__ = [ + 'components', + 'features', + 'others', + 'utils' +] +__version__ = '1.0.13' diff --git a/src/demo/components/__init__.py b/src/demo/components/__init__.py index b682078f..0e2666a8 100644 --- a/src/demo/components/__init__.py +++ b/src/demo/components/__init__.py @@ -5,5 +5,13 @@ # Package: demo.components """Package initialization.""" -__all__ = ["camera_demo", "internet_demo", "summarizer_demo", "vision_demo", "webcam_demo"] -__version__ = "1.0.13" +__all__ = [ + 'camera_demo', + 'internet_demo', + 'scheduler_demo', + 'summarizer_demo', + 'text_streamer_demo', + 'vision_demo', + 'webcam_demo' +] +__version__ = '1.0.13' diff --git a/src/demo/features/__init__.py b/src/demo/features/__init__.py index f1e4a74d..9d71cda8 100644 --- a/src/demo/features/__init__.py +++ b/src/demo/features/__init__.py @@ -5,5 +5,9 @@ # Package: demo.features """Package initialization.""" -__all__ = ["rag", "router_demo", "tools"] -__version__ = "1.0.13" +__all__ = [ + 'rag', + 'router_demo', + 'tools' +] +__version__ = '1.0.13' diff --git a/src/demo/features/rag/__init__.py b/src/demo/features/rag/__init__.py index 8743c9cb..7c4a72cc 100644 --- a/src/demo/features/rag/__init__.py +++ b/src/demo/features/rag/__init__.py @@ -5,5 +5,7 @@ # Package: demo.features.rag """Package initialization.""" -__all__ = ["x_refs_demo"] -__version__ = "1.0.13" +__all__ = [ + 'x_refs_demo' +] +__version__ = '1.0.13' diff --git a/src/demo/features/tools/__init__.py b/src/demo/features/tools/__init__.py index 0ebcd82b..4327c29b 100644 --- a/src/demo/features/tools/__init__.py +++ b/src/demo/features/tools/__init__.py @@ -5,5 +5,7 @@ # Package: demo.features.tools """Package initialization.""" -__all__ = ["query_output_demo"] -__version__ = "1.0.13" +__all__ = [ + 'query_output_demo' +] +__version__ = '1.0.13' diff --git a/src/demo/others/__init__.py b/src/demo/others/__init__.py index 9ab80d0c..f8c05b6d 100644 --- a/src/demo/others/__init__.py +++ b/src/demo/others/__init__.py @@ -5,5 +5,12 @@ # Package: demo.others """Package initialization.""" -__all__ = ["commander_demo", "deepl_demo", "tech_week_demo", "translate_demo", "vision_model_demos"] -__version__ = "1.0.13" +__all__ = [ + 'commander_demo', + 'deepl_demo', + 'screenshot_demo', + 'tech_week_demo', + 'translate_demo', + 'vision_model_demos' +] +__version__ = '1.0.13' diff --git a/src/demo/others/tech_week_demo.py b/src/demo/others/tech_week_demo.py index a62dbff3..939e4416 100755 --- a/src/demo/others/tech_week_demo.py +++ b/src/demo/others/tech_week_demo.py @@ -13,18 +13,18 @@ Copyright (c) 2024, HomeSetup """ -from askai.core.askai import AskAi -from askai.core.component.audio_player import AudioPlayer -from askai.core.support.shared_instances import shared -from clitt.core.term.terminal import Terminal +import sys from datetime import datetime, timedelta -from hspylib.core.tools.commons import sysout -from hspylib.core.tools.dict_tools import get_or_default from pathlib import Path from threading import Timer import pause -import sys +from askai.core.askai_cli import AskAiCli +from askai.core.component.audio_player import AudioPlayer +from askai.core.support.shared_instances import shared +from clitt.core.term.terminal import Terminal +from hspylib.core.tools.commons import sysout +from hspylib.core.tools.dict_tools import get_or_default today = datetime.today() th_dir: str = f"{Path.home()}/TechWeek" @@ -69,7 +69,7 @@ def taius_intro() -> None: with open(welcome_file) as f_pt: Terminal.clear() sysout(f"%GREEN%{intro}%NC%") - ai = AskAi(False, False, False, 1, "taius", "openai", "gpt-3.5-turbo", f_pt.readlines()) + ai = AskAiCli(False, False, False, False, 1, "taius", "openai", "gpt-3.5-turbo", f_pt.readlines()) ai.run() pause.seconds(1) Terminal.shell_exec(f"open {ppt_url}") diff --git a/src/main/__init__.py b/src/main/__init__.py index 2c7b3b7a..fa691888 100644 --- a/src/main/__init__.py +++ b/src/main/__init__.py @@ -5,5 +5,7 @@ # Package: main """Package initialization.""" -__all__ = ["askai"] -__version__ = "1.0.13" +__all__ = [ + 'askai' +] +__version__ = '1.0.13' diff --git a/src/main/askai/__init__.py b/src/main/askai/__init__.py index a3237699..78851fb6 100644 --- a/src/main/askai/__init__.py +++ b/src/main/askai/__init__.py @@ -5,5 +5,10 @@ # Package: main.askai """Package initialization.""" -__all__ = ["core", "exception", "language", "tui"] -__version__ = "1.0.13" +__all__ = [ + 'core', + 'exception', + 'language', + 'tui' +] +__version__ = '1.0.13' diff --git a/src/main/askai/core/__init__.py b/src/main/askai/core/__init__.py index 6e338929..38392f62 100644 --- a/src/main/askai/core/__init__.py +++ b/src/main/askai/core/__init__.py @@ -6,19 +6,19 @@ """Package initialization.""" __all__ = [ - "askai", - "askai_cli", - "askai_configs", - "askai_events", - "askai_messages", - "askai_prompt", - "askai_settings", - "commander", - "component", - "engine", - "enums", - "features", - "model", - "support", + 'askai', + 'askai_cli', + 'askai_configs', + 'askai_events', + 'askai_messages', + 'askai_prompt', + 'askai_settings', + 'commander', + 'component', + 'engine', + 'enums', + 'features', + 'model', + 'support' ] -__version__ = "1.0.13" +__version__ = '1.0.13' diff --git a/src/main/askai/core/askai_messages.py b/src/main/askai/core/askai_messages.py index 58e10425..8306a7bc 100644 --- a/src/main/askai/core/askai_messages.py +++ b/src/main/askai/core/askai_messages.py @@ -194,8 +194,8 @@ def invalid_command(self, response_text: AnyStr) -> str: def cmd_no_exist(self, command: str) -> str: return f"Command: `{command}' does not exist !" - def cmd_failed(self, cmd_line: str) -> str: - return f"Command: `{cmd_line}' failed to execute !" + def cmd_failed(self, cmd_line: str, error_msg: str) -> str: + return f"Command: `{cmd_line}' failed to execute -> {error_msg}!" def camera_not_open(self) -> str: return "Camera is not open, or unauthorized!" diff --git a/src/main/askai/core/commander/__init__.py b/src/main/askai/core/commander/__init__.py index ec244593..1b3f6253 100644 --- a/src/main/askai/core/commander/__init__.py +++ b/src/main/askai/core/commander/__init__.py @@ -5,5 +5,8 @@ # Package: main.askai.core.commander """Package initialization.""" -__all__ = ["commander", "commands"] -__version__ = "1.0.13" +__all__ = [ + 'commander', + 'commands' +] +__version__ = '1.0.13' diff --git a/src/main/askai/core/commander/commands/__init__.py b/src/main/askai/core/commander/commands/__init__.py index c86ea8eb..5b867155 100644 --- a/src/main/askai/core/commander/commands/__init__.py +++ b/src/main/askai/core/commander/commands/__init__.py @@ -5,5 +5,12 @@ # Package: main.askai.core.commander.commands """Package initialization.""" -__all__ = ["cache_cmd", "camera_cmd", "general_cmd", "history_cmd", "settings_cmd", "tts_stt_cmd"] -__version__ = "1.0.13" +__all__ = [ + 'cache_cmd', + 'camera_cmd', + 'general_cmd', + 'history_cmd', + 'settings_cmd', + 'tts_stt_cmd' +] +__version__ = '1.0.13' diff --git a/src/main/askai/core/commander/commands/general_cmd.py b/src/main/askai/core/commander/commands/general_cmd.py index 0f45433b..3bb46626 100644 --- a/src/main/askai/core/commander/commands/general_cmd.py +++ b/src/main/askai/core/commander/commands/general_cmd.py @@ -36,11 +36,11 @@ def execute(cmd_line: str | None = None) -> None: """Execute a terminal command. :param cmd_line: The command line to execute (optional). """ - output, exit_code = Terminal.INSTANCE.shell_exec(cmd_line, shell=True) + output, err_out, exit_code = Terminal.INSTANCE.shell_exec(cmd_line, shell=True) if exit_code == ExitStatus.SUCCESS: text_formatter.cmd_print(output) else: - display_text(f"\n%RED%-=- Command `{cmd_line}` failed to execute: Code ({exit_code}) -=-%NC%") + display_text(f"\n%RED%Command `{cmd_line}` failed. Error({err_out}) Code ({exit_code})%NC%") @staticmethod def summarize(folder: str, glob: str) -> None: diff --git a/src/main/askai/core/component/__init__.py b/src/main/askai/core/component/__init__.py index bc70f7d2..c677c1d8 100644 --- a/src/main/askai/core/component/__init__.py +++ b/src/main/askai/core/component/__init__.py @@ -6,14 +6,15 @@ """Package initialization.""" __all__ = [ - "audio_player", - "cache_service", - "camera", - "geo_location", - "image_store", - "internet_service", - "recorder", - "scheduler", - "summarizer", + 'audio_player', + 'cache_service', + 'camera', + 'geo_location', + 'image_store', + 'internet_service', + 'recorder', + 'scheduler', + 'summarizer', + 'text_streamer' ] -__version__ = "1.0.13" +__version__ = '1.0.13' diff --git a/src/main/askai/core/component/audio_player.py b/src/main/askai/core/component/audio_player.py index c81b5f60..28fbe01f 100644 --- a/src/main/askai/core/component/audio_player.py +++ b/src/main/askai/core/component/audio_player.py @@ -47,7 +47,7 @@ def play_audio_file(path_to_audio_file: str | Path, tempo: int = 1) -> bool: """ if file_is_not_empty(str(path_to_audio_file)): try: - out, code = Terminal.shell_exec( + _, _, code = Terminal.shell_exec( f'ffplay -af "atempo={tempo}" -v 0 -nodisp -autoexit {path_to_audio_file}' ) return code == ExitStatus.SUCCESS @@ -78,18 +78,18 @@ def audio_length(self, path_to_audio_file: str) -> float: :return: The length of the audio file in seconds as a float. """ check_argument(which("ffprobe") and file_is_not_empty(path_to_audio_file)) - ret: float = 0.0 + out: float = 0.0 try: - ret, code = Terminal.shell_exec( + out, _, code = Terminal.shell_exec( f'ffprobe -i {path_to_audio_file} -show_entries format=duration -v quiet -of csv="p=0"' ) - return float(ret) if code == ExitStatus.SUCCESS else 0.0 + return float(out) if code == ExitStatus.SUCCESS else 0.0 except FileNotFoundError: log.error("Audio file was not found: %s !", path_to_audio_file) except TypeError: log.error("Could not determine audio duration !") - return ret + return out def play_sfx(self, filename: str, file_ext: Literal[".mp3", ".wav", ".m4a"] = ".mp3") -> bool: """Play a sound effect audio file. diff --git a/src/main/askai/core/engine/__init__.py b/src/main/askai/core/engine/__init__.py index 3c9fb632..c741a6ff 100644 --- a/src/main/askai/core/engine/__init__.py +++ b/src/main/askai/core/engine/__init__.py @@ -5,5 +5,11 @@ # Package: main.askai.core.engine """Package initialization.""" -__all__ = ["ai_engine", "ai_model", "ai_reply", "ai_vision", "engine_factory", "openai"] -__version__ = "1.0.13" +__all__ = [ + 'ai_engine', + 'ai_model', + 'ai_vision', + 'engine_factory', + 'openai' +] +__version__ = '1.0.13' diff --git a/src/main/askai/core/engine/openai/__init__.py b/src/main/askai/core/engine/openai/__init__.py index 64c54805..acc36760 100644 --- a/src/main/askai/core/engine/openai/__init__.py +++ b/src/main/askai/core/engine/openai/__init__.py @@ -5,5 +5,11 @@ # Package: main.askai.core.engine.openai """Package initialization.""" -__all__ = ["openai_configs", "openai_engine", "openai_model", "openai_vision", "temperature"] -__version__ = "1.0.13" +__all__ = [ + 'openai_configs', + 'openai_engine', + 'openai_model', + 'openai_vision', + 'temperature' +] +__version__ = '1.0.13' diff --git a/src/main/askai/core/enums/__init__.py b/src/main/askai/core/enums/__init__.py index ce5f84c4..49e9bc0d 100644 --- a/src/main/askai/core/enums/__init__.py +++ b/src/main/askai/core/enums/__init__.py @@ -5,5 +5,10 @@ # Package: main.askai.core.enums """Package initialization.""" -__all__ = ["acc_response", "router_mode", "routing_model"] -__version__ = "1.0.13" +__all__ = [ + 'acc_response', + 'router_mode', + 'routing_model', + 'verbosity' +] +__version__ = '1.0.13' diff --git a/src/main/askai/core/features/__init__.py b/src/main/askai/core/features/__init__.py index f6ea9eae..942ce53d 100644 --- a/src/main/askai/core/features/__init__.py +++ b/src/main/askai/core/features/__init__.py @@ -5,5 +5,9 @@ # Package: main.askai.core.features """Package initialization.""" -__all__ = ["router", "validation"] -__version__ = "1.0.13" +__all__ = [ + 'processors', + 'router', + 'tools' +] +__version__ = '1.0.13' diff --git a/src/main/askai/core/features/processors/__init__.py b/src/main/askai/core/features/processors/__init__.py index 217c729a..71f34394 100644 --- a/src/main/askai/core/features/processors/__init__.py +++ b/src/main/askai/core/features/processors/__init__.py @@ -2,8 +2,14 @@ # # hspylib-askai v1.0.13 # -# Package: main.askai.core.features.router.processors +# Package: main.askai.core.features.processors """Package initialization.""" -__all__ = ["qna", "qstring", "rag", "task_splitter"] -__version__ = "1.0.13" +__all__ = [ + 'ai_processor', + 'qna', + 'qstring', + 'rag', + 'task_splitter' +] +__version__ = '1.0.13' diff --git a/src/main/askai/core/features/router/__init__.py b/src/main/askai/core/features/router/__init__.py index b43ff576..ea63ce8f 100644 --- a/src/main/askai/core/features/router/__init__.py +++ b/src/main/askai/core/features/router/__init__.py @@ -5,5 +5,10 @@ # Package: main.askai.core.features.router """Package initialization.""" -__all__ = ["ai_processor", "model_selector", "processors", "task_agent", "task_toolkit", "tools"] -__version__ = "1.0.13" +__all__ = [ + 'model_selector', + 'task_accuracy', + 'task_agent', + 'task_toolkit' +] +__version__ = '1.0.13' diff --git a/src/main/askai/core/features/tools/__init__.py b/src/main/askai/core/features/tools/__init__.py index 67e189d4..22c3f3e9 100644 --- a/src/main/askai/core/features/tools/__init__.py +++ b/src/main/askai/core/features/tools/__init__.py @@ -2,8 +2,17 @@ # # hspylib-askai v1.0.13 # -# Package: main.askai.core.features.router.tools +# Package: main.askai.core.features.tools """Package initialization.""" -__all__ = ["analysis", "browser", "general", "generation", "summarization", "terminal", "vision", "webcam"] -__version__ = "1.0.13" +__all__ = [ + 'analysis', + 'browser', + 'general', + 'generation', + 'summarization', + 'terminal', + 'vision', + 'webcam' +] +__version__ = '1.0.13' diff --git a/src/main/askai/core/features/tools/terminal.py b/src/main/askai/core/features/tools/terminal.py index a7b219c0..328a197d 100644 --- a/src/main/askai/core/features/tools/terminal.py +++ b/src/main/askai/core/features/tools/terminal.py @@ -116,7 +116,7 @@ def _execute_bash(command_line: str) -> Tuple[bool, str]: command = expandvars(command_line.replace("~/", f"{os.getenv('HOME')}/").strip()) log.info("Executing command `%s'", command) events.reply.emit(reply=AIReply.full(msg.executing(command_line))) - output, exit_code = Terminal.INSTANCE.shell_exec(command, shell=True) + output, err_out, exit_code = Terminal.INSTANCE.shell_exec(command, shell=True) if exit_code == ExitStatus.SUCCESS: log.info("Command succeeded: \n|-CODE=%s \n|-PATH: %s \n|-CMD: %s ", exit_code, os.getcwd(), command) if _path_ := extract_path(command): @@ -129,7 +129,7 @@ def _execute_bash(command_line: str) -> Tuple[bool, str]: status = True else: log.error("Command failed.\nCODE=%s \nPATH=%s \nCMD=%s ", exit_code, os.getcwd(), command) - output = msg.cmd_failed(command) + output = msg.cmd_failed(command, err_out) else: output = msg.cmd_no_exist(command) diff --git a/src/main/askai/core/model/__init__.py b/src/main/askai/core/model/__init__.py index b3217353..1d09c9dd 100644 --- a/src/main/askai/core/model/__init__.py +++ b/src/main/askai/core/model/__init__.py @@ -5,5 +5,13 @@ # Package: main.askai.core.model """Package initialization.""" -__all__ = ["action_plan", "api_keys", "image_result", "model_result", "search_result", "summary_result"] -__version__ = "1.0.13" +__all__ = [ + 'action_plan', + 'ai_reply', + 'api_keys', + 'image_result', + 'model_result', + 'search_result', + 'summary_result' +] +__version__ = '1.0.13' diff --git a/src/main/askai/core/support/__init__.py b/src/main/askai/core/support/__init__.py index 340ef171..93296ffc 100644 --- a/src/main/askai/core/support/__init__.py +++ b/src/main/askai/core/support/__init__.py @@ -6,13 +6,13 @@ """Package initialization.""" __all__ = [ - "chat_context", - "langchain_support", - "platform", - "presets", - "rag_provider", - "shared_instances", - "text_formatter", - "utilities", + 'chat_context', + 'langchain_support', + 'platform', + 'presets', + 'rag_provider', + 'shared_instances', + 'text_formatter', + 'utilities' ] -__version__ = "1.0.13" +__version__ = '1.0.13' diff --git a/src/main/askai/core/support/chat_context.py b/src/main/askai/core/support/chat_context.py index 29d3b37f..8b951134 100644 --- a/src/main/askai/core/support/chat_context.py +++ b/src/main/askai/core/support/chat_context.py @@ -92,7 +92,7 @@ def push(self, key: str, content: Any, role: ChatRoles = "human") -> ContextRaw: """ if (token_length := (self.length(key)) + len(content)) > self._token_limit: raise TokenLengthExceeded(f"Required token length={token_length} limit={self._token_limit}") - if (entry := ContextEntry(role, str(content))) not in (ctx := self._store[key]): + if (entry := ContextEntry(role, content)) not in (ctx := self._store[key]): ctx.append(entry) return self.get(key) diff --git a/src/main/askai/exception/__init__.py b/src/main/askai/exception/__init__.py index 5d89c407..5cf57b5b 100644 --- a/src/main/askai/exception/__init__.py +++ b/src/main/askai/exception/__init__.py @@ -5,5 +5,7 @@ # Package: main.askai.exception """Package initialization.""" -__all__ = ["exceptions"] -__version__ = "1.0.13" +__all__ = [ + 'exceptions' +] +__version__ = '1.0.13' diff --git a/src/main/askai/language/__init__.py b/src/main/askai/language/__init__.py index 170982a5..f186aa5d 100644 --- a/src/main/askai/language/__init__.py +++ b/src/main/askai/language/__init__.py @@ -5,5 +5,9 @@ # Package: main.askai.language """Package initialization.""" -__all__ = ["ai_translator", "language", "translators"] -__version__ = "1.0.13" +__all__ = [ + 'ai_translator', + 'language', + 'translators' +] +__version__ = '1.0.13' diff --git a/src/main/askai/language/translators/__init__.py b/src/main/askai/language/translators/__init__.py index c98443e5..de894085 100644 --- a/src/main/askai/language/translators/__init__.py +++ b/src/main/askai/language/translators/__init__.py @@ -5,5 +5,9 @@ # Package: main.askai.language.translators """Package initialization.""" -__all__ = ["argos_translator", "deepl_translator", "marian_translator"] -__version__ = "1.0.13" +__all__ = [ + 'argos_translator', + 'deepl_translator', + 'marian_translator' +] +__version__ = '1.0.13' diff --git a/src/main/askai/tui/__init__.py b/src/main/askai/tui/__init__.py index 00db4f77..89987117 100644 --- a/src/main/askai/tui/__init__.py +++ b/src/main/askai/tui/__init__.py @@ -5,5 +5,11 @@ # Package: main.askai.tui """Package initialization.""" -__all__ = ["app_header", "app_icons", "app_suggester", "app_widgets", "askai_app"] -__version__ = "1.0.13" +__all__ = [ + 'app_header', + 'app_icons', + 'app_suggester', + 'app_widgets', + 'askai_app' +] +__version__ = '1.0.13' diff --git a/src/main/requirements.txt b/src/main/requirements.txt index b2e02b4e..45cfd4d3 100644 --- a/src/main/requirements.txt +++ b/src/main/requirements.txt @@ -1,7 +1,7 @@ ###### AUTO-GENERATED Requirements file for: AskAI ###### hspylib>=1.12.49 -hspylib-clitt>=0.9.133 +hspylib-clitt>=0.9.135 hspylib-setman>=0.10.35 retry2>=0.9.5 pause>=0.3 diff --git a/src/test/__init__.py b/src/test/__init__.py index 4a23c5c3..e5708d7d 100644 --- a/src/test/__init__.py +++ b/src/test/__init__.py @@ -5,5 +5,8 @@ # Package: test """Package initialization.""" -__all__ = ["core", "test_main"] -__version__ = "1.0.13" +__all__ = [ + 'core', + 'test_main' +] +__version__ = '1.0.13' diff --git a/src/test/core/__init__.py b/src/test/core/__init__.py index 44afbb8a..ae4a6ed6 100644 --- a/src/test/core/__init__.py +++ b/src/test/core/__init__.py @@ -5,5 +5,7 @@ # Package: test.core """Package initialization.""" -__all__ = ["support"] -__version__ = "1.0.13" +__all__ = [ + 'support' +] +__version__ = '1.0.13' diff --git a/src/test/core/support/__init__.py b/src/test/core/support/__init__.py index 88689ddc..56d1d35c 100644 --- a/src/test/core/support/__init__.py +++ b/src/test/core/support/__init__.py @@ -5,5 +5,7 @@ # Package: test.core.support """Package initialization.""" -__all__ = ["test_utilities"] -__version__ = "1.0.13" +__all__ = [ + 'test_utilities' +] +__version__ = '1.0.13'