From 470bd9eb0ab116c48e0cddaa871364d870ccef81 Mon Sep 17 00:00:00 2001 From: Hugo Saporetti Junior Date: Thu, 12 Dec 2024 16:51:22 -0300 Subject: [PATCH] Refactorings, add missing comments --- gradle/dependencies.gradle | 4 +- pyproject.toml | 6 +++ src/__init__.py | 0 .../core/support => demo/devel}/spinner.py | 0 src/demo/others/spinner_demo.py | 3 +- .../askai/core/component/cache_service.py | 4 +- .../askai/core/component/internet_service.py | 7 ++- src/main/askai/core/component/scheduler.py | 12 ++++- .../askai/core/component/text_streamer.py | 8 +++- .../askai/core/engine/openai/openai_vision.py | 41 +++++++++++----- src/main/askai/core/enums/acc_color.py | 40 +++++++++------- src/main/askai/core/enums/router_mode.py | 25 ++++++---- src/main/askai/core/model/acc_response.py | 19 ++++---- .../askai/core/model/screenshot_result.py | 16 ++++++- src/main/askai/core/model/search_result.py | 21 ++++---- src/main/askai/core/processors/chat.py | 36 +++++++------- src/main/askai/core/processors/rag.py | 48 ++++++++----------- .../processors/splitter/splitter_pipeline.py | 48 +++++++------------ src/main/askai/core/router/agent_tools.py | 48 +++++++------------ src/main/askai/tui/app_widgets.py | 36 +++++++------- src/test/core/model/test_acc_response.py | 25 +++++----- 21 files changed, 238 insertions(+), 209 deletions(-) create mode 100644 src/__init__.py rename src/{main/askai/core/support => demo/devel}/spinner.py (100%) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 5e117691..17002c6c 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -268,10 +268,10 @@ task installBinaries(type: Task) { readDependencies() apps.each { app -> if ('latest' == app.version) { - println(os + ": TODO Installing latest version of '" + app.binary + "'") + println(os + ": FIXME Installing latest version of '" + app.binary + "'") } else { - println(os + ": TODO Installing '" + app.binary + "' v" + app.version) + println(os + ": FIXME Installing '" + app.binary + "' v" + app.version) } } } diff --git a/pyproject.toml b/pyproject.toml index e4734a41..b6493ed8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,3 +74,9 @@ opentelemetry-api = ">=1.27.0" opentelemetry-sdk = ">=1.27.0" opentelemetry-proto = ">=1.27.0" pytz = ">=2024.1" + +[tool.black] +fast = true +config = true +target-version = ["py310"] +line-length = 120 diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/main/askai/core/support/spinner.py b/src/demo/devel/spinner.py similarity index 100% rename from src/main/askai/core/support/spinner.py rename to src/demo/devel/spinner.py diff --git a/src/demo/others/spinner_demo.py b/src/demo/others/spinner_demo.py index 9258dace..1036c187 100644 --- a/src/demo/others/spinner_demo.py +++ b/src/demo/others/spinner_demo.py @@ -1,10 +1,11 @@ -from askai.core.support.spinner import Spinner from hspylib.core.tools.commons import sysout from hspylib.modules.cli.vt100.vt_color import VtColor import os import pause +from src.demo.devel.spinner import Spinner + def echo(message: str, prefix: str | None = None, end=os.linesep) -> None: """Prints a message with a prefix followed by the specified end character. diff --git a/src/main/askai/core/component/cache_service.py b/src/main/askai/core/component/cache_service.py index f7acc47a..0d404561 100644 --- a/src/main/askai/core/component/cache_service.py +++ b/src/main/askai/core/component/cache_service.py @@ -270,7 +270,9 @@ def _get_role_(msg: BaseMessage) -> str: ) def read_memory(self) -> list[str]: - """TODO""" + """Reads and parses the memory from the context file. + :return: A list of non-empty, stripped strings split by roles (human, assistant, system). + """ flags: int = re.MULTILINE | re.DOTALL | re.IGNORECASE memory: str = ASKAI_MEMORY_FILE.read_text() return list( diff --git a/src/main/askai/core/component/internet_service.py b/src/main/askai/core/component/internet_service.py index 7247f564..6dc5b918 100644 --- a/src/main/askai/core/component/internet_service.py +++ b/src/main/askai/core/component/internet_service.py @@ -88,9 +88,14 @@ class InternetService(metaclass=Singleton): @classmethod def _url_to_icon(cls, url: str) -> str: - """TODO""" + """Replaces a URL with its corresponding icon URL from SITE_ICONS if available. + :param url: The URL to be replaced with its corresponding icon. + :return: The icon URL if found in SITE_ICONS, otherwise the original URL. + :raises KeyError: If the URL is not found in SITE_ICONS. + """ return url.replace(url, cls.SITE_ICONS[url]) if cls.SITE_ICONS[url] else url + @classmethod def wrap_response(cls, terms: str, output: str, search: SearchResult) -> str: """Format and wrap the search response based on the search terms, output, and method used. diff --git a/src/main/askai/core/component/scheduler.py b/src/main/askai/core/component/scheduler.py index fdaf1ab0..20d8ff48 100644 --- a/src/main/askai/core/component/scheduler.py +++ b/src/main/askai/core/component/scheduler.py @@ -296,7 +296,13 @@ def _call_it_back() -> None: self._add(f"Every-{callback.__name__}", _call_it_back) def _add(self, thread_name: str, callback: Callable, *args, **kwargs) -> None: - """TODO""" + """Adds a new thread to the '_not_started' internal list for management. + :param thread_name: The name of the thread to be added. + :param callback: The function to be executed by the thread. + :param args: Positional arguments to be passed to the callback. + :param kwargs: Keyword arguments to be passed to the callback. + :return: None + """ th_new: Thread = Thread( name=thread_name, target=callback, args=args, kwargs=kwargs ) @@ -304,7 +310,9 @@ def _add(self, thread_name: str, callback: Callable, *args, **kwargs) -> None: self._threads[thread_name] = th_new def _remove(self, not_started: Thread) -> None: - """TODO""" + """Removes a thread from the '_not_started' internal list for management. + :param not_started: The thread to remove from the '_not_started' list + """ self._not_started.remove(not_started) diff --git a/src/main/askai/core/component/text_streamer.py b/src/main/askai/core/component/text_streamer.py index 26e0ecde..66fd80e5 100644 --- a/src/main/askai/core/component/text_streamer.py +++ b/src/main/askai/core/component/text_streamer.py @@ -114,7 +114,13 @@ def _process(text: AnyStr, prefix: AnyStr = "", tempo: int = 1, language: Langua def stream_text( self, text: AnyStr, prefix: AnyStr = "", tempo: int = 1, language: Language = Language.EN_US ) -> None: - """TODO""" + """Streams the given text with a specified prefix, tempo, and language settings. + :param text: The text to be streamed. + :param prefix: A prefix to be added to the streamed text. Defaults to an empty string. + :param tempo: The tempo for streaming the text. Defaults to 1. + :param language: The language for the text being streamed. Defaults to Language.EN_US. + :raises ValueError: If the provided tempo is invalid. + """ TextStreamer.STREAMER_THREAD = Thread(daemon=True, target=self._process, args=(text, prefix, tempo, language)) TextStreamer.STREAMER_THREAD.start() TextStreamer.STREAMER_THREAD.join() diff --git a/src/main/askai/core/engine/openai/openai_vision.py b/src/main/askai/core/engine/openai/openai_vision.py index 63a9dab5..37fb1ba7 100644 --- a/src/main/askai/core/engine/openai/openai_vision.py +++ b/src/main/askai/core/engine/openai/openai_vision.py @@ -2,15 +2,15 @@ # -*- coding: utf-8 -*- """ - @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 +@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 - Copyright (c) 2024, AskAI +Copyright (c) 2024, AskAI """ from typing import TypeAlias, Literal import os @@ -72,11 +72,19 @@ def create_image_caption_chain(inputs: dict) -> MessageContent: return msg.content def image_template(self, question: str = "") -> str: + """Generates a formatted prompt for an image based on the given question. + :param question: Optional question to include in the image prompt. + :return: A string containing the formatted prompt for an image description. + """ return PromptTemplate(input_variables=["question"], template=prompt.read_prompt("img-caption")).format( question=question ) def screenshot_template(self, question: str = "") -> str: + """Generates a formatted prompt for a screenshot based on the given question. + :param question: Optional question to include in the screenshot prompt. + :return: A string containing the formatted prompt for a screenshot description. + """ return PromptTemplate(input_variables=["question"], template=prompt.read_prompt("ss-caption")).format( question=question ) @@ -99,9 +107,7 @@ def caption( check_argument(len((final_path := str(find_file(final_path) or ""))) > 0, f"Invalid image path: {final_path}") vision_prompt: str = self._get_vision_prompt(query, image_type) load_image_chain = TransformChain( - input_variables=["image_path", "parser_guides"], - output_variables=["image"], - transform=self._encode_image + input_variables=["image_path", "parser_guides"], output_variables=["image"], transform=self._encode_image ) out_parser: JsonOutputParser = self._get_out_parser(image_type) vision_chain = load_image_chain | self.create_image_caption_chain | out_parser @@ -113,7 +119,11 @@ def caption( return str(vision_chain.invoke(args)) def _get_out_parser(self, image_type: Literal["photo", "screenshot"]) -> JsonOutputParser: - """TODO""" + """Returns the appropriate JSON output parser based on the image type. + :param image_type: The type of image, either "photo" or "screenshot". + :return: An instance of JsonOutputParser configured for the specified image type. + :raises ValueError: If no parser is found for the given image type. + """ match image_type: case "photo": return JsonOutputParser(pydantic_object=ImageResult) @@ -123,7 +133,12 @@ def _get_out_parser(self, image_type: Literal["photo", "screenshot"]) -> JsonOut raise ValueError(f"Parser not found for: {image_type}") def _get_vision_prompt(self, query: str, image_type: Literal["photo", "screenshot"]) -> str: - """TODO""" + """Returns the appropriate vision prompt based on the image type and query. + :param query: The query string used to generate the vision prompt. + :param image_type: The type of image, either "photo" or "screenshot". + :return: A string containing the vision prompt tailored for the specified image type. + :raises ValueError: If no prompt is found for the given image type. + """ match image_type: case "photo": return self.image_template(query) diff --git a/src/main/askai/core/enums/acc_color.py b/src/main/askai/core/enums/acc_color.py index 46666aa9..efd5bee7 100644 --- a/src/main/askai/core/enums/acc_color.py +++ b/src/main/askai/core/enums/acc_color.py @@ -2,15 +2,15 @@ # -*- coding: utf-8 -*- """ - @project: HsPyLib-AskAI - @package: askai.core.enums.acc_response - @file: acc_color.py - @created: Thu, 26 Sep 2024 - @author: Hugo Saporetti Junior - @site: https://github.com/yorevs/askai - @license: MIT - Please refer to - - Copyright (c) 2024, AskAI +@project: HsPyLib-AskAI +@package: askai.core.enums.acc_response + @file: acc_color.py +@created: Thu, 26 Sep 2024 + @author: Hugo Saporetti Junior + @site: https://github.com/yorevs/askai +@license: MIT - Please refer to + +Copyright (c) 2024, AskAI """ from typing import Literal, TypeAlias import logging as log @@ -21,21 +21,29 @@ class AccColor(Enumeration): - """TODO""" + """Represents various color-coded accuracy levels with associated values. + Each color corresponds to a specific status and numeric value. + """ # fmt: off - INTERRUPT = 'Black', -1 + INTERRUPT = 'Black', -1 + """Black color, indicating an interrupt status.""" - TERMINATE = 'White', 0 + TERMINATE = 'White', 0 + """White color, indicating a terminate status.""" - EXCELLENT = 'Blue', 1 + EXCELLENT = 'Blue', 1 + """Blue color, indicating an excellent status.""" - GOOD = 'Green', 2 + GOOD = 'Green', 2 + """Green color, indicating a good status.""" - MODERATE = 'Yellow', 3 + MODERATE = 'Yellow', 3 + """Yellow color, indicating a moderate status.""" - BAD = 'Red', 4 + BAD = 'Red', 4 + """Red color, indicating a bad status.""" def __init__(self, color: AccuracyColors, weight: int): self._color: AccuracyColors = color diff --git a/src/main/askai/core/enums/router_mode.py b/src/main/askai/core/enums/router_mode.py index 2557ea00..1e8ac72d 100644 --- a/src/main/askai/core/enums/router_mode.py +++ b/src/main/askai/core/enums/router_mode.py @@ -2,15 +2,15 @@ # -*- coding: utf-8 -*- """ - @project: HsPyLib-AskAI - @package: askai.core.enums.router_mode - @file: router_mode.py - @created: Tue, 24 Jun 2024 - @author: Hugo Saporetti Junior - @site: https://github.com/yorevs/askai - @license: MIT - Please refer to - - Copyright (c) 2024, AskAI +@project: HsPyLib-AskAI +@package: askai.core.enums.router_mode + @file: router_mode.py +@created: Tue, 24 Jun 2024 + @author: Hugo Saporetti Junior + @site: https://github.com/yorevs/askai +@license: MIT - Please refer to + +Copyright (c) 2024, AskAI """ from askai.core.askai_configs import configs from askai.core.askai_messages import msg @@ -94,7 +94,12 @@ def is_default(self) -> bool: return self == RouterMode.default() def welcome(self, **kwargs) -> Optional[str]: - """TODO""" + """Generates a welcome message based on the current router mode and optional context. + :param kwargs: Optional keyword arguments to customize the welcome message. + - sum_path: The path to the summary context (optional). + - sum_glob: The glob pattern for the summary files (optional). + :return: A string containing the welcome message, or None if no message is generated. + """ match self: case RouterMode.QNA: sum_path: str = get_or_default_by_key(kwargs, "sum_path", None) diff --git a/src/main/askai/core/model/acc_response.py b/src/main/askai/core/model/acc_response.py index 76f4e4a2..b9c68f19 100644 --- a/src/main/askai/core/model/acc_response.py +++ b/src/main/askai/core/model/acc_response.py @@ -2,15 +2,15 @@ # -*- coding: utf-8 -*- """ - @project: HsPyLib-AskAI - @package: askai.core.enums.acc_response - @file: acc_response.py - @created: Tue, 23 Apr 2024 - @author: Hugo Saporetti Junior - @site: https://github.com/yorevs/askai - @license: MIT - Please refer to - - Copyright (c) 2024, AskAI +@project: HsPyLib-AskAI +@package: askai.core.enums.acc_response + @file: acc_response.py +@created: Tue, 23 Apr 2024 + @author: Hugo Saporetti Junior + @site: https://github.com/yorevs/askai +@license: MIT - Please refer to + +Copyright (c) 2024, AskAI """ from askai.core.enums.acc_color import AccColor, AccuracyColors from askai.core.support.llm_parser import parse_field @@ -49,7 +49,6 @@ def __str__(self): return f"{self.status} -> {self.details}" def __eq__(self, other: "AccResponse") -> bool: - """TODO""" return ( self.acc_color == other.acc_color and self.accuracy == other.accuracy diff --git a/src/main/askai/core/model/screenshot_result.py b/src/main/askai/core/model/screenshot_result.py index 2566cdf5..a9e5be2c 100644 --- a/src/main/askai/core/model/screenshot_result.py +++ b/src/main/askai/core/model/screenshot_result.py @@ -11,7 +11,14 @@ class ScreenshotResult(BaseModel): """ class DocumentModel(BaseModel): - """TODO""" + """Represents a model for storing document metadata and content overview. + + Attributes: + page_number: The number of the page in the document. + header: The content of the document's header. + footer: The content of the document's footer. + content_overview: A brief overview of the document's content. + """ page_number: int = Field(description="Document page number") header: str = Field(description="Document header content") @@ -19,7 +26,12 @@ class DocumentModel(BaseModel): content_overview: str = Field(description="Document content overview") class WebsiteModel(BaseModel): - """TODO""" + """Represents a model for storing website metadata. + + Attributes: + website_description: A description of the website. + website_url: The URL of the website. + """ website_description: str = Field(description="Website description") website_url: str = Field(description="Website URL") diff --git a/src/main/askai/core/model/search_result.py b/src/main/askai/core/model/search_result.py index 5941a692..a28b658b 100644 --- a/src/main/askai/core/model/search_result.py +++ b/src/main/askai/core/model/search_result.py @@ -2,15 +2,15 @@ # -*- coding: utf-8 -*- """ - @project: HsPyLib-AskAI - @package: askai.core.model - @file: search_result.py - @created: Sun, 12 Mar 2024 - @author: Hugo Saporetti Junior - @site: https://github.com/yorevs/askai - @license: MIT - Please refer to +@project: HsPyLib-AskAI +@package: askai.core.model + @file: search_result.py +@created: Sun, 12 Mar 2024 + @author: Hugo Saporetti Junior + @site: https://github.com/yorevs/askai +@license: MIT - Please refer to - Copyright (c) 2024, AskAI +Copyright (c) 2024, AskAI """ from askai.core.component.geo_location import geo_location from askai.core.support.llm_parser import parse_field, parse_list @@ -56,7 +56,6 @@ def __str__(self): return f"Search Results: {json.dumps(self.__dict__, default=lambda obj: obj.__dict__)}" def __eq__(self, other: "SearchResult") -> bool: - """TODO""" return ( self.category == other.category and self.keywords == other.keywords @@ -65,5 +64,7 @@ def __eq__(self, other: "SearchResult") -> bool: ) def build(self) -> str: - """TODO""" + """Builds the search result reference, by combining keywords, sites, and filters. + :return: A string containing the concatenated keywords, sites, and filters. + """ return f"{self.keywords} {self.sites} {self.filters}" diff --git a/src/main/askai/core/processors/chat.py b/src/main/askai/core/processors/chat.py index be06c8e1..a148f406 100644 --- a/src/main/askai/core/processors/chat.py +++ b/src/main/askai/core/processors/chat.py @@ -2,15 +2,15 @@ # -*- coding: utf-8 -*- """ - @project: taius-coder - @package: askai.core.processors.chat - @file: chat.py - @created: Mon, 23 Sep 2024 - @author: Hugo Saporetti Junior" - @site: https://github.com/yorevs/taius-coder - @license: MIT - Please refer to - - Copyright 2024, HSPyLib team +@project: taius-coder +@package: askai.core.processors.chat + @file: chat.py +@created: Mon, 23 Sep 2024 + @author: Hugo Saporetti Junior" + @site: https://github.com/yorevs/taius-coder +@license: MIT - Please refer to + +Copyright 2024, HSPyLib team """ from typing import Any, Optional @@ -35,13 +35,17 @@ class ChatProcessor(metaclass=Singleton): - """TODO""" + """Handles the processing of chat-related operations and templates.""" INSTANCE: "ChatProcessor" def template(self, prompt_str: str, *inputs: str, **kwargs) -> ChatPromptTemplate: - """Retrieve the processor Template.""" - + """Retrieves a chat prompt template based on the provided prompt string and inputs. + :param prompt_str: The string used as the base template for the prompt. + :param inputs: Positional arguments representing input variables for the template. + :param kwargs: Keyword arguments to format the template. + :return: A ChatPromptTemplate object built with the specified configuration. + """ template = PromptTemplate(input_variables=list(inputs), template=prompt_str) # fmt: off @@ -65,9 +69,7 @@ def process(self, question: str, **kwargs) -> Optional[str]: events.mode_changed.emit(mode="DEFAULT") return None - with Live( - Spinner("dots", f"[green]{msg.wait()}[/green]", style="green"), console=tf.console - ): + with Live(Spinner("dots", f"[green]{msg.wait()}[/green]", style="green"), console=tf.console): response = None prompt_file: str = get_or_default_by_key(kwargs, "prompt_file", None) history_ctx: Any | None = get_or_default_by_key(kwargs, "history_ctx", "HISTORY") @@ -86,8 +88,8 @@ def process(self, question: str, **kwargs) -> Optional[str]: ) if output := runnable.invoke( - input={"input": question}, - config={"configurable": {"session_id": history_ctx or ""}}): + input={"input": question}, config={"configurable": {"session_id": history_ctx or ""}} + ): response = output.content cursor.erase_line() diff --git a/src/main/askai/core/processors/rag.py b/src/main/askai/core/processors/rag.py index 2bffc3d1..8a35f1b5 100644 --- a/src/main/askai/core/processors/rag.py +++ b/src/main/askai/core/processors/rag.py @@ -2,15 +2,15 @@ # -*- coding: utf-8 -*- """ - @project: HsPyLib-AskAI - @package: askai.core.processors.rag - @file: rag.py - @created: Fri, 5 May 2024 - @author: Hugo Saporetti Junior - @site: https://github.com/yorevs/askai - @license: MIT - Please refer to - - Copyright (c) 2024, AskAI +@project: HsPyLib-AskAI +@package: askai.core.processors.rag + @file: rag.py +@created: Fri, 5 May 2024 + @author: Hugo Saporetti Junior + @site: https://github.com/yorevs/askai +@license: MIT - Please refer to + +Copyright (c) 2024, AskAI """ from clitt.core.term.cursor import cursor from rich.live import Live @@ -62,12 +62,8 @@ def __init__(self): @property def rag_template(self) -> BasePromptTemplate: - prompt_file: PathObject = PathObject.of( - prompt.append_path(f"taius/taius-non-interactive") - ) - final_prompt: str = prompt.read_prompt( - prompt_file.filename, prompt_file.abs_dir - ) + prompt_file: PathObject = PathObject.of(prompt.append_path(f"taius/taius-non-interactive")) + final_prompt: str = prompt.read_prompt(prompt_file.filename, prompt_file.abs_dir) # fmt: off return ChatPromptTemplate.from_messages([ ("system", final_prompt), @@ -78,7 +74,11 @@ def rag_template(self) -> BasePromptTemplate: @lru_cache def persist_dir(self, rag_dir: AnyPath, file_glob: AnyPath) -> Path: - """TODO""" + """Returns the persistent directory path based on the hash of the provided directory and file pattern. + :param rag_dir: The RAG directory path to be used for generating the hash. + :param file_glob: The file pattern to be used for generating the hash. + :return: A Path object representing the persistent directory. + """ summary_hash = hash_text(os.path.join(rag_dir, file_glob)) return Path(os.path.join(str(PERSIST_DIR), summary_hash)) @@ -108,9 +108,7 @@ def process(self, question: str, **_) -> Optional[str]: return output - def generate( - self, rag_dir: AnyPath = RAG_EXT_DIR, file_glob: AnyPath = "**/*.md" - ) -> None: + def generate(self, rag_dir: AnyPath = RAG_EXT_DIR, file_glob: AnyPath = "**/*.md") -> None: """Generates RAG data from the specified directory. :param rag_dir: The directory containing the files for RAG data generation :param file_glob: The files from which to generate the RAG database. @@ -127,18 +125,12 @@ def generate( persist_dir: Path = self.persist_dir(rag_dir, file_glob) if persist_dir.exists() and persist_dir.is_dir(): log.info("Recovering vector store from: '%s'", persist_dir) - self._vectorstore = Chroma( - persist_directory=str(persist_dir), embedding_function=embeddings - ) + self._vectorstore = Chroma(persist_directory=str(persist_dir), embedding_function=embeddings) else: with Status(f"[green]{msg.summarizing()}[/green]"): - rag_docs: list[Document] = DirectoryLoader( - str(rag_dir), glob=file_glob, recursive=True - ).load() + rag_docs: list[Document] = DirectoryLoader(str(rag_dir), glob=file_glob, recursive=True).load() if len(rag_docs) <= 0: - raise DocumentsNotFound( - f"Unable to find any document to at: '{persist_dir}'" - ) + raise DocumentsNotFound(f"Unable to find any document to at: '{persist_dir}'") self._vectorstore = Chroma.from_documents( persist_directory=str(persist_dir), documents=self._text_splitter.split_documents(rag_docs), diff --git a/src/main/askai/core/processors/splitter/splitter_pipeline.py b/src/main/askai/core/processors/splitter/splitter_pipeline.py index a08051c1..7d4dabb1 100644 --- a/src/main/askai/core/processors/splitter/splitter_pipeline.py +++ b/src/main/askai/core/processors/splitter/splitter_pipeline.py @@ -2,15 +2,15 @@ # -*- coding: utf-8 -*- """ - @project: HsPyLib-AskAI - @package: askai.core.processors.splitter.splitter_pipeline - @file: ai_engine.py - @created: Mon, 21 Oct 2024 - @author: Hugo Saporetti Junior - @site: https://github.com/yorevs/askai - @license: MIT - Please refer to - - Copyright (c) 2024, AskAI +@project: HsPyLib-AskAI +@package: askai.core.processors.splitter.splitter_pipeline + @file: ai_engine.py +@created: Mon, 21 Oct 2024 + @author: Hugo Saporetti Junior + @site: https://github.com/yorevs/askai +@license: MIT - Please refer to + +Copyright (c) 2024, AskAI """ from askai.core.askai_configs import configs from askai.core.askai_messages import msg @@ -41,7 +41,7 @@ class SplitterPipeline: - """TODO""" + """Represents a pipeline for handling and processing splitting tasks.""" state: States @@ -154,11 +154,7 @@ def has_next(self) -> bool: """Check if the plan has more actions to be executed to complete it. :return: True if the plan is there are still actions pending, otherwise False. """ - return ( - len(self.plan.tasks) > 0 - if self.plan is not None and self.plan.tasks - else False - ) + return len(self.plan.tasks) > 0 if self.plan is not None and self.plan.tasks else False def is_direct(self) -> bool: """Check if the plan is direct or if there are actions to be executed to complete it. @@ -194,11 +190,7 @@ def st_task_split(self) -> bool: log.info("Splitting tasks...") if (plan := actions.split(self.question, self.model)) is not None: if plan.is_direct: - self.responses.append( - PipelineResponse( - self.question, plan.speak or msg.no_output("TaskSplitter") - ) - ) + self.responses.append(PipelineResponse(self.question, plan.speak or msg.no_output("TaskSplitter"))) self.plan = plan return True @@ -219,9 +211,7 @@ def st_execute_task(self) -> bool: return False - def st_accuracy_check( - self, pass_threshold: AccColor = configs.pass_threshold - ) -> AccColor: + def st_accuracy_check(self, pass_threshold: AccColor = configs.pass_threshold) -> AccColor: """Pipeline-State::AccuracyCheck Checks whether the AI response is complete enough to present to the user. :param pass_threshold: Threshold value used to determine passing accuracy. :return: AccColor indicating success or failure after processing the state. @@ -272,12 +262,8 @@ def st_refine_answer(self) -> bool: :return: Boolean indicating success or failure after processing the state. """ - if refined := actions.refine_answer( - self.question, self.final_answer, self.last_accuracy - ): - final_response: PipelineResponse = PipelineResponse( - self.question, refined, self.last_accuracy - ) + if refined := actions.refine_answer(self.question, self.final_answer, self.last_accuracy): + final_response: PipelineResponse = PipelineResponse(self.question, refined, self.last_accuracy) self.responses.clear() self.responses.append(final_response) return True @@ -300,9 +286,7 @@ def st_final_answer(self) -> bool: ) if wrapped := actions.wrap_answer(self.question, self.final_answer, model): - final_response: PipelineResponse = PipelineResponse( - self.question, wrapped, self.last_accuracy - ) + final_response: PipelineResponse = PipelineResponse(self.question, wrapped, self.last_accuracy) self.responses.clear() self.responses.append(final_response) return True diff --git a/src/main/askai/core/router/agent_tools.py b/src/main/askai/core/router/agent_tools.py index 9129ba81..19a604ce 100644 --- a/src/main/askai/core/router/agent_tools.py +++ b/src/main/askai/core/router/agent_tools.py @@ -2,15 +2,15 @@ # -*- coding: utf-8 -*- """ - @project: HsPyLib-AskAI - @package: askai.core.router - @file: agent_tools.py - @created: Mon, 01 Apr 2024 - @author: Hugo Saporetti Junior - @site: https://github.com/yorevs/askai - @license: MIT - Please refer to - - Copyright (c) 2024, AskAI +@project: HsPyLib-AskAI +@package: askai.core.router + @file: agent_tools.py +@created: Mon, 01 Apr 2024 + @author: Hugo Saporetti Junior + @site: https://github.com/yorevs/askai +@license: MIT - Please refer to + +Copyright (c) 2024, AskAI """ import inspect import logging as log @@ -61,12 +61,8 @@ class AgentTools(metaclass=Singleton): def __init__(self): self._all: dict[str, Callable] = dict( filter( - lambda pair: pair[0] not in self.RESERVED - and not pair[0].startswith("_"), - { - n: fn - for n, fn in inspect.getmembers(self, predicate=inspect.ismethod) - }.items(), + lambda pair: pair[0] not in self.RESERVED and not pair[0].startswith("_"), + {n: fn for n, fn in inspect.getmembers(self, predicate=inspect.ismethod)}.items(), ) ) @@ -75,9 +71,7 @@ def tools(self) -> list[BaseTool]: """Return a cached list of LangChain base tools. :return: A list of BaseTool's instances available for use. """ - tools: list[BaseTool] = [ - self._create_structured_tool(v) for _, v in self._all.items() - ] + tools: list[BaseTool] = [self._create_structured_tool(v) for _, v in self._all.items()] log.debug("Available tools: are: '%s'", tools) @@ -90,12 +84,8 @@ def available_tools(self) -> str: """ avail_list: list[str] = list() for t in self.tools(): - if match := re.search( - r"^```(.*?)^\s*Usage:", t.description, re.DOTALL | re.MULTILINE - ): - avail_list.append( - f"**{t.name}::** " + re.sub(r"\s{2,}", " ", match.group(1).strip()) - ) + if match := re.search(r"^```(.*?)^\s*Usage:", t.description, re.DOTALL | re.MULTILINE): + avail_list.append(f"**{t.name}::** " + re.sub(r"\s{2,}", " ", match.group(1).strip())) return os.linesep.join(avail_list) def _human_approval(self) -> bool: @@ -183,9 +173,7 @@ def webcam_identifier(self) -> str: """ return webcam_identifier() - def screenshot( - self, path_name: AnyPath | None = None, save_dir: AnyPath | None = None - ) -> str: + def screenshot(self, path_name: AnyPath | None = None, save_dir: AnyPath | None = None) -> str: """Capture a screenshot and save it to the specified path. Usage: `screenshot(path_name, load_dir)` :param path_name: Optional path name of the captured screenshot. @@ -194,9 +182,7 @@ def screenshot( """ return capture_screenshot(path_name, save_dir) - def generate_content( - self, instructions: str, mime_type: str, filepath: AnyPath - ) -> str: + def generate_content(self, instructions: str, mime_type: str, filepath: AnyPath) -> str: """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)` @@ -262,7 +248,7 @@ def terminal(self, shell_type: str, command: str) -> str: :param command: The command or set of commands to execute in the terminal. :return: The output of the executed terminal command(s) as a string. """ - # TODO Check for permission before executing + # FIXME Check for permission before executing return execute_command(shell_type, command) def shutdown(self, reason: str) -> None: diff --git a/src/main/askai/tui/app_widgets.py b/src/main/askai/tui/app_widgets.py index 26e0403e..079486ce 100644 --- a/src/main/askai/tui/app_widgets.py +++ b/src/main/askai/tui/app_widgets.py @@ -2,15 +2,15 @@ # -*- coding: utf-8 -*- """ - @project: HsPyLib-AskAI - @package: askai.tui.app_widgets - @file: app_widgets.py - @created: Mon, 29 Apr 2024 - @author: Hugo Saporetti Junior - @site: https://github.com/yorevs/askai - @license: MIT - Please refer to - - Copyright (c) 2024, AskAI +@project: HsPyLib-AskAI +@package: askai.tui.app_widgets + @file: app_widgets.py +@created: Mon, 29 Apr 2024 + @author: Hugo Saporetti Junior + @site: https://github.com/yorevs/askai +@license: MIT - Please refer to + +Copyright (c) 2024, AskAI """ import os import re @@ -109,7 +109,7 @@ def __init__(self, app_info: str = ""): self.info_text = app_info @property - def info(self) -> Static: + def info(self) -> Widget: return self.query_one("#info") def compose(self) -> ComposeResult: @@ -174,7 +174,7 @@ class InputActions(Static): @staticmethod def copy() -> None: - """TODO""" + """Copy the last reply to the clipboard.""" HistoryCmd.context_copy("LAST_REPLY") def __init__(self, **kwargs): @@ -191,7 +191,7 @@ def compose(self) -> ComposeResult: @work(thread=True) def read_aloud(self) -> None: - """TODO""" + """Read the last reply aloud using hte default voice.""" if (ctx := str(shared.context.flat("LAST_REPLY"))) and ( last_reply := re.sub( r"^((system|human|AI|assistant):\s*)", @@ -200,23 +200,21 @@ def read_aloud(self) -> None: flags=re.MULTILINE | re.DOTALL | re.IGNORECASE, ) ): - TtsSttCmd.tts( - last_reply.strip(), os.environ.get("TEMP", tempfile.gettempdir()), True - ) + TtsSttCmd.tts(last_reply.strip(), os.environ.get("TEMP", tempfile.gettempdir()), True) def submit(self) -> None: - """TODO""" + """Submit the request to the AI engine.""" line_input: Input = self.app.line_input self.post_message(Input.Submitted(line_input, line_input.value)) def like(self) -> None: - """TODO""" + """Rate the AI response as Good.""" pass def dislike(self) -> None: - """TODO""" + """Rate the AI response as Bad.""" pass def regenerate(self) -> None: - """TODO""" + """Regenerate the response using another model.""" pass diff --git a/src/test/core/model/test_acc_response.py b/src/test/core/model/test_acc_response.py index f8a2bf0c..827003b1 100644 --- a/src/test/core/model/test_acc_response.py +++ b/src/test/core/model/test_acc_response.py @@ -2,24 +2,25 @@ # -*- coding: utf-8 -*- """ - @project: HsPyLib-AskAI - @package: askai.test.core.support - @file: test_utilities.py - @created: Fri, 22 Mar 2024 - @author: "Hugo Saporetti Junior - @site: "https://github.com/yorevs/hspylib") - @license: MIT - Please refer to - - Copyright (c) 2024, AskAI +@project: HsPyLib-AskAI +@package: askai.test.core.support + @file: test_utilities.py +@created: Fri, 22 Mar 2024 + @author: "Hugo Saporetti Junior + @site: "https://github.com/yorevs/hspylib") +@license: MIT - Please refer to + +Copyright (c) 2024, AskAI """ from askai.core.model.acc_response import AccResponse -from fixtures.acc_response_stubs import stub_response from hspylib.core.tools.commons import dirname from pathlib import Path import sys import unittest +from fixtures.acc_response_stubs import stub_response + class TestClass(unittest.TestCase): """TODO""" @@ -30,9 +31,7 @@ class TestClass(unittest.TestCase): RESPONSE_FILE_TEXT: str = RESPONSE_FILE.read_text() - RESPONSES: list[str] = list( - filter(None, map(str.strip, RESPONSE_FILE_TEXT.split("---"))) - ) + RESPONSES: list[str] = list(filter(None, map(str.strip, RESPONSE_FILE_TEXT.split("---")))) # Setup tests def setUp(self):