aiengine
Table of Contents
- First Time Setup
- Local Installation
- Working with Poetry
- Testing
- Troubleshooting
- Deployment
- Logs
- Secret Management
- Contributing
- License
First Time Setup
Install Docker for Mac.
Clone lp-osx-setup and run the setup script.
Clone the lead-pages repo and run the bootstrap script.
Get a short-lived auth token from gcloud:
gcloud docker -a
Build and run your containers:
docker-compose up
After building and running your containers, you'll find the following services via HTTP:
| Name | URL |
|---|---|
| aiengine | http://aiengine.docker |
| Datastore | http://datastore.aiengine.docker |
| RabbitMQ | http://rabbit.aiengine.docker |
| Profiler | http://aiengine.docker/_profiler_ |
Local Installation
To install dependencies locally run the following commands:
- (optional) Install and configure pyenv
brew install pyenv- Run
pyenv initand follow the directions - Restart your terminal or run
eval "$(pyenv init -)"
brew install graphviz libmemcached- Install poetry
poetry install
Running the App Locally
- Update your
docker/envfile withOPENAI_API_KEY=[insert-api-key-here](make sure to insert your API key!) - Spin up your local environment from your lp-docker directory via
./up leadpages stargate fe-builder warehouse mandrel aiengine
Working with Poetry
Poetry is the package management solution used for this project. Consult its documentation for more in-depth usage.
Package Management
There are two files used for managing dependencies:
pyproject.tomlmanages the indexes, packages and Python version required by the project.poetry.lockhelps produce deterministic builds
Management of poetry.lock is automatic, and directly editing it should not be necessary under normal circumstances.
See the documentation for poetry for details on it's commands and using them to manage packages for your project.
Testing
Run the test suite with py.test. These commands assume you have the project
virtual environment active, otherwise you will need to prepend them with
poetry run
Filtering tests
py.test -k test_name
Running tests in parallel
py.test -nauto
Troubleshooting
Using the remote debugger
You can run the remote debugger at any point by doing
from common import start_debugger
start_debugger()
If you're debugging a handler, make a request to that handler and then connect to the debugger using a command like:
nc -C aiengine.docker 4444
or
socat readline tcp:aiengine.docker:4444
See the manual for a list of debugger commands.
Note: by default this port is not exposed through docker-compose.yml.
This avoids clashing ports when running more than one API in parallel, and as
a result, you will have to add it yourself. You can change the port by
editing config/base.yaml.
Debugging in PyCharm
Note that the default configuration for this project has test coverage forced on. This breaks PyCharm's debugger, so in PyCharm, configure your default run/debug configuration for PyTest to include the --no-cov switch.
Building the Documentation
TODO: Fix this section, as it is outdated and does not function.
To build the documentation:
cd docs/apibundle installbundle exec middleman buildbundle exec middleman server- Navigate to http://localhost:4567
Deployment
Pipeline is triggered by tagged push to master. kicking off a process that will result in your code changes deployed to test.
bumpversion patch git push origin master --tags
Test
After the tagged push to master, Cloud Build will fire a trigger entitled aiengine-image-build (which uses the cloudbuild.yaml file in the parent directory of this repo) this trigger builds a new image, runs tests and pushes it to Artifact Repository.
The last build step in the cloudbuild.yaml triggers an additional cloud build trigger that handles the deployment ('aiengine-test-deployment'). This handles the deployment to test without any further action from you.
You can view the progress of the this by selecting the build from the leadpage-test Cloud Build Console.
To manually deploy to test with a different image:
- Navigate to the aiengine Artifact Registry folder
- Copy the value in the 'tags' column for the image that you want to deploy.
- Open
leadpage-testcloud build trigger console and run the trigger entitledaiengine-test-deployment, pasting in the tag you copied in the previous step into theSHORT_SHAcorresponding empty input box (where it says Value 1). then hit 'run trigger'.
The aiengine-test-deployment cloud build trigger changes the image value for ai-copy in our lp-flux repo. Once the steps are complete, Flux will reconcile the existing pods in the test cluster (api-cluster-1 in leadpage-test) with the new image, rolling out new pods and terminating the old ones. You can view the progress on that by running the following commands in a terminal:
Authenticate with test cluster:
gcloud container clusters get-credentials api-cluster-1 --zone us-central1-b --project leadpage-test
View pods getting cycled as flux reconciles:
kubectl get pods | grep aiengine
To confirm its running the right image, run the following command against one of the pods you got in the previous kubectl get pods command:
kubectl get pod **POD_NAME** -o yaml | grep "image:"
Production
For Production, the steps are similar to the manual test deployment.
- You will need to copy the value in the 'tags' column for the image you want deployed to production in the ai-copy Artifact Registry.
- Open
lead-pagescloud build trigger console and run the trigger entitledaiengine-production-deployment, pasting in the tag you copied in the previous step into theSHORT_SHAcorresponding empty input box (where it says Value 1). then hit 'run trigger'.
Similar to what happened in test, the cloud build trigger changes the image value in the lp-flux repo, and flux will reconcile the existing pods in the production cluster (api-cluster-1 in lead-pages) with the new image.
Viewing the progress is also the same as the test deployment:
Authenticate with prod cluster:
gcloud container clusters get-credentials api-cluster-1 --zone us-central1-b --project lead-pages
View pods getting cycled as flux reconciles:
kubectl get pods | grep aiengine
To confirm its running the right image, run the following command against one of the pods you got in the previous kubectl get pods command:
kubectl get pod **POD_NAME** -o yaml | grep "image:"
Logs
To view running application logs, there are 2 options. First way is to run the following query in Gcloud logs exlorer. To find prod logs, switch the project to 'lead-pages'
the other way is to view the running pod logs directly, by running following commands:
set Gcloud kube config to correct context for cluster/ project/ etc: for prod: gcloud container clusters get-credentials api-cluster-1 --zone us-central1-b --project leadpage-test
for test: gcloud container clusters get-credentials api-cluster-1 --zone us-central1-b --project lead-pages
get pod name. will look something like "ai-copy-87cbdd94f-jzt2d" kubectl get pods
retrieve logs and print to terminal. k logs ai-copy-87cbdd94f-jzt2d --follow
Secret Management
Ai-copy uses a token to authenticate with the OpenAI API. https://beta.openai.com/docs/introduction. The secret is stored in https://console.cloud.google.com/security/secret-manager?project=center-infra in the center-infra project. Under the name 'aicopy'
Contributing
Clone the repo, create a branch for your work, test and develop your changes, and submit a pull request.
License
© 2023 LeadPages. See the LICENSE.md file for details.
Brought to you by Disaster Artists (@iddqdkfa, @jcravenlp, @noerbot, @TZawalich) and the #ai-copy team (@nmabusthLP, @evanmcneely). 💥🎨💥
Endpoints
/aiengine/v1
Ping
/aiengine/v1/ping
A no-op endpoint that can be used to check what the currently-deployed version of the API is.
Request
GET /aiengine/v1/ping
Get the currently-deployed version of the API.
| Body | |
|---|---|
| Empty | |
Response
| Body | |
|---|---|
_meta._version |
StringThe current internal version number of the API. |
_meta |
Dictionary |
| Responses | |
|---|---|
200 OK |
|
| Errors | |
|---|---|
| Empty | |
Copy
/aiengine/v1/copy
Request
POST /aiengine/v1/copy
Generate text with OpenAI.
| Body | |
|---|---|
description |
String |
tone |
Enum('casual', 'convincing', 'enthusiastic', 'formal', 'funny', 'inspirational', 'joyful', 'passionate', 'thoughtful') |
variants |
Number? |
useCase |
Enum('headline', 'cta', 'expand', 'summary', 'rewrite') |
randomizeTone |
Boolean? |
planInfo |
PlanInfo |
Response
| Body | |
|---|---|
data |
List[String] |
tone |
Enum('casual', 'convincing', 'enthusiastic', 'formal', 'funny', 'inspirational', 'joyful', 'passionate', 'thoughtful') |
user |
String |
usage |
UsageInfo |
creditBalance |
CreditDetails |
| Responses | |
|---|---|
200 OK |
|
| Errors | |
|---|---|
| Empty | |
Image
/aiengine/v1/image
Request
GET /aiengine/v1/image
Get the keywords and styles that can be used with image prompts.
| Body | |
|---|---|
| Empty | |
Response
| Body | |
|---|---|
styles |
List[Style] |
keywords |
List[KeywordsByCategory] |
| Responses | |
|---|---|
200 OK |
|
| Errors | |
|---|---|
| Empty | |
Request
POST /aiengine/v1/image
Generate images with OpenAI.
| Body | |
|---|---|
prompt |
String |
keywords |
List?[String]Keywords selected by the user to build the prompt |
style |
Enum('none', 'photo', 'painting', 'sketch', 'watercolor', '3D')Style selected by the user to build the prompt |
variants |
Number? |
width |
Number? |
height |
Number? |
response_format |
Enum('b64_json', 'url') |
planInfo |
PlanInfo |
Response
| Body | |
|---|---|
images |
List[Image] |
prompt |
String |
keywords |
List[String] |
style |
String |
variants |
Number |
user |
String |
usage |
UsageInfo |
creditBalance |
CreditDetails |
| Responses | |
|---|---|
200 OK |
|
| Errors | |
|---|---|
| Empty | |
Credit
/aiengine/v1/credit
Request
GET /aiengine/v1/credit
| Query string | |
|---|---|
level |
Enum('standard', 'start', 'pro', 'advanced', 'enterprise') |
period |
Enum('month', 'year', '2years', 'lifetime') |
trial |
Enum('1', '0') |
user |
String?The user's UUID |
Response
| Body | |
|---|---|
creditsAllotted |
Or[Number, String]The number of credits allotted to the user based on their plan type and period |
creditsUsed |
NumberThe number of credits used so far this month |
creditsRemaining |
Or[Number, String]The number of credits remaining this month |
| Responses | |
|---|---|
200 OK |
|
| Errors | |
|---|---|
| Empty | |
Request
PATCH /aiengine/v1/credit
Update the number of credits used by a user this month.
| Body | |
|---|---|
creditsUsed |
Number(0 < n)The number of credits used so far this month |
user |
StringThe user's UUID |
Response
| Body | |
|---|---|
creditsUsed |
Number(0 < n)The number of credits used so far this month |
user |
StringThe user's UUID |
| Responses | |
|---|---|
200 OK |
|
| Errors | |
|---|---|
| Empty | |
Definitions
PlanInfo
| PlanInfo | |
|---|---|
planLevel |
Enum('standard', 'start', 'pro', 'advanced', 'enterprise') |
planPeriod |
Enum('month', 'year', '2years', 'lifetime') |
isTrial |
Boolean |
UsageInfo
| UsageInfo | |
|---|---|
total |
Number |
CreditDetails
| CreditDetails | |
|---|---|
creditsAllotted |
Or[Number, String]The number of credits allotted to the user based on their plan type and period |
creditsUsed |
NumberThe number of credits used so far this month |
creditsRemaining |
Or[Number, String]The number of credits remaining this month |
Style
| Style | |
|---|---|
id |
Enum('none', 'photo', 'painting', 'sketch', 'watercolor', '3D') |
label |
String |
image |
String |
KeywordsByCategory
| KeywordsByCategory | |
|---|---|
label |
Enum('Composition', 'Color', 'Mood') |
keywords |
List[Keyword] |
Keyword
| Keyword | |
|---|---|
id |
Enum('35mm', '4K', '50mm', 'abstract', 'aerial_view', 'airbrush', 'amigurumi', 'amorphous', 'anime', 'art_deco', 'autochrome', 'background', 'blocks', 'blurred', 'bokeh', 'botanical', 'charcoal', 'childs_drawing', 'childs_painting', 'clean', 'close_up', 'collage', 'color_blocks', 'colored_pencil', 'comic_book', 'contemporary', 'cubism', 'decorative', 'double_exposure', 'earthy', 'elegant', 'etching', 'expressionism', 'fss', 'fauvism', 'fish_eye', 'flatlay', 'futuristic', 'geometric', 'graffiti', 'HD', 'ink', 'kodachrome', 'lomography', 'long_shot', 'low_angle', 'minimalist', 'mosaic', 'motion_blur', 'organic', 'overhead', 'paper_embossing', 'papercraft', 'pencil', 'photoreal', 'polaroid', 'post_impressionism', 'rhythmic', 'romanticism', 'screenprint', 'silhouette', 'simple', 'sss', 'still_life', 'street_art', 'surreal', 'surreal_photo', 'telephoto', 'ultra_realistic', 'vector_art', 'vector_line', 'wallpaper', 'woodcut', 'analogue', 'auburn', ' black', 'black_white', 'blue', 'blue_hour', 'brick', 'cinematic', 'colorful', 'cream', 'cyan', 'dark', 'deep', 'duotone', 'glitter', 'gold', 'goldenhour', 'gray', 'green', 'ivory', 'maroon', 'midday', 'monochromatic', 'muted', 'neon', 'overcast', 'oversaturated', 'pastel', 'purple', 'red', 'rose_gold', 'sepia', 'silver', 'tan', 'warm', 'white', 'desolate', 'dramatic', 'dreamy', 'emotive', 'enchanting', 'energetic', 'fantasy', 'gloomy', 'gothic', 'melancholy', 'moody', 'negative', 'playful', 'positive', 'romantic', 'serene', 'tranquil', 'cold') |
label |
String |
category |
Enum('Composition', 'Color', 'Mood') |
Image
| Image | |
|---|---|
id |
String |
uri |
String |
width |
Number |
height |
Number |