Skip to content

Commit

Permalink
add status spinner, update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Man Parvesh Singh Randhawa committed Oct 13, 2024
1 parent 8d7f444 commit f021eaf
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 120 deletions.
126 changes: 60 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
60 changes: 39 additions & 21 deletions docs/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
69 changes: 36 additions & 33 deletions src/yodapa/plugins/ai.py
Original file line number Diff line number Diff line change
@@ -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")

Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit f021eaf

Please sign in to comment.