Skip to content

Commit

Permalink
Merge branch 'dev' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
yalattas committed Apr 26, 2023
2 parents 0ac57f0 + 045f6c6 commit d61fc63
Show file tree
Hide file tree
Showing 12 changed files with 355 additions and 71 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ environment.yaml
# Python virtual environment
venv/
run/
~/

# Compiled Python files
*.pyc
Expand Down
14 changes: 14 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

# This is the stages / task to perform in jobs
stages:
- test
- build
- deploy
- release

# GitLab test ---------------------------------------------------
sast:
stage: test
include:
- template: Security/SAST.gitlab-ci.yml
# GitLab test ---------------------------------------------------
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [v0.0.2](#v0.0.1) - 2023-04-26 - Released - Alpha

### Added
- [ai command](#6)
- Allowing users to list available AI platforms based on pre-defined categories
### Changed
- [configure command](#7)
- checking `--model` value based on selected `--platform` value
- calling `openai` text completion endpoint to retrieve text -> UNCOOKED
- handling authentication and RateLimit exceptions

## [v0.0.1](#v0.0.1) - 2023-04-24 - Released - Alpha

Expand Down
18 changes: 11 additions & 7 deletions mindmate/cli.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import click
from .utils import helper
from .commands.configure import configure
from .commands.initdb import initdb
from .commands.dropdb import dropdb
from .commands.nested_group import nested_group
from mindmate.utils import helper
from mindmate.commands.configure import configure
from mindmate.commands.chat import chat
from mindmate.commands.ai import ai
from mindmate.commands.initdb import initdb
from mindmate.commands.dropdb import dropdb

@click.group()
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])

@click.group(context_settings=CONTEXT_SETTINGS)
def cli():
"""hi to mindmate cli, try option --help for more information"""
pass

cli.add_command(configure)
cli.add_command(chat)
cli.add_command(initdb)
cli.add_command(dropdb)
cli.add_command(nested_group)
cli.add_command(ai)

if __name__ == '__main__':
cli()
144 changes: 144 additions & 0 deletions mindmate/commands/ai.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import click
from mindmate.utils.conf import constants

def _generate_output(content: dict) -> None:
# Determine the maximum length of keys for formatting
try:
max_key_length = max(len(str(key)) for key in content.keys())
except ValueError as e:
max_key_length = 5

# Print the table headers
header = f'{"platform":<{max_key_length}} reference'
yield header + '\n'

header_line = f'{"-" * max_key_length} {"-" * max_key_length}'
yield header_line + '\n'

# Print the keys and values in the table
for key, value in content.items():
row = f'{str(key):<{max_key_length}} {value}'
yield row + '\n'

@click.group()
def ai():
"""Returns related functionalities to AI platforms"""
pass

@click.group()
def chat():
"""Returns related functionalities to chat-based AI platforms"""
pass

@click.group()
def image():
"""Returns related functionalities to image-based AI platforms"""
pass

@click.group()
def video():
"""Returns related functionalities to video-based AI platforms"""
pass

@click.group()
def prompting():
"""Returns related functionalities to chat-based AI platforms"""
pass

@click.group()
def design():
"""Returns related functionalities to design-based AI platforms"""
pass

@click.group()
def presentation():
"""Returns related functionalities to presentation-based AI platforms"""
pass

@click.group()
def no_code():
"""Returns related functionalities to build apps with no code using AI platforms"""
pass

@click.group()
def data():
"""Returns related functionalities to data manipulation AI platforms"""
pass

@click.group()
def coding():
"""Returns related functionalities to code-based AI platforms"""
pass

@click.command(name='list')
def list_chat():
"""a list of AI platforms that offer chat-based responses"""
click.echo_via_pager(_generate_output(constants.WRITING))

chat.add_command(list_chat)

@click.command(name='list')
def list_image():
"""a list of AI platforms that offer image-based responses"""
click.echo_via_pager(_generate_output(constants.IMAGES))

image.add_command(list_image)

@click.command(name='list')
def list_video():
"""a list of AI platforms that offer video-based responses"""
click.echo_via_pager(_generate_output(constants.VIDEO))

video.add_command(list_video)

@click.command(name='list')
def list_prompting():
"""a list of AI platforms that offer image-based responses"""
click.echo_via_pager(_generate_output(constants.PROMPTING))

prompting.add_command(list_prompting)

@click.command(name='list')
def list_design():
"""a list of AI platforms that offer design-based responses"""
click.echo_via_pager(_generate_output(constants.DESIGN))

design.add_command(list_design)

@click.command(name='list')
def list_presentation():
"""a list of AI platforms that offer presentation-based responses"""
click.echo_via_pager(_generate_output(constants.PRESENTATION))

presentation.add_command(list_presentation)

@click.command(name='list')
def list_no_code_apps():
"""a list of AI platforms that offer no-code development platform"""
click.echo_via_pager(_generate_output(constants.NO_CODE))

no_code.add_command(list_no_code_apps)

@click.command(name='list')
def list_data():
"""a list of AI platforms that offer data-based responses"""
click.echo_via_pager(_generate_output(constants.DATA))

data.add_command(list_data)

@click.command(name='list')
def list_coding():
"""a list of AI platforms that offer code-based responses"""
click.echo_via_pager(_generate_output(constants.CODING))

coding.add_command(list_coding)

ai.add_command(chat)
ai.add_command(image)
ai.add_command(video)
ai.add_command(prompting)
ai.add_command(design)
ai.add_command(presentation)
ai.add_command(no_code)
ai.add_command(data)
ai.add_command(coding)
73 changes: 73 additions & 0 deletions mindmate/commands/chat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import click
import sys
import openai
from mindmate.utils.utils import utility
from mindmate.utils.helper import help
from mindmate.utils.conf import constants


def stream_response(completion):
"""Stream the messages generated by the OpenAI API to the user."""
for choice in completion.choices:
if 'message' in choice and 'content' in choice.message:
return choice.message['content']

def model_option_callback(ctx, param, value):
try:
platform = ctx.params['platform']
except KeyError:
click.echo(f"{constants.SYS_ROLE}: You can't specify a platform/model alone, you need to specify them both or don't specify both.")
click.echo(f"{constants.HINT_ROLE}: try to re-order to the options and starts with --platform/-P then --model/-m and then --prompt/-p")
sys.exit(1)
if platform == 'openai':
return value if value in constants.MODEL_OPTIONS['openai'] else None
elif platform == 'aws':
return value if value in constants.MODEL_OPTIONS['aws'] else None
else:
return None

@click.command()
@click.option('-P', '--platform', required=True, default='openai', show_default=False, type=click.Choice(constants.PLATFORM_OPTIONS), help='use platform as an underlying technology')
@click.option('-m', '--model', required=True, default='text-davinci-003', show_default=True, type=str, callback=model_option_callback, help='select targeted model to utilize')
@click.option('-p', '--prompt', required=True, show_default=False, type=str, help='Your prompt to AI')
def chat(platform, model, prompt):
"""offers text-based response to your prompt"""
click.echo(help.generic_message())
# click.echo(click.get_current_context().params)
KEYS = utility.set_yaml_state(constants.FILE_PATH+'/'+constants.FILE_NAME)['keys']
if platform == 'openai':
if model not in constants.MODEL_OPTIONS['openai']:
click.echo(f"{constants.SYS_ROLE}: invalid model, pass one of the correct options python main.py chat --model {constants.MODEL_OPTIONS['openai']}")
sys.exit(1)
OPENAI_TOKEN = KEYS['openai_token']
OPENAI_ID = KEYS['openai_id']

openai.api_key = OPENAI_TOKEN
try:
#TODO: unstable code, due to not having openAI paid account
completion = openai.ChatCompletion.create(
model=model,
temperature=0,
stream=True,
user=OPENAI_ID,
messages = [
{
'role':'user',
'content': prompt
}
]
)
except openai.error.AuthenticationError as e:
click.echo(f"{constants.SYS_ROLE}: invalid credentials, use 'configure' command to provide valid token, see https://platform.openai.com/account/api-keys")
sys.exit(1)
except openai.error.RateLimitError as r:
click.echo(f"{constants.SYS_ROLE}: You exceeded your current OPENAI quota, check your plan and billing details")
sys.exit(1)

#TODO: not cooked code
for choice in completion.choices:
if 'message' in choice and 'content' in choice.message:
# return choice.message['content']
click.echo(choice.message['content'])
else:
click.echo(f'{constants.SYS_ROLE}: platform ({platform}) is not supported yet')
33 changes: 15 additions & 18 deletions mindmate/commands/configure.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,36 @@
import click
import yaml
import uuid
import os

def set_yaml_state(file: str) -> dict:
"""read yaml file and return it as a dictionary object"""
with open(file, 'r') as f:
data_dict = yaml.load(f, Loader=yaml.FullLoader)
return data_dict
def update_yaml_state(state: dict, file_path: str) -> None:
"""update new yaml state with only changed keys and maintain unchanged values"""
with open(file_path, 'w') as outputfile:
yaml.dump(state, outputfile, default_flow_style=False)
from mindmate.utils.utils import utility
from mindmate.utils.conf import constants

@click.command()
def configure():
"""collect keys from user and save it into state file as yaml"""

PATH = '~/.mindmate'
FILE_NAME = 'environment.yaml'
# PATH = '~/.mindmate'
# FILE_NAME = 'environment.yaml'

while not os.path.isfile(PATH+'/'+FILE_NAME):
while not os.path.isfile(constants.FILE_PATH+'/'+constants.FILE_NAME):
try:
os.makedirs(PATH)
os.makedirs(constants.FILE_PATH)
except FileExistsError as f:
pass
data = {
'version':1,
'keys': {
'openai_token':'xxxx'
'openai_token':'xxxx',
'openai_id':'xxxx',
}
}
with open(PATH+'/'+FILE_NAME, 'w') as outputfile:
with open(constants.FILE_PATH+'/'+constants.FILE_NAME, 'w') as outputfile:
yaml.dump(data, outputfile, default_flow_style=False)

file = set_yaml_state(PATH+'/'+FILE_NAME)
file = utility.set_yaml_state(constants.FILE_PATH+'/'+constants.FILE_NAME)
#TODO: check environment variable first before setting values into yaml file. If exist, then skip yaml update
openai_value = file['keys']['openai_token']
openai_user_token = click.prompt(f'OPENAI TOKEN [****************{openai_value[-4:]}]', type=str)
file['keys']['openai_token'] = openai_user_token
update_yaml_state(state=file, file_path=PATH+'/'+FILE_NAME)
file['keys']['openai_id'] = str(uuid.uuid4())
utility.update_yaml_state(state=file, file_path=constants.FILE_PATH+'/'+constants.FILE_NAME)
44 changes: 0 additions & 44 deletions mindmate/commands/nested_group.py

This file was deleted.

Loading

0 comments on commit d61fc63

Please sign in to comment.