Skip to content

Commit

Permalink
Basic flask app setup along with initial Estimations Module
Browse files Browse the repository at this point in the history
  • Loading branch information
martin-jamszolik committed Apr 24, 2023
0 parents commit 8cd1d07
Show file tree
Hide file tree
Showing 10 changed files with 387 additions and 0 deletions.
161 changes: 161 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
**/.DS_STORE
27 changes: 27 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use a Python base image
FROM python:3.11

# Set environment variables
ENV FLASK_APP=app.py \
FLASK_ENV=production \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PORT=5000

# Set the working directory
WORKDIR /app

# Copy the requirements file to the working directory
COPY requirements.txt .

# Install the required packages
RUN pip install -r requirements.txt

# Copy the application code to the working directory
COPY . .

# Expose the port that the Flask application will run on
EXPOSE $PORT

# Start the Flask application
CMD ["flask", "run", "--host", "0.0.0.0", "--port", "$PORT"]
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Welcome to Vivid AI Service

## Why?
Integrate best of AI frameworks available today. So that we can make use of such technologies within your application that could be written in a different web app stacks.

## How?
Expose a micro-service written in python flask to expose one of the latest AI web frameworks called `langchain`

## Setup

```sh
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
# to maintain latest requirements run:
pip install -U -r requirements.txt

```
Create a `.env` file for your local setup.

Run the application

```sh
python app.py

# alternatively
FLASK_CONFIG=config.DevelopmentConfig FLASK_APP=app.py flask run
```
18 changes: 18 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from flask import Flask
import os
import estimations

env_config = os.environ.get("FLASK_CONFIG","config.DevelopmentConfig")

app = Flask(__name__)

app.config.from_object(env_config)

print(f"Environment: {app.config['ENV']}")
print(f"Debug: {app.config['DEBUG']}")
print(f"Testing: {app.config['TESTING']}")

estimations.init_estimations(app)

if __name__ == '__main__':
app.run(debug = app.config['DEBUG'])
20 changes: 20 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import os

class Config(object):
DEBUG = True
TESTING = True
ENV = "development"
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")


class ProductionConfig(Config):
DEBUG = False
FLASK_DEBUG = 0
TESTING = False
ENV = "production"


class DevelopmentConfig(Config):
ENV = "development"
FLASK_DEBUG = 1
DEVELOPMENT = True
13 changes: 13 additions & 0 deletions estimations/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from flask import Blueprint
from flask_restful import Api
from .views import *


def init_estimations(app):
estimations_bp = Blueprint('estimations', __name__, url_prefix='/api/estimations')
api = Api(estimations_bp)
api.add_resource(ProposalList, '/proposals')
api.add_resource(ProposalDetail, '/proposals/<int:id>')
app.register_blueprint(estimations_bp)


23 changes: 23 additions & 0 deletions estimations/ai.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter
from langchain import OpenAI, VectorDBQA
from langchain.document_loaders import DirectoryLoader



class ProposalAIService:
loader = DirectoryLoader('../downloads/estimations/', glob='**/*.txt')
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)


def __init__(self, config):
embeddings = OpenAIEmbeddings(openai_api_key= config['OPENAI_API_KEY'] )
texts = self.text_splitter.split_documents(self.documents)
docsearch = Chroma.from_documents(texts, embeddings)
self.qa = VectorDBQA.from_chain_type(llm=OpenAI(), chain_type="stuff", vectorstore=docsearch)

def ask_question( self, query ):
return self.qa.run(query)

38 changes: 38 additions & 0 deletions estimations/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
class Proposal:
def __init__(self, id, location, tasks):
self.id = id
self.location = location
self.tasks = tasks

def serialize(self):
return {
'id': self.id,
'location': self.location,
'tasks': [task.serialize() for task in self.tasks]
}

class Task:
def __init__(self, name, quantity, price):
self.name = name
self.quantity = quantity
self.price = price

def serialize(self):
return {
'name': self.name,
'quantity': self.quantity,
'price': self.price
}

# Mock data
proposals = [
Proposal(0, '123 Main St, Anytown, USA', [
Task('Task 1', 2, 50.0),
Task('Task 2', 1, 75.0),
Task('Task 3', 4, 25.0)
]),
Proposal(1, '456 Elm St, Anytown, USA', [
Task('Task 4', 3, 100.0),
Task('Task 5', 2, 150.0)
])
]
42 changes: 42 additions & 0 deletions estimations/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from flask import jsonify, current_app, request
from flask_restful import Resource, reqparse
from .models import Proposal, Task, proposals
from .ai import ProposalAIService


parser = reqparse.RequestParser()
parser.add_argument('location', type=str, required=True, help='Location cannot be blank')
parser.add_argument('tasks', type=list, required=True, help='Tasks cannot be blank')

class ProposalList(Resource):
def get(self):
#service = ProposalAIService(current_app.config)
#service.ask_question("This is a basic test")
return jsonify([p.serialize() for p in proposals])

def post(self):
data = parser.parse_args()
proposal = Proposal(location=data['location'])
for task_data in data['tasks']:
task = Task(name=task_data['name'], quantity=task_data['quantity'], price=task_data['price'])
proposal.tasks.append(task)
return proposal.serialize(), 201

class ProposalDetail(Resource):
def get(self, id):
proposal = proposals[id]
return proposal.serialize()

def put(self, id):
proposal = proposals[id]
data = parser.parse_args()
proposal.location = data['location']
proposal.tasks = []
for task_data in data['tasks']:
task = Task(name=task_data['name'], quantity=task_data['quantity'], price=task_data['price'])
proposal.tasks.append(task)
return proposal.serialize()

def delete(self, id):
return '', 204

Loading

0 comments on commit 8cd1d07

Please sign in to comment.