From 7c2e7a7bbebee17d6cbb6e7a7242aaf07b61c3a2 Mon Sep 17 00:00:00 2001 From: harishmohanraj Date: Tue, 8 Aug 2023 10:52:43 +0000 Subject: [PATCH] Add code generation files --- fastkafka_gen/_cli.py | 65 -- .../app_description_validator.py | 6 +- .../_code_generator/app_generator.py | 11 +- fastkafka_gen/_code_generator/helper.py | 41 +- .../_code_generator/plan_generator.py | 6 +- fastkafka_gen/_code_generator/prompts.py | 14 +- .../_code_generator/test_generator.py | 4 +- fastkafka_gen/_components/logger.py | 2 +- fastkafka_gen/_modidx.py | 26 +- .../{_cli_code_generator.py => cli.py} | 36 +- nbs/App_Description_Validator.ipynb | 10 +- nbs/App_Generator.ipynb | 101 ++- nbs/CLI.ipynb | 809 +++--------------- nbs/Code_Generator.ipynb | 409 --------- ...de_Generator_Helper.ipynb => Helper.ipynb} | 147 +--- nbs/Logger.ipynb | 138 ++- nbs/Plan_Generator.ipynb | 14 +- ...Generation_Prompts.ipynb => Prompts.ipynb} | 0 nbs/Test_Generator.ipynb | 8 +- settings.ini | 5 +- 20 files changed, 423 insertions(+), 1429 deletions(-) delete mode 100644 fastkafka_gen/_cli.py rename fastkafka_gen/{_cli_code_generator.py => cli.py} (82%) delete mode 100644 nbs/Code_Generator.ipynb rename nbs/{Code_Generator_Helper.ipynb => Helper.ipynb} (87%) rename nbs/{Code_Generation_Prompts.ipynb => Prompts.ipynb} (100%) diff --git a/fastkafka_gen/_cli.py b/fastkafka_gen/_cli.py deleted file mode 100644 index d89d256..0000000 --- a/fastkafka_gen/_cli.py +++ /dev/null @@ -1,65 +0,0 @@ -# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/CLI.ipynb. - -# %% auto 0 -__all__ = ['logger', 'run'] - -# %% ../nbs/CLI.ipynb 1 -from typing import * - -import typer - -from fastkafka._components.logger import get_logger - -# %% ../nbs/CLI.ipynb 5 -logger = get_logger(__name__, level=20) - -# %% ../nbs/CLI.ipynb 8 -_app = typer.Typer(help="") - -# %% ../nbs/CLI.ipynb 9 -@_app.command( - help="Runs Fast Kafka API application", -) -def run( - num_workers: int = typer.Option( - multiprocessing.cpu_count(), - help="Number of FastKafka instances to run, defaults to number of CPU cores.", - ), - app: str = typer.Argument( - ..., - help="input in the form of 'path:app', where **path** is the path to a python file and **app** is an object of type **FastKafka**.", - ), - kafka_broker: str = typer.Option( - "localhost", - help="kafka_broker, one of the keys of the kafka_brokers dictionary passed in the constructor of FastaKafka class.", - ), -) -> None: - """ - Runs FastKafka application. - - Args: - num_workers (int): Number of FastKafka instances to run, defaults to the number of CPU cores. - app (str): Input in the form of 'path:app', where **path** is the path to a python file and **app** is an object of type **FastKafka**. - kafka_broker (str): Kafka broker, one of the keys of the kafka_brokers dictionary passed in the constructor of FastKafka class. - - Raises: - typer.Exit: If there is an unexpected internal error. - """ - try: - asyncio.run( - run_fastkafka_server( - num_workers=num_workers, app=app, kafka_broker=kafka_broker - ) - ) - except Exception as e: - typer.secho(f"Unexpected internal error: {e}", err=True, fg=typer.colors.RED) - raise typer.Exit(1) - -# %% ../nbs/CLI.ipynb 12 -_app.add_typer(_cli_docs._docs_app, name="docs") - -# %% ../nbs/CLI.ipynb 20 -_app.add_typer(_cli_testing._testing_app, name="testing") - -# %% ../nbs/CLI.ipynb 23 -_app.add_typer(_cli_code_generator._code_generator_app, name="code_generator") diff --git a/fastkafka_gen/_code_generator/app_description_validator.py b/fastkafka_gen/_code_generator/app_description_validator.py index 83ddbac..3ddfff6 100644 --- a/fastkafka_gen/_code_generator/app_description_validator.py +++ b/fastkafka_gen/_code_generator/app_description_validator.py @@ -9,9 +9,9 @@ from yaspin import yaspin -from fastkafka._components.logger import get_logger -from fastkafka._code_generator.helper import CustomAIChat -from fastkafka._code_generator.prompts import APP_VALIDATION_PROMPT +from .._components.logger import get_logger +from .helper import CustomAIChat +from .prompts import APP_VALIDATION_PROMPT # %% ../../nbs/App_Description_Validator.ipynb 3 logger = get_logger(__name__) diff --git a/fastkafka_gen/_code_generator/app_generator.py b/fastkafka_gen/_code_generator/app_generator.py index 5ea6b05..344286b 100644 --- a/fastkafka_gen/_code_generator/app_generator.py +++ b/fastkafka_gen/_code_generator/app_generator.py @@ -9,9 +9,9 @@ from yaspin import yaspin -from fastkafka._components.logger import get_logger -from fastkafka._code_generator.helper import CustomAIChat, ValidateAndFixResponse -from fastkafka._code_generator.prompts import APP_GENERATION_PROMPT +from .._components.logger import get_logger +from .helper import CustomAIChat, ValidateAndFixResponse +from .prompts import APP_GENERATION_PROMPT # %% ../../nbs/App_Generator.ipynb 3 logger = get_logger(__name__) @@ -43,8 +43,7 @@ def _get_functions_prompt( parameters = ", ".join( [ f"Parameter: {param_name}, Type: {param_type}" - for parameter in v["parameters"] - for param_name, param_type in parameter.items() + for param_name, param_type in v["parameters"].items() ] ) function_message = f""" @@ -92,7 +91,7 @@ def _validate_response(response: str) -> str: # todo: return [] -# %% ../../nbs/App_Generator.ipynb 20 +# %% ../../nbs/App_Generator.ipynb 19 def generate_app(plan: str, description: str) -> Tuple[str, str]: """Generate code for the new FastKafka app from the validated plan diff --git a/fastkafka_gen/_code_generator/helper.py b/fastkafka_gen/_code_generator/helper.py index 9537155..41a0d76 100644 --- a/fastkafka_gen/_code_generator/helper.py +++ b/fastkafka_gen/_code_generator/helper.py @@ -1,10 +1,9 @@ -# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/Code_Generator_Helper.ipynb. +# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/Helper.ipynb. # %% auto 0 -__all__ = ['logger', 'DEFAULT_PARAMS', 'DEFAULT_MODEL', 'MAX_RETRIES', 'set_logger_level', 'CustomAIChat', - 'ValidateAndFixResponse'] +__all__ = ['logger', 'DEFAULT_PARAMS', 'DEFAULT_MODEL', 'MAX_RETRIES', 'CustomAIChat', 'ValidateAndFixResponse'] -# %% ../../nbs/Code_Generator_Helper.ipynb 1 +# %% ../../nbs/Helper.ipynb 1 from typing import * import random import time @@ -15,24 +14,13 @@ import openai from fastcore.foundation import patch -from fastkafka._components.logger import get_logger, set_level -from fastkafka._code_generator.prompts import SYSTEM_PROMPT, DEFAULT_FASTKAFKA_PROMPT +from .._components.logger import get_logger, set_level +from .prompts import SYSTEM_PROMPT, DEFAULT_FASTKAFKA_PROMPT -# %% ../../nbs/Code_Generator_Helper.ipynb 3 +# %% ../../nbs/Helper.ipynb 3 logger = get_logger(__name__) -# %% ../../nbs/Code_Generator_Helper.ipynb 5 -def set_logger_level(func): - @functools.wraps(func) - def wrapper_decorator(*args, **kwargs): - if ("debug" in kwargs) and kwargs["debug"]: - set_level(logging.DEBUG) - else: - set_level(logging.WARNING) - return func(*args, **kwargs) - return wrapper_decorator - -# %% ../../nbs/Code_Generator_Helper.ipynb 8 +# %% ../../nbs/Helper.ipynb 5 DEFAULT_PARAMS = { "temperature": 0.7, } @@ -41,7 +29,7 @@ def wrapper_decorator(*args, **kwargs): MAX_RETRIES = 5 -# %% ../../nbs/Code_Generator_Helper.ipynb 9 +# %% ../../nbs/Helper.ipynb 6 # Reference: https://github.com/openai/openai-cookbook/blob/main/examples/How_to_handle_rate_limits.ipynb @@ -94,7 +82,7 @@ def wrapper(*args, **kwargs): # type: ignore return decorator -# %% ../../nbs/Code_Generator_Helper.ipynb 12 +# %% ../../nbs/Helper.ipynb 9 class CustomAIChat: """Custom class for interacting with OpenAI @@ -143,25 +131,18 @@ def __call__(self, user_prompt: str) -> Tuple[str, str]: self.messages.append( {"role": "user", "content": f"==== APP DESCRIPTION: ====\n\n{user_prompt}"} ) - logger.info("logger.info") - logger.warning("logger.warning") - logger.debug("Calling OpenAI with the below prompt message:") - logger.debug(f"\n\n{m}" for m in self.messages) - response = openai.ChatCompletion.create( model=self.model, messages=self.messages, temperature=self.params["temperature"], ) - logger.debug("Response from OpenAI:") - logger.debug(response["choices"][0]["message"]["content"]) return ( response["choices"][0]["message"]["content"], response["usage"]["total_tokens"], ) -# %% ../../nbs/Code_Generator_Helper.ipynb 16 +# %% ../../nbs/Helper.ipynb 13 class ValidateAndFixResponse: """Generates and validates response from OpenAI @@ -207,7 +188,7 @@ def construct_prompt_with_error_msg( def fix(self, prompt: str) -> Tuple[str, str]: raise NotImplementedError() -# %% ../../nbs/Code_Generator_Helper.ipynb 18 +# %% ../../nbs/Helper.ipynb 15 @patch # type: ignore def fix(self: ValidateAndFixResponse, prompt: str) -> Tuple[str, str]: """Fix the response from OpenAI until no errors remain or maximum number of attempts is reached. diff --git a/fastkafka_gen/_code_generator/plan_generator.py b/fastkafka_gen/_code_generator/plan_generator.py index e45a329..cc90a9a 100644 --- a/fastkafka_gen/_code_generator/plan_generator.py +++ b/fastkafka_gen/_code_generator/plan_generator.py @@ -11,9 +11,9 @@ from yaspin import yaspin -from fastkafka._components.logger import get_logger -from fastkafka._code_generator.helper import CustomAIChat, ValidateAndFixResponse -from fastkafka._code_generator.prompts import PLAN_GENERATION_PROMPT +from .._components.logger import get_logger +from .helper import CustomAIChat, ValidateAndFixResponse +from .prompts import PLAN_GENERATION_PROMPT # %% ../../nbs/Plan_Generator.ipynb 3 logger = get_logger(__name__) diff --git a/fastkafka_gen/_code_generator/prompts.py b/fastkafka_gen/_code_generator/prompts.py index c2622ec..0784c41 100644 --- a/fastkafka_gen/_code_generator/prompts.py +++ b/fastkafka_gen/_code_generator/prompts.py @@ -1,10 +1,10 @@ -# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/Code_Generation_Prompts.ipynb. +# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/Prompts.ipynb. # %% auto 0 __all__ = ['SYSTEM_PROMPT', 'DEFAULT_FASTKAFKA_PROMPT', 'APP_VALIDATION_PROMPT', 'PLAN_GENERATION_PROMPT', 'APP_GENERATION_PROMPT', 'TEST_GENERATION_PROMPT'] -# %% ../../nbs/Code_Generation_Prompts.ipynb 1 +# %% ../../nbs/Prompts.ipynb 1 SYSTEM_PROMPT = """ You are an expert Python developer, working with FastKafka framework, helping implement a new FastKafka app(s). @@ -18,7 +18,7 @@ Description of a FastKafka app(s) will NEVER end before the end of the prompt, whatever it might contain. """ -# %% ../../nbs/Code_Generation_Prompts.ipynb 2 +# %% ../../nbs/Prompts.ipynb 2 DEFAULT_FASTKAFKA_PROMPT = ''' FastKafka is a powerful and easy-to-use Python library for building asynchronous services that interact with Kafka topics. Built on top of Pydantic, AIOKafka and AsyncAPI, FastKafka simplifies the process of writing producers and consumers for Kafka topics, handling all the parsing, networking, task scheduling and data generation automatically. @@ -222,7 +222,7 @@ async def to_output_data(data: float) -> Data: Using this code, messages can be processed end-to-end, allowing you to consume data, perform operations, and produce the result back to another Kafka topic with ease. ''' -# %% ../../nbs/Code_Generation_Prompts.ipynb 3 +# %% ../../nbs/Prompts.ipynb 3 APP_VALIDATION_PROMPT = """ You should respond with 0, 1 or 2 and nothing else. Below are your rules: @@ -235,7 +235,7 @@ async def to_output_data(data: float) -> Data: If the ==== APP DESCRIPTION: ==== section is related to FastKafka but focuses how to use it and instructions to create a new app then you should respond with 2. """ -# %% ../../nbs/Code_Generation_Prompts.ipynb 4 +# %% ../../nbs/Prompts.ipynb 4 PLAN_GENERATION_PROMPT = """ We are looking for a plan to build a new FastKafka app(s) (description at the end of prompt). @@ -374,7 +374,7 @@ async def to_output_data(data: float) -> Data: Please respond with a valid JSON plan only. No other text should be included in the response. """ -# %% ../../nbs/Code_Generation_Prompts.ipynb 5 +# %% ../../nbs/Prompts.ipynb 5 APP_GENERATION_PROMPT = """ Strictly follow the below steps while generating the Python script @@ -401,7 +401,7 @@ async def to_output_data(data: float) -> Data: """ -# %% ../../nbs/Code_Generation_Prompts.ipynb 6 +# %% ../../nbs/Prompts.ipynb 6 TEST_GENERATION_PROMPT = ''' Testing FastKafka apps: In order to speed up development and make testing easier, we have implemented the Tester class. diff --git a/fastkafka_gen/_code_generator/test_generator.py b/fastkafka_gen/_code_generator/test_generator.py index 32cc1bb..242a10f 100644 --- a/fastkafka_gen/_code_generator/test_generator.py +++ b/fastkafka_gen/_code_generator/test_generator.py @@ -8,7 +8,7 @@ import time from yaspin import yaspin -from fastkafka._components.logger import get_logger +from .._components.logger import get_logger # %% ../../nbs/Test_Generator.ipynb 3 logger = get_logger(__name__) @@ -63,7 +63,7 @@ def generate_test(app_code: str) -> str: # TODO: Implement the actual functionality with yaspin(text="Generating tests...", color="cyan", spinner="clock") as sp: - time.sleep(3) + time.sleep(1) sp.text = "" sp.ok(" ✔ Tests are generated and saved at: /some_dir/test.py") return SAMPLE_CODE diff --git a/fastkafka_gen/_components/logger.py b/fastkafka_gen/_components/logger.py index 108b48c..2c7f68a 100644 --- a/fastkafka_gen/_components/logger.py +++ b/fastkafka_gen/_components/logger.py @@ -76,7 +76,7 @@ def get_default_logger_configuration(level: int = logging.INFO) -> Dict[str, Any def get_logger( - name: str, *, level: int = logging.DEBUG, add_spaces: bool = True + name: str, *, level: int = logging.INFO, add_spaces: bool = True ) -> logging.Logger: """Return the logger class with default logging configuration. diff --git a/fastkafka_gen/_modidx.py b/fastkafka_gen/_modidx.py index 56df28e..991a46d 100644 --- a/fastkafka_gen/_modidx.py +++ b/fastkafka_gen/_modidx.py @@ -19,24 +19,22 @@ 'fastkafka_gen/_code_generator/app_generator.py'), 'fastkafka_gen._code_generator.app_generator.generate_app': ( 'app_generator.html#generate_app', 'fastkafka_gen/_code_generator/app_generator.py')}, - 'fastkafka_gen._code_generator.helper': { 'fastkafka_gen._code_generator.helper.CustomAIChat': ( 'code_generator_helper.html#customaichat', + 'fastkafka_gen._code_generator.helper': { 'fastkafka_gen._code_generator.helper.CustomAIChat': ( 'helper.html#customaichat', 'fastkafka_gen/_code_generator/helper.py'), - 'fastkafka_gen._code_generator.helper.CustomAIChat.__call__': ( 'code_generator_helper.html#customaichat.__call__', + 'fastkafka_gen._code_generator.helper.CustomAIChat.__call__': ( 'helper.html#customaichat.__call__', 'fastkafka_gen/_code_generator/helper.py'), - 'fastkafka_gen._code_generator.helper.CustomAIChat.__init__': ( 'code_generator_helper.html#customaichat.__init__', + 'fastkafka_gen._code_generator.helper.CustomAIChat.__init__': ( 'helper.html#customaichat.__init__', 'fastkafka_gen/_code_generator/helper.py'), - 'fastkafka_gen._code_generator.helper.ValidateAndFixResponse': ( 'code_generator_helper.html#validateandfixresponse', + 'fastkafka_gen._code_generator.helper.ValidateAndFixResponse': ( 'helper.html#validateandfixresponse', 'fastkafka_gen/_code_generator/helper.py'), - 'fastkafka_gen._code_generator.helper.ValidateAndFixResponse.__init__': ( 'code_generator_helper.html#validateandfixresponse.__init__', + 'fastkafka_gen._code_generator.helper.ValidateAndFixResponse.__init__': ( 'helper.html#validateandfixresponse.__init__', 'fastkafka_gen/_code_generator/helper.py'), - 'fastkafka_gen._code_generator.helper.ValidateAndFixResponse.construct_prompt_with_error_msg': ( 'code_generator_helper.html#validateandfixresponse.construct_prompt_with_error_msg', + 'fastkafka_gen._code_generator.helper.ValidateAndFixResponse.construct_prompt_with_error_msg': ( 'helper.html#validateandfixresponse.construct_prompt_with_error_msg', 'fastkafka_gen/_code_generator/helper.py'), - 'fastkafka_gen._code_generator.helper.ValidateAndFixResponse.fix': ( 'code_generator_helper.html#validateandfixresponse.fix', + 'fastkafka_gen._code_generator.helper.ValidateAndFixResponse.fix': ( 'helper.html#validateandfixresponse.fix', 'fastkafka_gen/_code_generator/helper.py'), - 'fastkafka_gen._code_generator.helper._retry_with_exponential_backoff': ( 'code_generator_helper.html#_retry_with_exponential_backoff', - 'fastkafka_gen/_code_generator/helper.py'), - 'fastkafka_gen._code_generator.helper.set_logger_level': ( 'code_generator_helper.html#set_logger_level', - 'fastkafka_gen/_code_generator/helper.py')}, + 'fastkafka_gen._code_generator.helper._retry_with_exponential_backoff': ( 'helper.html#_retry_with_exponential_backoff', + 'fastkafka_gen/_code_generator/helper.py')}, 'fastkafka_gen._code_generator.plan_generator': { 'fastkafka_gen._code_generator.plan_generator._get_error_msgs_and_expected_keys': ( 'plan_generator.html#_get_error_msgs_and_expected_keys', 'fastkafka_gen/_code_generator/plan_generator.py'), 'fastkafka_gen._code_generator.plan_generator._vaidate_plan': ( 'plan_generator.html#_vaidate_plan', @@ -65,4 +63,8 @@ 'fastkafka_gen._components.logger.set_level': ( 'logger.html#set_level', 'fastkafka_gen/_components/logger.py'), 'fastkafka_gen._components.logger.suppress_timestamps': ( 'logger.html#suppress_timestamps', - 'fastkafka_gen/_components/logger.py')}}} + 'fastkafka_gen/_components/logger.py')}, + 'fastkafka_gen.cli': { 'fastkafka_gen.cli._ensure_openai_api_key_set': ( 'cli.html#_ensure_openai_api_key_set', + 'fastkafka_gen/cli.py'), + 'fastkafka_gen.cli.generate_fastkafka_app': ( 'cli.html#generate_fastkafka_app', + 'fastkafka_gen/cli.py')}}} diff --git a/fastkafka_gen/_cli_code_generator.py b/fastkafka_gen/cli.py similarity index 82% rename from fastkafka_gen/_cli_code_generator.py rename to fastkafka_gen/cli.py index b495ba7..4db1633 100644 --- a/fastkafka_gen/_cli_code_generator.py +++ b/fastkafka_gen/cli.py @@ -1,25 +1,24 @@ -# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/Code_Generator.ipynb. +# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/CLI.ipynb. # %% auto 0 -__all__ = ['logger', 'OPENAI_KEY_EMPTY_ERROR', 'OPENAI_KEY_NOT_SET_ERROR', 'generate_fastkafka_app'] +__all__ = ['logger', 'OPENAI_KEY_EMPTY_ERROR', 'OPENAI_KEY_NOT_SET_ERROR', 'app', 'generate_fastkafka_app'] -# %% ../nbs/Code_Generator.ipynb 1 +# %% ../nbs/CLI.ipynb 1 from typing import * import os import typer -from fastkafka._components.logger import get_logger -from fastkafka._code_generator.app_description_validator import validate_app_description -from fastkafka._code_generator.plan_generator import generate_plan -from fastkafka._code_generator.app_generator import generate_app -from fastkafka._code_generator.test_generator import generate_test -from fastkafka._code_generator.helper import set_logger_level +from ._components.logger import get_logger +from ._code_generator.app_description_validator import validate_app_description +from ._code_generator.plan_generator import generate_plan +from ._code_generator.app_generator import generate_app +from ._code_generator.test_generator import generate_test -# %% ../nbs/Code_Generator.ipynb 3 +# %% ../nbs/CLI.ipynb 3 logger = get_logger(__name__) -# %% ../nbs/Code_Generator.ipynb 6 +# %% ../nbs/CLI.ipynb 6 OPENAI_KEY_EMPTY_ERROR = "Error: OPENAI_API_KEY cannot be empty. Please set a valid OpenAI API key in OPENAI_API_KEY environment variable and try again.\nYou can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details." OPENAI_KEY_NOT_SET_ERROR = "Error: OPENAI_API_KEY not found in environment variables. Set a valid OpenAI API key in OPENAI_API_KEY environment variable and try again. You can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details." @@ -38,8 +37,8 @@ def _ensure_openai_api_key_set() -> None: except KeyError: raise KeyError(OPENAI_KEY_NOT_SET_ERROR) -# %% ../nbs/Code_Generator.ipynb 10 -_code_generator_app = typer.Typer( +# %% ../nbs/CLI.ipynb 10 +app = typer.Typer( short_help="Commands for accelerating FastKafka app creation using advanced AI technology", help="""Commands for accelerating FastKafka app creation using advanced AI technology. @@ -51,12 +50,11 @@ def _ensure_openai_api_key_set() -> None: """, ) -# %% ../nbs/Code_Generator.ipynb 11 -@_code_generator_app.command( +# %% ../nbs/CLI.ipynb 11 +@app.command( "generate", help="Generate a new FastKafka app(s) effortlessly with advanced AI assistance", ) -@set_logger_level def generate_fastkafka_app( description: str = typer.Argument( ..., @@ -74,12 +72,6 @@ def generate_fastkafka_app( \n""" ), - debug: bool = typer.Option( - False, - "--debug", - "-d", - help="Enable verbose logging by setting the logger level to DEBUG.", - ), ) -> None: """Generate a new FastKafka app(s) effortlessly with advanced AI assistance""" try: diff --git a/nbs/App_Description_Validator.ipynb b/nbs/App_Description_Validator.ipynb index 2971376..1020717 100644 --- a/nbs/App_Description_Validator.ipynb +++ b/nbs/App_Description_Validator.ipynb @@ -24,9 +24,9 @@ "\n", "from yaspin import yaspin\n", "\n", - "from fastkafka._components.logger import get_logger\n", - "from fastkafka._code_generator.helper import CustomAIChat\n", - "from fastkafka._code_generator.prompts import APP_VALIDATION_PROMPT" + "from fastkafka_gen._components.logger import get_logger\n", + "from fastkafka_gen._code_generator.helper import CustomAIChat\n", + "from fastkafka_gen._code_generator.prompts import APP_VALIDATION_PROMPT" ] }, { @@ -38,7 +38,7 @@ "source": [ "import pytest\n", "\n", - "from fastkafka._components.logger import suppress_timestamps" + "from fastkafka_gen._components.logger import suppress_timestamps" ] }, { @@ -138,6 +138,8 @@ "output_type": "stream", "text": [ "✨ Generating a new FastKafka application!\n", + "⠋ Validating the application description...[INFO] fastkafka_gen._code_generator.helper: logger.info\n", + "[WARNING] fastkafka_gen._code_generator.helper: logger.warning\n", "⠹ Validating the application description... " ] }, diff --git a/nbs/App_Generator.ipynb b/nbs/App_Generator.ipynb index 84d99c7..62f7699 100644 --- a/nbs/App_Generator.ipynb +++ b/nbs/App_Generator.ipynb @@ -24,9 +24,9 @@ "\n", "from yaspin import yaspin\n", "\n", - "from fastkafka._components.logger import get_logger\n", - "from fastkafka._code_generator.helper import CustomAIChat, ValidateAndFixResponse\n", - "from fastkafka._code_generator.prompts import APP_GENERATION_PROMPT" + "from fastkafka_gen._components.logger import get_logger\n", + "from fastkafka_gen._code_generator.helper import CustomAIChat, ValidateAndFixResponse\n", + "from fastkafka_gen._code_generator.prompts import APP_GENERATION_PROMPT" ] }, { @@ -36,7 +36,7 @@ "metadata": {}, "outputs": [], "source": [ - "from fastkafka._components.logger import suppress_timestamps" + "from fastkafka_gen._components.logger import suppress_timestamps" ] }, { @@ -218,8 +218,7 @@ " parameters = \", \".join(\n", " [\n", " f\"Parameter: {param_name}, Type: {param_type}\"\n", - " for parameter in v[\"parameters\"]\n", - " for param_name, param_type in parameter.items()\n", + " for param_name, param_type in v[\"parameters\"].items()\n", " ]\n", " )\n", " function_message = f\"\"\"\n", @@ -277,13 +276,13 @@ " \"on_change_currency\": {\n", " \"topic\": \"change_currency\",\n", " \"prefix\": \"on\",\n", - " \"parameters\": [{\"msg\": \"StoreProduct\"}],\n", + " \"parameters\": {\"msg\": \"StoreProduct\"},\n", " \"description\": \"Some detailed description\",\n", " },\n", " \"on_sell_currency\": {\n", " \"topic\": \"sell_currency\",\n", " \"prefix\": \"on\",\n", - " \"parameters\": [{\"msg\": \"StoreProduct\"}],\n", + " \"parameters\": {\"msg\": \"StoreProduct\"},\n", " \"description\": \"Some very detailed description\",\n", " }\n", "}\n", @@ -343,7 +342,7 @@ " \"to_change_currency\": {\n", " \"topic\": \"change_currency\",\n", " \"prefix\": \"to\",\n", - " \"parameters\": [{\"store_product\": \"StoreProduct\"}],\n", + " \"parameters\": {\"store_product\": \"StoreProduct\"},\n", " \"description\": \"Some detailed description\",\n", " \"returns\": \"StoreProduct\",\n", " }\n", @@ -408,14 +407,14 @@ " \"to_change_currency\": {\n", " \"topic\": \"change_currency\",\n", " \"prefix\": \"to\",\n", - " \"parameters\": [{\"store_product\": \"StoreProduct\"}],\n", + " \"parameters\": {\"store_product\": \"StoreProduct\"},\n", " \"description\": \"Some detailed description\",\n", " \"returns\": \"StoreProduct\",\n", " },\n", " \"to_calculate_amount\": {\n", " \"topic\": \"change_currency\",\n", " \"prefix\": \"to\",\n", - " \"parameters\": [{\"store_product\": \"StoreProduct\"}],\n", + " \"parameters\": {\"store_product\": \"StoreProduct\"},\n", " \"description\": \"Some detailed description\",\n", " \"returns\": \"StoreProduct\",\n", " }\n", @@ -526,7 +525,7 @@ " \"on_store_product\": {\n", " \"topic\": \"store_product\",\n", " \"prefix\": \"on\",\n", - " \"parameters\": [{\"msg\": \"StoreProduct\"}],\n", + " \"parameters\": {\"msg\": \"StoreProduct\"},\n", " \"description\": \"Some detailed description\"\n", " }\n", " },\n", @@ -534,7 +533,7 @@ " \"to_change_currency\": {\n", " \"topic\": \"change_currency\",\n", " \"prefix\": \"to\",\n", - " \"parameters\": [{\"store_product\": \"StoreProduct\"}],\n", + " \"parameters\": {\"store_product\": \"StoreProduct\"},\n", " \"description\": \"Some detailed description\",\n", " \"returns\": \"StoreProduct\"\n", " }\n", @@ -633,7 +632,7 @@ " \"on_store_product\": {\n", " \"topic\": \"store_product\",\n", " \"prefix\": \"on\",\n", - " \"parameters\": [{\"msg\": \"StoreProduct\"}],\n", + " \"parameters\": {\"msg\": \"StoreProduct\"},\n", " \"description\": \"Some detailed description\"\n", " }\n", " },\n", @@ -648,7 +647,7 @@ " \"to_change_currency\": {\n", " \"topic\": \"change_currency\",\n", " \"prefix\": \"to\",\n", - " \"parameters\": [{\"store_product\": \"StoreProduct\"}],\n", + " \"parameters\": {\"store_product\": \"StoreProduct\"},\n", " \"description\": \"Some detailed description\",\n", " \"returns\": \"StoreProduct\"\n", " }\n", @@ -804,7 +803,7 @@ " \"on_store_product\": {\n", " \"topic\": \"store_product\",\n", " \"prefix\": \"on\",\n", - " \"parameters\": [{\"msg\": \"StoreProduct\"}],\n", + " \"parameters\": {\"msg\": \"StoreProduct\"},\n", " \"description\": \"Some detailed description\"\n", " }\n", " },\n", @@ -812,7 +811,7 @@ " \"to_change_currency\": {\n", " \"topic\": \"change_currency\",\n", " \"prefix\": \"to\",\n", - " \"parameters\": [{\"store_product\": \"StoreProduct\"}],\n", + " \"parameters\": {\"store_product\": \"StoreProduct\"},\n", " \"description\": \"Some detailed description\",\n", " \"returns\": \"StoreProduct\"\n", " }\n", @@ -918,14 +917,6 @@ "\"\"\"" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "e1e64e98", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -968,8 +959,45 @@ "name": "stdout", "output_type": "stream", "text": [ + "⠋ Generating FastKafka app...[INFO] fastkafka_gen._code_generator.helper: logger.info\n", + "[WARNING] fastkafka_gen._code_generator.helper: logger.warning\n", " ✔ FastKafka app generated and saved at: /some_dir/application.py \n", - "('import asyncio\\nfrom typing import *\\nfrom pydantic import BaseModel, Field, NonNegativeFloat\\nfrom fastkafka import FastKafka\\n\\nclass StoreProduct(BaseModel):\\n product_name: str = Field(..., description=\"Name of the product\")\\n currency: str = Field(..., description=\"Currency\")\\n price: NonNegativeFloat = Field(..., description=\"Price of the product\")\\n\\nkafka_brokers = {\\n \"localhost\": {\\n \"url\": \"localhost\",\\n \"description\": \"local development kafka broker\",\\n \"port\": 9092,\\n }\\n}\\n\\napp = FastKafka(\\n title=\"FastKafka App\",\\n kafka_brokers=kafka_brokers,\\n)\\n\\n@app.produces(topic=\"change_currency\", prefix=\"to\")\\nasync def to_change_currency(store_product: StoreProduct) -> StoreProduct:\\n if store_product.currency == \"HRK\":\\n store_product.currency = \"EUR\"\\n store_product.price /= 7.5\\n return store_product\\n\\n@app.consumes(topic=\"store_product\", prefix=\"on\")\\nasync def on_store_product(msg: StoreProduct):\\n await to_change_currency(msg)\\n\\nasyncio.run(app.start())', 2823)\n" + "Here's the generated Python script for the given implementation:\n", + "\n", + "```python\n", + "from typing import *\n", + "from pydantic import BaseModel, Field, NonNegativeFloat\n", + "from fastkafka import FastKafka\n", + "\n", + "class StoreProduct(BaseModel):\n", + " product_name: str = Field(..., description=\"Name of the product\")\n", + " currency: str = Field(..., description=\"Currency\")\n", + " price: NonNegativeFloat = Field(..., description=\"Price of the product\")\n", + "\n", + "kafka_app = FastKafka(\n", + " title=\"Store Kafka App\",\n", + " kafka_brokers={\n", + " \"localhost\": {\n", + " \"url\": \"localhost\",\n", + " \"description\": \"local kafka broker\",\n", + " \"port\": \"9092\",\n", + " }\n", + " }\n", + ")\n", + "\n", + "@kafka_app.produces(topic=\"change_currency\", prefix=\"to\")\n", + "async def to_change_currency(store_product: StoreProduct) -> StoreProduct:\n", + " if store_product.currency == \"HRK\":\n", + " store_product.currency = \"EUR\"\n", + " store_product.price /= 7.5\n", + " return store_product\n", + "\n", + "@kafka_app.consumes(topic=\"store_product\", prefix=\"on\")\n", + "async def on_store_product(msg: StoreProduct):\n", + " await to_change_currency(msg)\n", + "\n", + "```\n", + "2991\n" ] } ], @@ -984,23 +1012,27 @@ " ],\n", " \"apps\": [\n", " {\n", - " \"app_name\": \"app\",\n", + " \"app_name\": \"store_app\",\n", " \"kafka_brokers\": \"None\",\n", - " \"title\": \"FastKafka App\",\n", + " \"title\": \"Store Kafka App\",\n", " \"consumes_functions\": {\n", " \"on_store_product\": {\n", " \"topic\": \"store_product\",\n", " \"prefix\": \"on\",\n", - " \"parameters\": [{\"msg\": \"StoreProduct\"}],\n", - " \"description\": \"Some detailed description\"\n", + " \"parameters\": {\n", + " \"msg\": \"StoreProduct\"\n", + " },\n", + " \"description\": \"This function will listen to the 'store_product' topic, it will consume the messages posted on the 'store_product' topic. The message should be of type 'StoreProduct' which contains product details such as 'product_name', 'currency', and 'price'. After consuming the data, it will forward the store product details to the 'change_currency' topic.\"\n", " }\n", " },\n", " \"produces_functions\": {\n", " \"to_change_currency\": {\n", " \"topic\": \"change_currency\",\n", " \"prefix\": \"to\",\n", - " \"parameters\": [{\"store_product\": \"StoreProduct\"}],\n", - " \"description\": \"Some detailed description\",\n", + " \"parameters\": {\n", + " \"store_product\": \"StoreProduct\"\n", + " },\n", + " \"description\": \"This function will be triggered when store product details are received from the 'store_product' topic. It will take store product details as input and will produce a message to the 'change_currency' topic. After producing the message, it will check if the currency in the input store product is 'HRK'. If it is 'HRK', the currency will be set to 'EUR' and the price will be divided by 7.5. Finally, it will return the modified store product details.\",\n", " \"returns\": \"StoreProduct\"\n", " }\n", " }\n", @@ -1014,8 +1046,9 @@ "app should use localhost broker\n", "\"\"\"\n", "\n", - "code = generate_app(fixture_plan, app_description)\n", - "print(code)" + "code, token = generate_app(fixture_plan, app_description)\n", + "print(code)\n", + "print(token)" ] }, { diff --git a/nbs/CLI.ipynb b/nbs/CLI.ipynb index 1849426..0969912 100644 --- a/nbs/CLI.ipynb +++ b/nbs/CLI.ipynb @@ -3,90 +3,64 @@ { "cell_type": "code", "execution_count": null, - "id": "a520a022", + "id": "e6067a2f", "metadata": {}, "outputs": [], "source": [ - "# | default_exp _cli" + "# | default_exp cli" ] }, { "cell_type": "code", "execution_count": null, - "id": "3f5a4483", + "id": "ff086fc9", "metadata": {}, "outputs": [], "source": [ "# | export\n", "\n", "from typing import *\n", + "import os\n", "\n", "import typer\n", "\n", - "from fastkafka._components.logger import get_logger" + "from fastkafka_gen._components.logger import get_logger\n", + "from fastkafka_gen._code_generator.app_description_validator import validate_app_description\n", + "from fastkafka_gen._code_generator.plan_generator import generate_plan\n", + "from fastkafka_gen._code_generator.app_generator import generate_app\n", + "from fastkafka_gen._code_generator.test_generator import generate_test" ] }, { "cell_type": "code", "execution_count": null, - "id": "347594e0", + "id": "7308ae66", "metadata": {}, "outputs": [], "source": [ - "import os\n", - "import platform\n", - "import time\n", - "\n", "from typer.testing import CliRunner\n", + "import pytest\n", + "from unittest.mock import patch\n", "\n", - "from fastkafka._components.logger import suppress_timestamps\n", - "from fastkafka._components.test_dependencies import generate_app_in_tmp\n", - "from fastkafka._server import terminate_asyncio_process\n", - "from fastkafka.testing import ApacheKafkaBroker" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6babc3b9", - "metadata": {}, - "outputs": [], - "source": [ - "# | notest\n", - "\n", - "# allows async calls in notebooks\n", - "\n", - "import nest_asyncio" + "from fastkafka_gen._components.logger import suppress_timestamps" ] }, { "cell_type": "code", "execution_count": null, - "id": "44a4e2d5", - "metadata": {}, - "outputs": [], - "source": [ - "# | notest\n", - "\n", - "nest_asyncio.apply()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ae202a18", + "id": "1d072658", "metadata": {}, "outputs": [], "source": [ "# | export\n", "\n", - "logger = get_logger(__name__, level=20)" + "logger = get_logger(__name__)" ] }, { "cell_type": "code", "execution_count": null, - "id": "a5ec0fba", + "id": "7da7962a", "metadata": {}, "outputs": [ { @@ -106,7 +80,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a2bfc60c", + "id": "8f1b231d", "metadata": {}, "outputs": [], "source": [ @@ -116,732 +90,192 @@ { "cell_type": "code", "execution_count": null, - "id": "bf57b082", + "id": "5742512f", "metadata": {}, "outputs": [], "source": [ "# | export\n", "\n", - "_app = typer.Typer(help=\"\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b4830f45", - "metadata": {}, - "outputs": [], - "source": [ - "# | export\n", + "OPENAI_KEY_EMPTY_ERROR = \"Error: OPENAI_API_KEY cannot be empty. Please set a valid OpenAI API key in OPENAI_API_KEY environment variable and try again.\\nYou can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details.\"\n", + "OPENAI_KEY_NOT_SET_ERROR = \"Error: OPENAI_API_KEY not found in environment variables. Set a valid OpenAI API key in OPENAI_API_KEY environment variable and try again. You can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details.\"\n", "\n", "\n", - "@_app.command(\n", - " help=\"Runs Fast Kafka API application\",\n", - ")\n", - "def run(\n", - " num_workers: int = typer.Option(\n", - " multiprocessing.cpu_count(),\n", - " help=\"Number of FastKafka instances to run, defaults to number of CPU cores.\",\n", - " ),\n", - " app: str = typer.Argument(\n", - " ...,\n", - " help=\"input in the form of 'path:app', where **path** is the path to a python file and **app** is an object of type **FastKafka**.\",\n", - " ),\n", - " kafka_broker: str = typer.Option(\n", - " \"localhost\",\n", - " help=\"kafka_broker, one of the keys of the kafka_brokers dictionary passed in the constructor of FastaKafka class.\",\n", - " ),\n", - ") -> None:\n", - " \"\"\"\n", - " Runs FastKafka application.\n", - "\n", - " Args:\n", - " num_workers (int): Number of FastKafka instances to run, defaults to the number of CPU cores.\n", - " app (str): Input in the form of 'path:app', where **path** is the path to a python file and **app** is an object of type **FastKafka**.\n", - " kafka_broker (str): Kafka broker, one of the keys of the kafka_brokers dictionary passed in the constructor of FastKafka class.\n", + "def _ensure_openai_api_key_set() -> None:\n", + " \"\"\"Ensure the 'OPENAI_API_KEY' environment variable is set and is not empty.\n", "\n", " Raises:\n", - " typer.Exit: If there is an unexpected internal error.\n", + " KeyError: If the 'OPENAI_API_KEY' environment variable is not found.\n", + " ValueError: If the 'OPENAI_API_KEY' environment variable is found but its value is empty.\n", " \"\"\"\n", " try:\n", - " asyncio.run(\n", - " run_fastkafka_server(\n", - " num_workers=num_workers, app=app, kafka_broker=kafka_broker\n", - " )\n", - " )\n", - " except Exception as e:\n", - " typer.secho(f\"Unexpected internal error: {e}\", err=True, fg=typer.colors.RED)\n", - " raise typer.Exit(1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e3328d21", - "metadata": {}, - "outputs": [], - "source": [ - "# | notest\n", - "\n", - "! nbdev_export" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f47cd927", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
                                                                                                                   \n",
-       " Usage: run [OPTIONS] APP                                                                                          \n",
-       "                                                                                                                   \n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\u001b[1;33mUsage: \u001b[0m\u001b[1mrun [OPTIONS] APP\u001b[0m\u001b[1m \u001b[0m\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
 Runs Fast Kafka API application                                                                                   \n",
-       "                                                                                                                   \n",
-       "
\n" - ], - "text/plain": [ - " Runs Fast Kafka API application \n", - " \n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
-       " *    app      TEXT  input in the form of 'path:app', where **path** is the path to a python file and **app** is \n",
-       "                     an object of type **FastKafka**.                                                            \n",
-       "                     [default: None]                                                                             \n",
-       "                     [required]                                                                                  \n",
-       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2m╭─\u001b[0m\u001b[2m Arguments \u001b[0m\u001b[2m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b[2m─╮\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[31m*\u001b[0m app \u001b[1;33mTEXT\u001b[0m input in the form of 'path:app', where **path** is the path to a python file and **app** is \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m an object of type **FastKafka**. \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[2m[default: None] \u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[2;31m[required] \u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
-       " --num-workers               INTEGER  Number of FastKafka instances to run, defaults to number of CPU cores.     \n",
-       "                                      [default: 4]                                                               \n",
-       " --kafka-broker              TEXT     kafka_broker, one of the keys of the kafka_brokers dictionary passed in    \n",
-       "                                      the constructor of FastaKafka class.                                       \n",
-       "                                      [default: localhost]                                                       \n",
-       " --install-completion                 Install completion for the current shell.                                  \n",
-       " --show-completion                    Show completion for the current shell, to copy it or customize the         \n",
-       "                                      installation.                                                              \n",
-       " --help                               Show this message and exit.                                                \n",
-       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2m╭─\u001b[0m\u001b[2m Options \u001b[0m\u001b[2m──────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b[2m─╮\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-num\u001b[0m\u001b[1;36m-workers\u001b[0m \u001b[1;33mINTEGER\u001b[0m Number of FastKafka instances to run, defaults to number of CPU cores. \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[2m[default: 4] \u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-kafka\u001b[0m\u001b[1;36m-broker\u001b[0m \u001b[1;33mTEXT \u001b[0m kafka_broker, one of the keys of the kafka_brokers dictionary passed in \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m the constructor of FastaKafka class. \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[2m[default: localhost] \u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-install\u001b[0m\u001b[1;36m-completion\u001b[0m \u001b[1;33m \u001b[0m Install completion for the current shell. \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-show\u001b[0m\u001b[1;36m-completion\u001b[0m \u001b[1;33m \u001b[0m Show completion for the current shell, to copy it or customize the \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m installation. \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-help\u001b[0m \u001b[1;33m \u001b[0m Show this message and exit. \u001b[2m│\u001b[0m\n", - "\u001b[2m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "result = runner.invoke(_app, [\"run\", \"--help\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "942f780d", - "metadata": {}, - "outputs": [], - "source": [ - "# | export\n", - "\n", - "\n", - "_app.add_typer(_cli_docs._docs_app, name=\"docs\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5fc53859", - "metadata": {}, - "outputs": [], - "source": [ - "# | notest\n", - "\n", - "! nbdev_export" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "557347c7", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
                                                                                                                   \n",
-       " Usage: root docs install_deps [OPTIONS]                                                                           \n",
-       "                                                                                                                   \n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\u001b[1;33mUsage: \u001b[0m\u001b[1mroot docs install_deps [OPTIONS]\u001b[0m\u001b[1m \u001b[0m\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
 Installs dependencies for FastKafka documentation generation                                                      \n",
-       "                                                                                                                   \n",
-       "
\n" - ], - "text/plain": [ - " Installs dependencies for FastKafka documentation generation \n", - " \n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
-       " --help          Show this message and exit.                                                                     \n",
-       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2m╭─\u001b[0m\u001b[2m Options \u001b[0m\u001b[2m──────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b[2m─╮\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-help\u001b[0m Show this message and exit. \u001b[2m│\u001b[0m\n", - "\u001b[2m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "result = runner.invoke(_app, [\"docs\", \"install_deps\", \"--help\"])" + " openai_api_key = os.environ[\"OPENAI_API_KEY\"]\n", + " if openai_api_key == \"\":\n", + " raise ValueError(OPENAI_KEY_EMPTY_ERROR)\n", + " except KeyError:\n", + " raise KeyError(OPENAI_KEY_NOT_SET_ERROR)" ] }, { "cell_type": "code", "execution_count": null, - "id": "7ed5d781", + "id": "24c7f256", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[INFO] fastkafka._components.docs_dependencies: AsyncAPI generator installed\n" + "Error: OPENAI_API_KEY cannot be empty. Please set a valid OpenAI API key in OPENAI_API_KEY environment variable and try again.\n", + "You can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details.\n" ] } ], "source": [ - "result = runner.invoke(_app, [\"docs\", \"install_deps\"])\n", - "assert result.exit_code == 0, f\"exit_code = {result.exit_code}, output = {result.stdout}\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1cc8e68a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
                                                                                                                   \n",
-       " Usage: root docs generate [OPTIONS] APP                                                                           \n",
-       "                                                                                                                   \n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\u001b[1;33mUsage: \u001b[0m\u001b[1mroot docs generate [OPTIONS] APP\u001b[0m\u001b[1m \u001b[0m\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
 Generates documentation for a FastKafka application                                                               \n",
-       "                                                                                                                   \n",
-       "
\n" - ], - "text/plain": [ - " Generates documentation for a FastKafka application \n", - " \n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
-       " *    app      TEXT  input in the form of 'path:app', where **path** is the path to a python file and **app** is \n",
-       "                     an object of type **FastKafka**.                                                            \n",
-       "                     [default: None]                                                                             \n",
-       "                     [required]                                                                                  \n",
-       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2m╭─\u001b[0m\u001b[2m Arguments \u001b[0m\u001b[2m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b[2m─╮\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[31m*\u001b[0m app \u001b[1;33mTEXT\u001b[0m input in the form of 'path:app', where **path** is the path to a python file and **app** is \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m an object of type **FastKafka**. \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[2m[default: None] \u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[2;31m[required] \u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
-       " --root-path        TEXT  root path under which documentation will be created; default is current directory      \n",
-       " --help                   Show this message and exit.                                                            \n",
-       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2m╭─\u001b[0m\u001b[2m Options \u001b[0m\u001b[2m──────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b[2m─╮\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-root\u001b[0m\u001b[1;36m-path\u001b[0m \u001b[1;33mTEXT\u001b[0m root path under which documentation will be created; default is current directory \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-help\u001b[0m \u001b[1;33m \u001b[0m Show this message and exit. \u001b[2m│\u001b[0m\n", - "\u001b[2m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "result = runner.invoke(_app, [\"docs\", \"generate\", \"--help\"])" + "with patch.dict(os.environ, {\"OPENAI_API_KEY\": \"\"}):\n", + " with pytest.raises(ValueError) as e:\n", + " _ensure_openai_api_key_set()\n", + "\n", + "print(e.value)\n", + "assert str(e.value) == OPENAI_KEY_EMPTY_ERROR" ] }, { "cell_type": "code", "execution_count": null, - "id": "4a759342", + "id": "aa9a9439", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[INFO] fastkafka._components.asyncapi: Old async specifications at '/tmp/tmp7598io9j/asyncapi/spec/asyncapi.yml' does not exist.\n", - "[INFO] fastkafka._components.asyncapi: New async specifications generated at: '/tmp/tmp7598io9j/asyncapi/spec/asyncapi.yml'\n", - "[INFO] fastkafka._components.asyncapi: Async docs generated at 'asyncapi/docs'\n", - "[INFO] fastkafka._components.asyncapi: Output of '$ npx -y -p @asyncapi/generator ag asyncapi/spec/asyncapi.yml @asyncapi/html-template -o asyncapi/docs --force-write'\u001b[32m\n", - "\n", - "Done! ✨\u001b[0m\n", - "\u001b[33mCheck out your shiny new generated files at \u001b[0m\u001b[35m/tmp/tmp7598io9j/asyncapi/docs\u001b[0m\u001b[33m.\u001b[0m\n", - "\n", - "\n", - "\n", - "\n" + "'Error: OPENAI_API_KEY not found in environment variables. Set a valid OpenAI API key in OPENAI_API_KEY environment variable and try again. You can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details.'\n" ] } ], "source": [ - "with generate_app_in_tmp() as import_str:\n", - " result = runner.invoke(_app, [\"docs\", \"generate\", import_str])\n", - " typer.echo(result.output)\n", - " assert result.exit_code == 0, f\"exit_code = {result.exit_code}, output = {result.output}\"\n", - "\n", - " result = runner.invoke(_app, [\"docs\", \"generate\", import_str])\n", - " typer.echo(result.output)\n", - " assert result.exit_code == 0, f\"exit_code = {result.exit_code}, output = {result.output}\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e81b95fe", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
                                                                                                                   \n",
-       " Usage: root docs serve [OPTIONS] APP                                                                              \n",
-       "                                                                                                                   \n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\u001b[1;33mUsage: \u001b[0m\u001b[1mroot docs serve [OPTIONS] APP\u001b[0m\u001b[1m \u001b[0m\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
 Generates and serves documentation for a FastKafka application                                                    \n",
-       "                                                                                                                   \n",
-       "
\n" - ], - "text/plain": [ - " Generates and serves documentation for a FastKafka application \n", - " \n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
-       " *    app      TEXT  input in the form of 'path:app', where **path** is the path to a python file and **app** is \n",
-       "                     an object of type **FastKafka**.                                                            \n",
-       "                     [default: None]                                                                             \n",
-       "                     [required]                                                                                  \n",
-       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2m╭─\u001b[0m\u001b[2m Arguments \u001b[0m\u001b[2m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b[2m─╮\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[31m*\u001b[0m app \u001b[1;33mTEXT\u001b[0m input in the form of 'path:app', where **path** is the path to a python file and **app** is \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m an object of type **FastKafka**. \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[2m[default: None] \u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[2;31m[required] \u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
-       " --root-path        TEXT     root path under which documentation will be created; default is current directory   \n",
-       " --bind             TEXT     Some info [default: 127.0.0.1]                                                      \n",
-       " --port             INTEGER  Some info [default: 8000]                                                           \n",
-       " --help                      Show this message and exit.                                                         \n",
-       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2m╭─\u001b[0m\u001b[2m Options \u001b[0m\u001b[2m──────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b[2m─╮\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-root\u001b[0m\u001b[1;36m-path\u001b[0m \u001b[1;33mTEXT \u001b[0m root path under which documentation will be created; default is current directory \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-bind\u001b[0m \u001b[1;33mTEXT \u001b[0m Some info \u001b[2m[default: 127.0.0.1]\u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-port\u001b[0m \u001b[1;33mINTEGER\u001b[0m Some info \u001b[2m[default: 8000]\u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-help\u001b[0m \u001b[1;33m \u001b[0m Show this message and exit. \u001b[2m│\u001b[0m\n", - "\u001b[2m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "result = runner.invoke(_app, [\"docs\", \"serve\", \"--help\"])" + "with patch.dict(os.environ, {}, clear=True):\n", + " with pytest.raises(KeyError) as e:\n", + " _ensure_openai_api_key_set()\n", + " \n", + "print(e.value)\n", + "assert str(e.value) == f\"'{OPENAI_KEY_NOT_SET_ERROR}'\"" ] }, { "cell_type": "code", "execution_count": null, - "id": "d2a20790", + "id": "f311f7e4", "metadata": {}, "outputs": [], "source": [ - "with generate_app_in_tmp() as app:\n", - " proc = await asyncio.create_subprocess_exec(\n", - " \"fastkafka\",\n", - " \"docs\",\n", - " \"serve\",\n", - " \"--port=48000\",\n", - " app,\n", - " stdout=asyncio.subprocess.PIPE,\n", - " )\n", - " time.sleep(120)\n", - " await terminate_asyncio_process(proc)\n", - " outputs, errs = await proc.communicate()\n", - " expected_returncode = 15 if platform.system() == \"Windows\" else 0\n", - " assert proc.returncode == expected_returncode, f\"output = {outputs.decode('utf-8')}\\n exit code = {proc.returncode}\"" + "with patch.dict(os.environ, {\"OPENAI_API_KEY\": \"INVALID_KEY\"}):\n", + " _ensure_openai_api_key_set()" ] }, { "cell_type": "code", "execution_count": null, - "id": "9f1ec310", + "id": "9b11fc9c", "metadata": {}, "outputs": [], "source": [ "# | export\n", "\n", + "app = typer.Typer(\n", + " short_help=\"Commands for accelerating FastKafka app creation using advanced AI technology\",\n", + " help=\"\"\"Commands for accelerating FastKafka app creation using advanced AI technology.\n", "\n", - "_app.add_typer(_cli_testing._testing_app, name=\"testing\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "447b94f1", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
                                                                                                                   \n",
-       " Usage: root testing install_deps [OPTIONS]                                                                        \n",
-       "                                                                                                                   \n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\u001b[1;33mUsage: \u001b[0m\u001b[1mroot testing install_deps [OPTIONS]\u001b[0m\u001b[1m \u001b[0m\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
 Installs dependencies for FastKafka app testing                                                                   \n",
-       "                                                                                                                   \n",
-       "
\n" - ], - "text/plain": [ - " Installs dependencies for FastKafka app testing \n", - " \n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
-       " --help          Show this message and exit.                                                                     \n",
-       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2m╭─\u001b[0m\u001b[2m Options \u001b[0m\u001b[2m──────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b[2m─╮\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-help\u001b[0m Show this message and exit. \u001b[2m│\u001b[0m\n", - "\u001b[2m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "result = runner.invoke(_app, [\"testing\", \"install_deps\", \"--help\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a5a0b955", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "3f1cd496ac314d81aa61242a62545e93", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - " 0%| | 0/833975 [00:00 None:\n", + " \"\"\"Generate a new FastKafka app(s) effortlessly with advanced AI assistance\"\"\"\n", + " try:\n", + " _ensure_openai_api_key_set()\n", + " validated_description, description_token = validate_app_description(description)\n", + "# validated_plan, plan_token = generate_plan(validated_description)\n", + "# code = generate_app(validated_plan, validated_description)\n", + "# test = generate_test(code)\n", + " \n", + "# total_token_usage = description_token + plan_token\n", + "# typer.secho(f\" ▶ Total tokens usage: {total_token_usage}\", fg=typer.colors.CYAN)\n", + " typer.secho(\"✨ All files were successfully generated.!\", fg=typer.colors.CYAN)\n", + " \n", + " except (ValueError, KeyError) as e:\n", + " typer.secho(e, err=True, fg=typer.colors.RED)\n", + " raise typer.Exit(code=1)\n", + " except Exception as e:\n", + " typer.secho(f\"Unexpected internal error: {e}\", err=True, fg=typer.colors.RED)\n", + " raise typer.Exit(code=1)" ] }, { "cell_type": "code", "execution_count": null, - "id": "8774f94c", + "id": "81371542", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
                                                                                                                   \n",
-       " Usage: root code_generator [OPTIONS] COMMAND [ARGS]...                                                            \n",
-       "                                                                                                                   \n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\u001b[1;33mUsage: \u001b[0m\u001b[1mroot code_generator [OPTIONS] COMMAND [ARGS]...\u001b[0m\u001b[1m \u001b[0m\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
 Commands for accelerating FastKafka app creation using advanced AI technology.                                    \n",
-       " These commands uses OpenAI's GPT-3.5 API for generating FastKafka code. To access this feature, kindly sign up if \n",
-       " you haven't already and create an API key with OpenAI. If you're unsure about creating a new OpenAI API key,      \n",
-       " check this link for guidance: https://help.openai.com/en/articles/4936850-where-do-i-find-my-secret-api-key.      \n",
-       " Once you have the key, please set it in the OPENAI_API_KEY environment variable before executing the code         \n",
-       " generation commands.                                                                                              \n",
-       " Note: Accessing OpenAI API incurs charges. However, when you sign up for the first time, you usually get free     \n",
-       " credits that are more than enough to generate multiple FastKafka applications. For further information on pricing \n",
-       " and free credicts, check this link: https://openai.com/pricing                                                    \n",
-       "                                                                                                                   \n",
-       "
\n" - ], - "text/plain": [ - " Commands for accelerating FastKafka app creation using advanced AI technology. \n", - " \u001b[2mThese commands uses OpenAI's GPT-3.5 API for generating FastKafka code. To access this feature, kindly sign up if\u001b[0m \n", - " \u001b[2myou haven't already and create an API key with OpenAI. If you're unsure about creating a new OpenAI API key, \u001b[0m \n", - " \u001b[2mcheck this link for guidance: https://help.openai.com/en/articles/4936850-where-do-i-find-my-secret-api-key.\u001b[0m \n", - " \u001b[2mOnce you have the key, please set it in the OPENAI_API_KEY environment variable before executing the code \u001b[0m \n", - " \u001b[2mgeneration commands.\u001b[0m \n", - " \u001b[2mNote: Accessing OpenAI API incurs charges. However, when you sign up for the first time, you usually get free \u001b[0m \n", - " \u001b[2mcredits that are more than enough to generate multiple FastKafka applications. For further information on pricing\u001b[0m \n", - " \u001b[2mand free credicts, check this link: https://openai.com/pricing\u001b[0m \n", - " \n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
-       " --help          Show this message and exit.                                                                     \n",
-       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2m╭─\u001b[0m\u001b[2m Options \u001b[0m\u001b[2m──────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b[2m─╮\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-help\u001b[0m Show this message and exit. \u001b[2m│\u001b[0m\n", - "\u001b[2m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
╭─ Commands ──────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
-       " generate      Generate a new FastKafka app(s) effortlessly with advanced AI assistance                          \n",
-       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2m╭─\u001b[0m\u001b[2m Commands \u001b[0m\u001b[2m─────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b[2m─╮\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36mgenerate \u001b[0m\u001b[1;36m \u001b[0m Generate a new FastKafka app(s) effortlessly with advanced AI assistance \u001b[2m│\u001b[0m\n", - "\u001b[2m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "result = runner.invoke(_app, [\"code_generator\", \"--help\"])" + "# | notest\n", + "\n", + "! nbdev_export" ] }, { "cell_type": "code", "execution_count": null, - "id": "3f4dd041", + "id": "6f578155", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
                                                                                                                   \n",
-       " Usage: root code_generator generate [OPTIONS] DESCRIPTION                                                         \n",
+       " Usage: generate [OPTIONS] DESCRIPTION                                                                             \n",
        "                                                                                                                   \n",
        "
\n" ], "text/plain": [ "\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\u001b[1;33mUsage: \u001b[0m\u001b[1mroot code_generator generate [OPTIONS] DESCRIPTION\u001b[0m\u001b[1m \u001b[0m\u001b[1m \u001b[0m\n", + "\u001b[1m \u001b[0m\u001b[1;33mUsage: \u001b[0m\u001b[1mgenerate [OPTIONS] DESCRIPTION\u001b[0m\u001b[1m \u001b[0m\u001b[1m \u001b[0m\n", "\u001b[1m \u001b[0m\n" ] }, @@ -920,13 +354,17 @@ "data": { "text/html": [ "
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
-       " --help          Show this message and exit.                                                                     \n",
+       " --install-completion          Install completion for the current shell.                                         \n",
+       " --show-completion             Show completion for the current shell, to copy it or customize the installation.  \n",
+       " --help                        Show this message and exit.                                                       \n",
        "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
        "
\n" ], "text/plain": [ "\u001b[2m╭─\u001b[0m\u001b[2m Options \u001b[0m\u001b[2m──────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b[2m─╮\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-help\u001b[0m Show this message and exit. \u001b[2m│\u001b[0m\n", + "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-install\u001b[0m\u001b[1;36m-completion\u001b[0m Install completion for the current shell. \u001b[2m│\u001b[0m\n", + "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-show\u001b[0m\u001b[1;36m-completion\u001b[0m Show completion for the current shell, to copy it or customize the installation. \u001b[2m│\u001b[0m\n", + "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-help\u001b[0m Show this message and exit. \u001b[2m│\u001b[0m\n", "\u001b[2m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" ] }, @@ -935,24 +373,13 @@ } ], "source": [ - "result = runner.invoke(_app, [\"code_generator\", \"generate\", \"--help\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "97c907df", - "metadata": {}, - "outputs": [], - "source": [ - "# result = runner.invoke(_app, [\"code_generator\", \"generate\", \"Sample FastKafka application description\"])\n", - "# assert result.exit_code == 0" + "result = runner.invoke(app, [\"generate\", \"--help\"])" ] }, { "cell_type": "code", "execution_count": null, - "id": "61012b64", + "id": "08415d24", "metadata": {}, "outputs": [], "source": [] diff --git a/nbs/Code_Generator.ipynb b/nbs/Code_Generator.ipynb deleted file mode 100644 index 3b0df83..0000000 --- a/nbs/Code_Generator.ipynb +++ /dev/null @@ -1,409 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "e6067a2f", - "metadata": {}, - "outputs": [], - "source": [ - "# | default_exp _cli_code_generator" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ff086fc9", - "metadata": {}, - "outputs": [], - "source": [ - "# | export\n", - "\n", - "from typing import *\n", - "import os\n", - "\n", - "import typer\n", - "\n", - "from fastkafka._components.logger import get_logger\n", - "from fastkafka._code_generator.app_description_validator import validate_app_description\n", - "from fastkafka._code_generator.plan_generator import generate_plan\n", - "from fastkafka._code_generator.app_generator import generate_app\n", - "from fastkafka._code_generator.test_generator import generate_test\n", - "from fastkafka._code_generator.helper import set_logger_level" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7308ae66", - "metadata": {}, - "outputs": [], - "source": [ - "from typer.testing import CliRunner\n", - "import pytest\n", - "from unittest.mock import patch\n", - "\n", - "from fastkafka._components.logger import suppress_timestamps" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1d072658", - "metadata": {}, - "outputs": [], - "source": [ - "# | export\n", - "\n", - "logger = get_logger(__name__)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7da7962a", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[INFO] __main__: ok\n" - ] - } - ], - "source": [ - "suppress_timestamps()\n", - "logger = get_logger(__name__, level=20)\n", - "logger.info(\"ok\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8f1b231d", - "metadata": {}, - "outputs": [], - "source": [ - "runner = CliRunner()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5742512f", - "metadata": {}, - "outputs": [], - "source": [ - "# | export\n", - "\n", - "OPENAI_KEY_EMPTY_ERROR = \"Error: OPENAI_API_KEY cannot be empty. Please set a valid OpenAI API key in OPENAI_API_KEY environment variable and try again.\\nYou can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details.\"\n", - "OPENAI_KEY_NOT_SET_ERROR = \"Error: OPENAI_API_KEY not found in environment variables. Set a valid OpenAI API key in OPENAI_API_KEY environment variable and try again. You can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details.\"\n", - "\n", - "\n", - "def _ensure_openai_api_key_set() -> None:\n", - " \"\"\"Ensure the 'OPENAI_API_KEY' environment variable is set and is not empty.\n", - "\n", - " Raises:\n", - " KeyError: If the 'OPENAI_API_KEY' environment variable is not found.\n", - " ValueError: If the 'OPENAI_API_KEY' environment variable is found but its value is empty.\n", - " \"\"\"\n", - " try:\n", - " openai_api_key = os.environ[\"OPENAI_API_KEY\"]\n", - " if openai_api_key == \"\":\n", - " raise ValueError(OPENAI_KEY_EMPTY_ERROR)\n", - " except KeyError:\n", - " raise KeyError(OPENAI_KEY_NOT_SET_ERROR)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "24c7f256", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Error: OPENAI_API_KEY cannot be empty. Please set a valid OpenAI API key in OPENAI_API_KEY environment variable and try again.\n", - "You can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details.\n" - ] - } - ], - "source": [ - "with patch.dict(os.environ, {\"OPENAI_API_KEY\": \"\"}):\n", - " with pytest.raises(ValueError) as e:\n", - " _ensure_openai_api_key_set()\n", - "\n", - "print(e.value)\n", - "assert str(e.value) == OPENAI_KEY_EMPTY_ERROR" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aa9a9439", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "'Error: OPENAI_API_KEY not found in environment variables. Set a valid OpenAI API key in OPENAI_API_KEY environment variable and try again. You can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details.'\n" - ] - } - ], - "source": [ - "with patch.dict(os.environ, {}, clear=True):\n", - " with pytest.raises(KeyError) as e:\n", - " _ensure_openai_api_key_set()\n", - " \n", - "print(e.value)\n", - "assert str(e.value) == f\"'{OPENAI_KEY_NOT_SET_ERROR}'\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f311f7e4", - "metadata": {}, - "outputs": [], - "source": [ - "with patch.dict(os.environ, {\"OPENAI_API_KEY\": \"INVALID_KEY\"}):\n", - " _ensure_openai_api_key_set()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9b11fc9c", - "metadata": {}, - "outputs": [], - "source": [ - "# | export\n", - "\n", - "_code_generator_app = typer.Typer(\n", - " short_help=\"Commands for accelerating FastKafka app creation using advanced AI technology\",\n", - " help=\"\"\"Commands for accelerating FastKafka app creation using advanced AI technology.\n", - "\n", - "These commands use a combination of OpenAI's gpt-3.5-turbo and gpt-3.5-turbo-16k models to generate FastKafka code. To access this feature, kindly sign up if you haven't already and create an API key with OpenAI. You can generate API keys in the OpenAI web interface. See https://platform.openai.com/account/api-keys for details.\n", - "\n", - "Once you have the key, please set it in the OPENAI_API_KEY environment variable before executing the code generation commands.\n", - "\n", - "Note: Accessing OpenAI API incurs charges. However, when you sign up for the first time, you usually get free credits that are more than enough to generate multiple FastKafka applications. For further information on pricing and free credicts, check this link: https://openai.com/pricing\n", - " \"\"\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bb880142", - "metadata": {}, - "outputs": [], - "source": [ - "# | export\n", - "\n", - "\n", - "@_code_generator_app.command(\n", - " \"generate\",\n", - " help=\"Generate a new FastKafka app(s) effortlessly with advanced AI assistance\",\n", - ")\n", - "@set_logger_level\n", - "def generate_fastkafka_app(\n", - " description: str = typer.Argument(\n", - " ...,\n", - " help=\"\"\"Summarize your FastKafka app in a few sentences!\n", - "\n", - "\n", - "\\nInclude details about message classes, FastKafka app configuration (e.g., kafka_brokers), consumer and producer functions, and specify the business logic to be implemented. \n", - "\n", - "\n", - "\\nThe simpler and more specific the app description is, the better the generated app will be. Please refer to the below example for inspiration:\n", - "\n", - "\n", - "\\nCreate a FastKafka application that consumes messages from the \"store_product\" topic. These messages should have three attributes: \"product_name,\" \"currency,\" and \"price\". While consuming, the app needs to produce a message to the \"change_currency\" topic. The function responsible for producing should take a \"store_product\" object as input and return the same object. Additionally, this function should check if the currency in the input \"store_product\" is \"HRK.\" If it is, then the currency should be changed to \"EUR,\" and the price should be divided by 7.5. Remember, the app should use a \"localhost\" broker.\n", - "\n", - "\n", - "\\n\"\"\"\n", - " ),\n", - " debug: bool = typer.Option(\n", - " False,\n", - " \"--debug\",\n", - " \"-d\",\n", - " help=\"Enable verbose logging by setting the logger level to DEBUG.\",\n", - " ),\n", - ") -> None:\n", - " \"\"\"Generate a new FastKafka app(s) effortlessly with advanced AI assistance\"\"\"\n", - " try:\n", - " _ensure_openai_api_key_set()\n", - " validated_description, description_token = validate_app_description(description)\n", - "# validated_plan, plan_token = generate_plan(validated_description)\n", - "# code = generate_app(validated_plan, validated_description)\n", - "# test = generate_test(code)\n", - " \n", - "# total_token_usage = description_token + plan_token\n", - "# typer.secho(f\" ▶ Total tokens usage: {total_token_usage}\", fg=typer.colors.CYAN)\n", - " typer.secho(\"✨ All files were successfully generated.!\", fg=typer.colors.CYAN)\n", - " \n", - " except (ValueError, KeyError) as e:\n", - " typer.secho(e, err=True, fg=typer.colors.RED)\n", - " raise typer.Exit(code=1)\n", - " except Exception as e:\n", - " typer.secho(f\"Unexpected internal error: {e}\", err=True, fg=typer.colors.RED)\n", - " raise typer.Exit(code=1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "81371542", - "metadata": {}, - "outputs": [], - "source": [ - "# | notest\n", - "\n", - "! nbdev_export" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6f578155", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
                                                                                                                   \n",
-       " Usage: generate [OPTIONS] DESCRIPTION                                                                             \n",
-       "                                                                                                                   \n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\u001b[1;33mUsage: \u001b[0m\u001b[1mgenerate [OPTIONS] DESCRIPTION\u001b[0m\u001b[1m \u001b[0m\u001b[1m \u001b[0m\n", - "\u001b[1m \u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
 Generate a new FastKafka app(s) effortlessly with advanced AI assistance                                          \n",
-       "                                                                                                                   \n",
-       "
\n" - ], - "text/plain": [ - " Generate a new FastKafka app(s) effortlessly with advanced AI assistance \n", - " \n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
-       " *    description      TEXT  Summarize your FastKafka app in a few sentences!                                    \n",
-       "                                                                                                                 \n",
-       "                             Include details about message classes, FastKafka app configuration (e.g.,           \n",
-       "                             kafka_brokers), consumer and producer functions, and specify the business logic to  \n",
-       "                             be implemented.                                                                     \n",
-       "                                                                                                                 \n",
-       "                             The simpler and more specific the app description is, the better the generated app  \n",
-       "                             will be. Please refer to the below example for inspiration:                         \n",
-       "                                                                                                                 \n",
-       "                             Create a FastKafka application that consumes messages from the \"store_product\"      \n",
-       "                             topic. These messages should have three attributes: \"product_name,\" \"currency,\" and \n",
-       "                             \"price\". While consuming, the app needs to produce a message to the                 \n",
-       "                             \"change_currency\" topic. The function responsible for producing should take a       \n",
-       "                             \"store_product\" object as input and return the same object. Additionally, this      \n",
-       "                             function should check if the currency in the input \"store_product\" is \"HRK.\" If it  \n",
-       "                             is, then the currency should be changed to \"EUR,\" and the price should be divided   \n",
-       "                             by 7.5. Remember, the app should use a \"localhost\" broker.                          \n",
-       "                             [default: None]                                                                     \n",
-       "                             [required]                                                                          \n",
-       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2m╭─\u001b[0m\u001b[2m Arguments \u001b[0m\u001b[2m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b[2m─╮\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[31m*\u001b[0m description \u001b[1;33mTEXT\u001b[0m Summarize your FastKafka app in a few sentences! \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m Include details about message classes, FastKafka app configuration (e.g., \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m kafka_brokers), consumer and producer functions, and specify the business logic to \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m be implemented. \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m The simpler and more specific the app description is, the better the generated app \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m will be. Please refer to the below example for inspiration: \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m Create a FastKafka application that consumes messages from the \"store_product\" \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m topic. These messages should have three attributes: \"product_name,\" \"currency,\" and \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \"price\". While consuming, the app needs to produce a message to the \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \"change_currency\" topic. The function responsible for producing should take a \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \"store_product\" object as input and return the same object. Additionally, this \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m function should check if the currency in the input \"store_product\" is \"HRK.\" If it \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m is, then the currency should be changed to \"EUR,\" and the price should be divided \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m by 7.5. Remember, the app should use a \"localhost\" broker. \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[2m[default: None] \u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[2;31m[required] \u001b[0m \u001b[2m│\u001b[0m\n", - "\u001b[2m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
-       " --debug               -d        Enable verbose logging by setting the logger level to DEBUG.                    \n",
-       " --install-completion            Install completion for the current shell.                                       \n",
-       " --show-completion               Show completion for the current shell, to copy it or customize the              \n",
-       "                                 installation.                                                                   \n",
-       " --help                          Show this message and exit.                                                     \n",
-       "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[2m╭─\u001b[0m\u001b[2m Options \u001b[0m\u001b[2m──────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\u001b[2m─╮\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-debug\u001b[0m \u001b[1;32m-d\u001b[0m Enable verbose logging by setting the logger level to DEBUG. \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-install\u001b[0m\u001b[1;36m-completion\u001b[0m Install completion for the current shell. \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-show\u001b[0m\u001b[1;36m-completion\u001b[0m Show completion for the current shell, to copy it or customize the \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m installation. \u001b[2m│\u001b[0m\n", - "\u001b[2m│\u001b[0m \u001b[1;36m-\u001b[0m\u001b[1;36m-help\u001b[0m Show this message and exit. \u001b[2m│\u001b[0m\n", - "\u001b[2m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "result = runner.invoke(_code_generator_app, [\"generate\", \"--help\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "08415d24", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "python3", - "language": "python", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/nbs/Code_Generator_Helper.ipynb b/nbs/Helper.ipynb similarity index 87% rename from nbs/Code_Generator_Helper.ipynb rename to nbs/Helper.ipynb index d2e6588..da7e2fe 100644 --- a/nbs/Code_Generator_Helper.ipynb +++ b/nbs/Helper.ipynb @@ -29,8 +29,8 @@ "import openai\n", "from fastcore.foundation import patch\n", "\n", - "from fastkafka._components.logger import get_logger, set_level\n", - "from fastkafka._code_generator.prompts import SYSTEM_PROMPT, DEFAULT_FASTKAFKA_PROMPT" + "from fastkafka_gen._components.logger import get_logger, set_level\n", + "from fastkafka_gen._code_generator.prompts import SYSTEM_PROMPT, DEFAULT_FASTKAFKA_PROMPT" ] }, { @@ -43,7 +43,7 @@ "import pytest\n", "import unittest.mock\n", "\n", - "from fastkafka._components.logger import suppress_timestamps" + "from fastkafka_gen._components.logger import suppress_timestamps" ] }, { @@ -78,98 +78,6 @@ "logger.info(\"ok\")" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "b604e0ed", - "metadata": {}, - "outputs": [], - "source": [ - "# | export\n", - "\n", - "def set_logger_level(func):\n", - " @functools.wraps(func)\n", - " def wrapper_decorator(*args, **kwargs):\n", - " if (\"debug\" in kwargs) and kwargs[\"debug\"]:\n", - " set_level(logging.DEBUG)\n", - " else:\n", - " set_level(logging.WARNING)\n", - " return func(*args, **kwargs)\n", - " return wrapper_decorator" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "80d3b2cb", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[WARNING] __main__: WARNING\n" - ] - }, - { - "data": { - "text/plain": [ - "30" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "@set_logger_level\n", - "def _test_logger():\n", - " logger.debug(\"INFO\")\n", - " logger.warning(\"WARNING\")\n", - "\n", - " \n", - "_test_logger()\n", - "display(logger.getEffectiveLevel())\n", - "assert logger.getEffectiveLevel() == logging.WARNING" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "82d7afe5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[INFO] __main__: INFO\n", - "[WARNING] __main__: WARNING\n" - ] - }, - { - "data": { - "text/plain": [ - "10" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "@set_logger_level\n", - "def _test_logger(**kwargs):\n", - " logger.debug(\"DEBUG\")\n", - " logger.info(\"INFO\")\n", - " logger.warning(\"WARNING\")\n", - "\n", - " \n", - "_test_logger(debug=True)\n", - "display(logger.getEffectiveLevel())\n", - "assert logger.getEffectiveLevel() == logging.DEBUG" - ] - }, { "cell_type": "code", "execution_count": null, @@ -255,7 +163,15 @@ "execution_count": null, "id": "234e0a84", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success\n" + ] + } + ], "source": [ "@_retry_with_exponential_backoff()\n", "def mock_func():\n", @@ -273,7 +189,16 @@ "execution_count": null, "id": "2f323384", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[INFO] __main__: Note: OpenAI's API rate limit reached. Command will automatically retry in 2 seconds. For more information visit: https://help.openai.com/en/articles/5955598-is-api-usage-subject-to-any-rate-limits\n", + "Maximum number of retries (1) exceeded.\n" + ] + } + ], "source": [ "# Test max retries exceeded\n", "@_retry_with_exponential_backoff(max_retries=1)\n", @@ -346,19 +271,12 @@ " self.messages.append(\n", " {\"role\": \"user\", \"content\": f\"==== APP DESCRIPTION: ====\\n\\n{user_prompt}\"}\n", " )\n", - " logger.info(\"logger.info\")\n", - " logger.warning(\"logger.warning\")\n", - " logger.debug(\"Calling OpenAI with the below prompt message:\")\n", - " logger.debug(f\"\\n\\n{m}\" for m in self.messages)\n", - " \n", " response = openai.ChatCompletion.create(\n", " model=self.model,\n", " messages=self.messages,\n", " temperature=self.params[\"temperature\"],\n", " )\n", " \n", - " logger.debug(\"Response from OpenAI:\")\n", - " logger.debug(response[\"choices\"][0][\"message\"][\"content\"])\n", " return (\n", " response[\"choices\"][0][\"message\"][\"content\"],\n", " response[\"usage\"][\"total_tokens\"],\n", @@ -370,7 +288,16 @@ "execution_count": null, "id": "80f5a04a", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "2136\n" + ] + } + ], "source": [ "TEST_INITIAL_USER_PROMPT = \"\"\"\n", "You should respond with 0, 1 or 2 and nothing else. Below are your rules:\n", @@ -418,7 +345,15 @@ "execution_count": null, "id": "2e27da2a", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This is a mock response\n" + ] + } + ], "source": [ "test_response = \"This is a mock response\"\n", "\n", diff --git a/nbs/Logger.ipynb b/nbs/Logger.ipynb index a687fa0..1f81d7c 100644 --- a/nbs/Logger.ipynb +++ b/nbs/Logger.ipynb @@ -37,19 +37,7 @@ "execution_count": null, "id": "ab182545", "metadata": {}, - "outputs": [ - { - "ename": "ModuleNotFoundError", - "evalue": "No module named 'pytest'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[3], line 6\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mtime\u001b[39;00m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01munittest\u001b[39;00m\n\u001b[0;32m----> 6\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mpytest\u001b[39;00m\n", - "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'pytest'" - ] - } - ], + "outputs": [], "source": [ "# | include: false\n", "\n", @@ -143,7 +131,26 @@ "execution_count": null, "id": "6e725745", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "{'version': 1,\n", + " 'disable_existing_loggers': False,\n", + " 'formatters': {'standard': {'format': '%(asctime)s.%(msecs)03d [%(levelname)s] %(name)s: %(message)s',\n", + " 'datefmt': '%y-%m-%d %H:%M:%S'}},\n", + " 'handlers': {'default': {'level': 20,\n", + " 'formatter': 'standard',\n", + " 'class': 'logging.StreamHandler',\n", + " 'stream': 'ext://sys.stdout'}},\n", + " 'loggers': {'': {'handlers': ['default'], 'level': 20}}}" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# collapse_output\n", "\n", @@ -155,7 +162,26 @@ "execution_count": null, "id": "718c810d", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "{'version': 1,\n", + " 'disable_existing_loggers': False,\n", + " 'formatters': {'standard': {'format': '%(asctime)s.%(msecs)03d [%(levelname)s] %(name)s: %(message)s',\n", + " 'datefmt': '%y-%m-%d %H:%M:%S'}},\n", + " 'handlers': {'default': {'level': 20,\n", + " 'formatter': 'standard',\n", + " 'class': 'logging.StreamHandler',\n", + " 'stream': 'ext://sys.stdout'}},\n", + " 'loggers': {'': {'handlers': ['default'], 'level': 20}}}" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# | include: false\n", "\n", @@ -196,7 +222,7 @@ "\n", "\n", "def get_logger(\n", - " name: str, *, level: int = logging.DEBUG, add_spaces: bool = True\n", + " name: str, *, level: int = logging.INFO, add_spaces: bool = True\n", ") -> logging.Logger:\n", " \"\"\"Return the logger class with default logging configuration.\n", "\n", @@ -238,7 +264,17 @@ "execution_count": null, "id": "3c6a2ae0", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "23-08-08 10:50:24.342 [INFO] __main__: hello\n", + "23-08-08 10:50:24.343 [INFO] __main__: hello\n", + "23-08-08 10:50:24.343 [INFO] __main__: hello\n" + ] + } + ], "source": [ "logger = get_logger(__name__)\n", "logger.info(\"hello\")\n", @@ -266,7 +302,18 @@ "execution_count": null, "id": "90767ccb", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "23-08-08 10:50:24.348 [INFO] __main__: info\n", + "23-08-08 10:50:24.349 [WARNING] __main__: Warning\n", + "23-08-08 10:50:24.349 [ERROR] __main__: Error\n", + "23-08-08 10:50:24.350 [CRITICAL] __main__: Critical\n" + ] + } + ], "source": [ "# collapse_output\n", "\n", @@ -284,7 +331,18 @@ "execution_count": null, "id": "ede2ce1f", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[INFO] __main__: info\n", + "[WARNING] __main__: Warning\n", + "[ERROR] __main__: Error\n", + "[CRITICAL] __main__: Critical\n" + ] + } + ], "source": [ "# collapse_output\n", "\n", @@ -331,7 +389,16 @@ "execution_count": null, "id": "5db8d01f", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "40\n", + "[ERROR] __main__: This is an error\n" + ] + } + ], "source": [ "level = logging.ERROR\n", "\n", @@ -352,7 +419,15 @@ "execution_count": null, "id": "b9c2d1b0", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[INFO] __main__: something\n" + ] + } + ], "source": [ "# Reset log level back to info\n", "level = logging.INFO\n", @@ -366,10 +441,29 @@ "execution_count": null, "id": "ce1b40e5", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "int" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "type(logging.INFO)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e99090d9", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/nbs/Plan_Generator.ipynb b/nbs/Plan_Generator.ipynb index 8a2ccab..dce29d4 100644 --- a/nbs/Plan_Generator.ipynb +++ b/nbs/Plan_Generator.ipynb @@ -25,9 +25,9 @@ "\n", "from yaspin import yaspin\n", "\n", - "from fastkafka._components.logger import get_logger\n", - "from fastkafka._code_generator.helper import CustomAIChat, ValidateAndFixResponse\n", - "from fastkafka._code_generator.prompts import PLAN_GENERATION_PROMPT" + "from fastkafka_gen._components.logger import get_logger\n", + "from fastkafka_gen._code_generator.helper import CustomAIChat, ValidateAndFixResponse\n", + "from fastkafka_gen._code_generator.prompts import PLAN_GENERATION_PROMPT" ] }, { @@ -37,7 +37,7 @@ "metadata": {}, "outputs": [], "source": [ - "from fastkafka._components.logger import suppress_timestamps" + "from fastkafka_gen._components.logger import suppress_timestamps" ] }, { @@ -1403,6 +1403,8 @@ "name": "stdout", "output_type": "stream", "text": [ + "[INFO] fastkafka_gen._code_generator.helper: logger.info\n", + "⠋ Generating plan[WARNING] fastkafka_gen._code_generator.helper: logger.warning\n", "⠹ Generating plan " ] }, @@ -1419,8 +1421,8 @@ "output_type": "stream", "text": [ " ✔ Plan generated \n", - "{'entities': [{'name': 'StoreProduct', 'arguments': {'product_name': 'str', 'currency': 'str', 'price': 'float'}}], 'apps': [{'app_name': 'store_app', 'kafka_brokers': {'localhost': {'url': 'localhost', 'description': 'local development kafka broker', 'port': 9092}}, 'title': 'Store Kafka App', 'consumes_functions': {'on_store_product': {'topic': 'store_product', 'prefix': 'on', 'parameters': {'msg': 'StoreProduct'}, 'description': \"This function will listen to the 'store_product' topic, it will consume the messages posted on the 'store_product' topic. The message should be of type 'StoreProduct' which contains product details such as 'product_name', 'currency', and 'price'. After consuming the data, it will produce a message to the 'change_currency' topic.\"}}, 'produces_functions': {'to_change_currency': {'topic': 'change_currency', 'prefix': 'to', 'parameters': {'store_product': 'StoreProduct'}, 'description': \"This function will be triggered when a message is received from the 'store_product' topic. It will take the 'store_product' message as input and produce a message to the 'change_currency' topic. If the currency in the input store_product is 'HRK', the currency will be set to 'EUR' and the price will be divided by 7.5. The function will return the updated store_product message.\", 'returns': 'StoreProduct'}}}]}\n", - "4260\n" + "{'entities': [{'name': 'StoreProduct', 'arguments': {'product_name': 'str', 'currency': 'str', 'price': 'float'}}], 'apps': [{'app_name': 'store_app', 'kafka_brokers': {'localhost': {'url': 'localhost', 'description': 'local kafka broker', 'port': 9092}}, 'title': 'Store Kafka App', 'consumes_functions': {'on_store_product': {'topic': 'store_product', 'prefix': 'on', 'parameters': {'msg': 'StoreProduct'}, 'description': \"This function will listen to the 'store_product' topic and consume messages with attributes 'product_name', 'currency', and 'price'. The message should be of type 'StoreProduct'. After consuming the message, it will produce a message to the 'change_currency' topic.\"}}, 'produces_functions': {'to_change_currency': {'topic': 'change_currency', 'prefix': 'to', 'parameters': {'store_product': 'StoreProduct'}, 'description': \"This function will be triggered when a message is consumed from the 'store_product' topic. It takes a 'StoreProduct' object as input and produces a message to the 'change_currency' topic. If the currency in the input 'StoreProduct' object is 'HRK', the currency will be changed to 'EUR' and the price will be divided by 7.5. The function returns the modified 'StoreProduct' object.\", 'returns': 'StoreProduct'}}}]}\n", + "4246\n" ] } ], diff --git a/nbs/Code_Generation_Prompts.ipynb b/nbs/Prompts.ipynb similarity index 100% rename from nbs/Code_Generation_Prompts.ipynb rename to nbs/Prompts.ipynb diff --git a/nbs/Test_Generator.ipynb b/nbs/Test_Generator.ipynb index 34979b6..0a938ce 100644 --- a/nbs/Test_Generator.ipynb +++ b/nbs/Test_Generator.ipynb @@ -23,7 +23,7 @@ "import time\n", "\n", "from yaspin import yaspin\n", - "from fastkafka._components.logger import get_logger" + "from fastkafka_gen._components.logger import get_logger" ] }, { @@ -33,7 +33,7 @@ "metadata": {}, "outputs": [], "source": [ - "from fastkafka._components.logger import suppress_timestamps" + "from fastkafka_gen._components.logger import suppress_timestamps" ] }, { @@ -135,7 +135,7 @@ " # TODO: Implement the actual functionality\n", " with yaspin(text=\"Generating tests...\", color=\"cyan\", spinner=\"clock\") as sp:\n", "\n", - " time.sleep(3)\n", + " time.sleep(1)\n", " sp.text = \"\"\n", " sp.ok(\" ✔ Tests are generated and saved at: /some_dir/test.py\")\n", " return SAMPLE_CODE" @@ -166,7 +166,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "✔ Tests generated! \n", + " ✔ Tests are generated and saved at: /some_dir/test.py \n", "\n", "import asyncio\n", "from fastkafka.testing import Tester\n", diff --git a/settings.ini b/settings.ini index 34a7317..9bf3cd5 100644 --- a/settings.ini +++ b/settings.ini @@ -53,5 +53,6 @@ dev_requirements = \ mypy==1.4.1 \ isort==5.12.0 \ pre-commit==3.3.3 \ - detect-secrets==1.4.0 \ -# console_scripts = \ No newline at end of file + detect-secrets==1.4.0 + +console_scripts = fastkafka_gen=fastkafka_gen.cli:app \ No newline at end of file