From f021eafb01cb3dfc4b617b496b74caf7a95479b1 Mon Sep 17 00:00:00 2001 From: Man Parvesh Singh Randhawa Date: Sun, 13 Oct 2024 00:34:26 -0700 Subject: [PATCH] add status spinner, update docs --- README.md | 126 +++++++++++++++++++-------------------- docs/docs/index.md | 60 ++++++++++++------- src/yodapa/plugins/ai.py | 69 +++++++++++---------- 3 files changed, 135 insertions(+), 120 deletions(-) diff --git a/README.md b/README.md index 8b10f53..454b89c 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,16 @@ The help command will list all the available plugins. $ yoda --help ``` +![img.png](docs/docs/img.png) + +You can find the details for each plugin with the `--help` flag. Some examples: + +![img_1.png](docs/docs/img_1.png) + +![img_2.png](docs/docs/img_2.png) + +![img_3.png](docs/docs/img_3.png) + ### Write your own plugin for Yoda Simply create a class with the `@yoda_plugin(name="plugin-name")` decorator and add methods to it. The non-private @@ -41,94 +51,78 @@ decorator. ```python import typer -from yodapa.plugin_manager.decorator import yoda_plugin - -@yoda_plugin(name="hi") -class HiPlugin: - """ +app = typer.Typer(help=""" Hi plugin. Say hello. Example: + $ yoda hi hello --name MP + $ yoda hi hello - """ + """) - def hello(self, name: str = None): - """Say hello.""" - name = name or "Padawan" - typer.echo(f"Hello {name}!") - def _private_method_should_not_be_added(self): - """This method should not be added as a command.""" - raise NotImplementedError() +@app.command() +def hello(name: str = None): + """Say hello.""" + name = name or "Padawan" + typer.echo(f"Hello {name}!") ``` ### Use AI to generate your own plugin ```bash -$ yoda ai generate-command todo "todo list that keeps track of your todos" +$ yoda ai generate-command weather "show weather for the provided location" 🤖 Generated code: -import typer - -from yodapa.plugin_manager.decorator import yoda_plugin - +import requests +from typing import Optional -@yoda_plugin(name="todo") -class TodoPlugin: - """ - Todo plugin. Keeps track of your todos. +app = typer.Typer(help=""" + Show weather for a given location. Example: - $ yoda todo list --all - $ yoda todo add "Finish assignment" - $ yoda todo done 1 - $ yoda todo delete 2 - """ - - def list(self, all: bool = False): - """List all todos.""" - if all: - typer.echo("All todos:") - for todo in self.todos: - typer.echo(f"- {todo}") - else: - typer.echo("Active todos:") - for todo in self.active_todos: - typer.echo(f"- {todo}") - - def add(self, name: str): - """Add a new todo.""" - if name == "": - raise ValueError("Todo name cannot be empty") - self.todos.append(name) - typer.echo(f"Added todo '{name}'") - - def done(self, id: int): - """Mark a todo as done.""" - if id < 0 or id >= len(self.todos): - raise ValueError("Todo ID out of range") - self.active_todos.remove(self.todos[id]) - typer.echo(f"Marked todo {id} as done") - - def delete(self, id: int): - """Delete a todo.""" - if id < 0 or id >= len(self.todos): - raise ValueError("Todo ID out of range") - self.todos.remove(self.todos[id]) - typer.echo(f"Deleted todo {id}") - - def __init__(self): - self.todos = [] - self.active_todos = [] - -if __name__ == "__main__": - typer.run(TodoPlugin()) + $ yoda weather London + + $ yoda weather -l London + """) + +@app.command() +def weather(location: str, units: Optional[str] = None): + """Show the current weather for a given location.""" + # Set up your API key or database connection here + api_key = "YOUR_API_KEY" + db_conn = None # Initialize your DB connection here + + # Use the requests library to make an HTTP request to the API + url = f"https://api.openweathermap.org/data/2.5/weather?q={location}&appid={api_key}" + response = requests.get(url) + + # If the response is successful, parse the JSON data and return it in a format that typer can display + if response.status_code == 200: + data = response.json() + temperature = data["main"]["temp"] + humidity = data["main"]["humidity"] + wind = data["wind"]["speed"] + pressure = data["main"]["pressure"] + + typer.echo(f"Weather for {location}:") + typer.echo(f"\tTemperature: {temperature}°C") + typer.echo(f"\tHumidity: {humidity}%") + typer.echo(f"\tWind speed: {wind} m/s") + typer.echo(f"\tPressure: {pressure} hPa") + + # If the response is not successful, print an error message + else: + typer.echo(f"Error: {response.status_code}") ``` +.. or chat with Yoda: +![img_5.png](docs/docs/img_5.png) + ## Development setup ```bash diff --git a/docs/docs/index.md b/docs/docs/index.md index aaf905b..8ba733b 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -73,33 +73,51 @@ def hello(name: str = None): ### Use AI to generate your own plugin ```bash -$ yoda ai generate-command todo "todo list that keeps track of your todos" +$ yoda ai generate-command weather "show weather for the provided location" 🤖 Generated code: -import typer -from weather import Weather +import requests +from typing import Optional -app = typer.Typer(help="Show weather for a given location") +app = typer.Typer(help=""" + Show weather for a given location. -@app.command() -def weather(location: str): - """Show weather for a given location.""" - try: - weather_data = Weather(location).get_weather() - print(f"Weather for {location}:") - print(f"Temperature: {weather_data['temperature']}") - print(f"Description: {weather_data['description']}") - except KeyError as error: - print("Invalid location") - -This code uses the `Weather` class from the `weather` library to retrieve weather data for a given location. The -`location` argument is passed as a command-line argument, and the `get_weather()` method of the `Weather` object returns -a dictionary containing the current temperature and description of the weather in the given location. - -The code uses a try-except block to catch any errors that may occur when retrieving the weather data, such as invalid -locations. In this case, it prints an error message to the console indicating that the location is invalid. + Example: + $ yoda weather London + + $ yoda weather -l London + """) + +@app.command() +def weather(location: str, units: Optional[str] = None): + """Show the current weather for a given location.""" + # Set up your API key or database connection here + api_key = "YOUR_API_KEY" + db_conn = None # Initialize your DB connection here + + # Use the requests library to make an HTTP request to the API + url = f"https://api.openweathermap.org/data/2.5/weather?q={location}&appid={api_key}" + response = requests.get(url) + + # If the response is successful, parse the JSON data and return it in a format that typer can display + if response.status_code == 200: + data = response.json() + temperature = data["main"]["temp"] + humidity = data["main"]["humidity"] + wind = data["wind"]["speed"] + pressure = data["main"]["pressure"] + + typer.echo(f"Weather for {location}:") + typer.echo(f"\tTemperature: {temperature}°C") + typer.echo(f"\tHumidity: {humidity}%") + typer.echo(f"\tWind speed: {wind} m/s") + typer.echo(f"\tPressure: {pressure} hPa") + + # If the response is not successful, print an error message + else: + typer.echo(f"Error: {response.status_code}") ``` .. or chat with Yoda: diff --git a/src/yodapa/plugins/ai.py b/src/yodapa/plugins/ai.py index df86f48..ca89371 100644 --- a/src/yodapa/plugins/ai.py +++ b/src/yodapa/plugins/ai.py @@ -1,5 +1,6 @@ import ollama import typer +from rich.console import Console app = typer.Typer(help="AI command. Allows you to communicate with your local LLMs") @@ -61,39 +62,41 @@ def generate_command(plugin_name: str, prompt: str): {prompt}. """ try: - response = ollama.chat( - model="codellama", - messages=[{"role": "system", "content": """You are an expert python programmer. You must use all the python best practices to write the most efficient python code. Provide complete working code for all the subcommands. Provide full working code. If the plugin requires storage, use local storage like files or sqlite, whichever is easier to use. -You need to generate a typer command line app. An example app can be found below: -```python -import typer - -app = typer.Typer(help=\"\"\" - Hi plugin. Say hello. - - Example: - - $ yoda hi hello --name MP - - $ yoda hi hello - \"\"\") - - -@app.command() -def hello(name: str = None): - \"\"\"Say hello.\"\"\" - name = name or "Padawan" - typer.echo(f"Hello {{name}}!") -``` - -You must only return the generated code for the plugin class. All the details for the plugin class should be added in the docstring. -When the user provides a description for their requirement, you must use all the best practices required to implement what they need. - """}, - {"role": "user", "content": prompt}], - stream=False, - ) - # typer.echo(f"Received response from Ollama: {response['message']['content'].strip()}") - generated_code = response['message']['content'].strip() + console = Console() + with console.status("[bold green]Waiting for AI response..."): + response = ollama.chat( + model="codellama", + messages=[{"role": "system", "content": """You are an expert python programmer. You must use all the python best practices to write the most efficient python code. Provide complete working code for all the subcommands. Provide full working code. If the plugin requires storage, use local storage like files or sqlite, whichever is easier to use. + You need to generate a typer command line app. An example app can be found below: + ```python + import typer + + app = typer.Typer(help=\"\"\" + Hi plugin. Say hello. + + Example: + + $ yoda hi hello --name MP + + $ yoda hi hello + \"\"\") + + + @app.command() + def hello(name: str = None): + \"\"\"Say hello.\"\"\" + name = name or "Padawan" + typer.echo(f"Hello {{name}}!") + ``` + + You must only return the generated code for the plugin class. All the details for the plugin class should be added in the docstring. + When the user provides a description for their requirement, you must use all the best practices required to implement what they need. + """}, + {"role": "user", "content": ai_prompt}], + stream=False, + ) + # typer.echo(f"Received response from Ollama: {response['message']['content'].strip()}") + generated_code = response['message']['content'].strip() typer.echo(f"🤖 Generated code:\n{generated_code}") except ollama.ResponseError as e: typer.echo(f"Error communicating with Ollama: {e}", err=True)