Skip to content

Azd demo #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,4 @@ RUN export DEBIAN_FRONTEND=noninteractive \
&& apt-get clean -y && rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://aka.ms/install-azd.sh | bash

RUN python3 -m pip install --upgrade pip

# Copy requirements.txt (if found) to a temp location so we update the environment. Also
# copy "noop.txt" so the COPY instruction does not fail if no requirements.txt exists.
COPY requirements.txt* .devcontainer/noop.txt /tmp/pip/
RUN if [ -f "/tmp/pip/requirements.txt" ]; then umask 0002 && python3 -m pip install -r /tmp/pip/requirements.txt && sudo rm -rf /tmp/pip; fi

# sudo apt update
# sudo apt-get -y install ufw
# sudo ufw allow 8000
RUN python3 -m pip install --upgrade pip
5 changes: 2 additions & 3 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"name": "FastAPI",
"build": {
"context": "..",
"context": ".",
"dockerfile": "Dockerfile"
},

Expand All @@ -19,7 +19,6 @@
// Set *default* container specific settings.json values on container create.
"settings": {
"[python]": {
"defaultInterpreterPath": "/opt/conda/envs/myenv/bin/python",
"editor.formatOnType": true,
"editor.formatOnSave": true
}
Expand All @@ -45,7 +44,7 @@
"memory": "8gb"
},

"postAttachCommand": "export DATASTORE=redis && export BEARER_TOKEN=footoken && export OPENAI_API_KEY='' && docker compose -f ./docker-compose.yml up -d"
"postAttachCommand": "chmod +x .devcontainer/setup.sh && .devcontainer/setup.sh"

// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "pip3 install --user -r requirements.txt",
Expand Down
1 change: 0 additions & 1 deletion .devcontainer/noop.txt

This file was deleted.

33 changes: 33 additions & 0 deletions .devcontainer/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/sh
# set -eu

# Install libraries from requirements.txt
chmod +x ./requirements.txt && pip install -r ./requirements.txt
echo

# Check if the "redis" container is running
if ! docker ps --filter "status=running" --format "{{.Names}}" | grep -q "redis"; then
# If the "redis" container is not running, start it using docker-compose
docker-compose -f ./docker-compose.yml up -d
else
echo "The 'redis' container is already running."
fi

echo
echo "Let's set up your development environment..."
echo
echo "Please enter your OpenAI API key found here: https://platform.openai.com/account/api-keys:"
read -r OPENAI_API_KEY

# Export the OPENAI_API_KEY environment variable
export OPENAI_API_KEY
export DATASTORE=redis
export BEARER_TOKEN=footoken
export PLUGIN_HOSTNAME=https://$CODESPACE_NAME-8000.$GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN

echo
echo "Setting host configuration (from ./hostconfig.sh)..."
chmod +x ./hostconfig.sh && ./hostconfig.sh

echo
echo "Enter 'footoken' if OpenAI prompts you for a Bearer Token"
2 changes: 1 addition & 1 deletion .vscode/json.code-snippets
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"OpenAPI Manifest": {
"prefix": "manifest-openapi",
"body": ["{\n\t\"schema_version\": \"${1:v1}\",\n\t\"name_for_human\": \"${2}\",\n\t\"name_for_model\": \"${3}\",\n\t\"description_for_human\": \"${4}\",\n\t\"description_for_model\": \"${5}\",\n\t\"auth\": {\n\t\t\"type\": \"${6:none}\"\n\t},\n\t\"api\": {\n\t\t\"type\": \"openapi\",\n\t\t\"url\": \"${7:https://your-app-url.com/openapi.yaml}\",\n\t\t\"is_user_authenticated\": \"${8:false}\"\n\t},\n\t\"logo_url\": \"${9:https://example.com/logo.png}\",\n\t\"contact_email\": \"${10:[email protected]}\",\n\t\"legal_info_url\": \"${11:https://example.com/legal}\"\n}$0"],
"body": ["{\n\t\"schema_version\": \"${1:v1}\",\n\t\"name_for_human\": \"${2}\",\n\t\"name_for_model\": \"${3}\",\n\t\"description_for_human\": \"${4}\",\n\t\"description_for_model\": \"${5}\",\n\t\"auth\": {\n\t\t\"type\": \"${6:none}\"\n\t},\n\t\"api\": {\n\t\t\"type\": \"openapi\",\n\t\t\"url\": \"${7:https://your-app-url.com/openapi.yaml}\",\n\t\t\"is_user_authenticated\": \"${8:false}\"\n\t},\n\t\"logo_url\": \"${9:https://your-app-url.com/logo.png}\",\n\t\"contact_email\": \"${10:[email protected]}\",\n\t\"legal_info_url\": \"${11:https://example.com/legal}\"\n}$0"],
"description": "OpenAI manifest"
}
}
15 changes: 8 additions & 7 deletions ai-plugin.json → .well-known/ai-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
"description_for_human": "Todo app for managing your tasks",
"description_for_model": "Todo app for managing your tasks",
"auth": {
"type": "user_http",
"authorization_type": "bearer"
"type": "user_http",
"authorization_type": "bearer"
},
"api": {
"type": "openapi",
"url": "https://your-app-url.com/openapi.yaml",
"is_user_authenticated": "false"
"type": "openapi",
"url": "https://your-app-url.com/.well-known/openapi.yaml",
"is_user_authenticated": "false"
},
"logo_url": "https://example.com/logo.png",
"logo_url": "https://your-app-url.com/.well-known/logo.png",
"contact_email": "[email protected]",
"legal_info_url": "https://example.com/legal"
}
}

Binary file added .well-known/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 36 additions & 29 deletions openapi.yaml → .well-known/openapi.yaml
Original file line number Diff line number Diff line change
@@ -1,82 +1,89 @@
openapi: 3.0.0
openapi: 3.0.2
info:
title: TODO app
description: Todo app for managing your tasks
title: OpenAI plugin for a simple todo app
description: Todo app for managing your tasks on ChatGPT
version: 1.0.0
servers:
- url: https://your-app-url.com
paths:
/todos:
post:
summary: Create a new TODO item
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TodoItem'
description: Accepts a string and adds as new TODO item
operationId: create_todo
parameters:
- in: query
name: todo
schema:
type: string
required: true
description: The description of the TODO item
responses:
'200':
"200":
description: OK
content:
application/json:
schema:
type: object
properties:
item_id:
type: integer
format: int64
$ref: "#/components/schemas/TodoItem"
get:
summary: Get a list of all TODO items
operationId: list_todos
responses:
'200':
"200":
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/TodoItem'
/todos/{item_id}:
$ref: "#/components/schemas/TodoList"
/todos/{todo_id}:
get:
summary: Get a TODO item by ID
operationId: get_todo
parameters:
- name: item_id
- name: todo_id
in: path
required: true
description: ID of the TODO item to retrieve
schema:
type: integer
format: int64
responses:
'200':
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/TodoItem'
$ref: "#/components/schemas/TodoItem"
"404":
description: Todo not found
delete:
summary: Delete a TODO item by ID
operationId: delete_todo
parameters:
- name: item_id
- name: todo_id
in: path
required: true
description: ID of the TODO item to delete
schema:
type: integer
format: int64
responses:
'204':
description: No Content
"204":
description: Todo deleted
"404":
description: Todo not found
components:
schemas:
TodoItem:
type: object
properties:
title:
type: string
description:
todo:
type: string
todo_id:
type: integer
format: int32
readOnly: true
required:
- title
- description
- todo
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ This is a sample repo for developing OpenAI plugin using the FastAPI framework.
- Code for the FastAPI app
- Code to help deploy the app on the cloud

## Code to help setup development environment
## 📦 Code to help setup development environment
Create a Codespaces by clicking **<> Code** -> **Codespaces** -> **Create codespaces on {branch}**, and a containerized development environment will be set up for you on the cloud based on the contents of the following files.

### **.devcontainer**
The `.devcontainer` folder contains files for defining a containerized development environment, specific to building this FastAPI app. It's set up in a way that makes it easy for you to use with GitHub Codespaces as well: launch a Codespace using this template, and you're ready to start developing! Learn more about devcontainers [here](https://containers.dev/).
Expand All @@ -16,8 +17,8 @@ The `.vscode` folder contains:
- `settings.json` file that helps to validate the manifest file (`ai-plugin.json`) against [this schema](https://github.com/minsa110/ai-plugin-schema/blob/main/ai-plugin-schema.json).
- `launch.json` file that helps to customize **Run and Debug**.

## Code for the FastAPI app
To test the app, run `uvicorn main:app` in the integrated terminal, or press `F5`, and debug CRUD operations at .../docs.
## 💻 Code for the FastAPI app
If you have [access](https://code.visualstudio.com/blogs/2023/03/30/vscode-copilot#_getting-started-today) to [GitHub Copilot](https://github.com/features/copilot), try it out to help you write code faster. To test the app, run `uvicorn main:app` in the integrated terminal, or press `F5`, and debug CRUD operations at .../docs.

- `main.py` was the plugin code generated by Copilot. Learn more about Copilot here. The prompt used here is:
```markdown
Expand All @@ -29,7 +30,7 @@ To test the app, run `uvicorn main:app` in the integrated terminal, or press `F5
```
- `ai-plugin.json` is a JSON manifest file that defines relevant metadata for the plugin.

## Code to help deploy the app on the cloud
## ☁️ Code to help deploy the app on the cloud
This repo uses Azure Developer CLI to create two Azure Container Apps: one for the API and the other for the vector database (using Redis), then deploys the app. You can use the following commands to invoke the deployment flow:
```bash
azd auth login # for now, use azd login
Expand All @@ -41,3 +42,6 @@ azd up

- `azure.yaml` describes the application for Azure Developer CLI (`azd`). Learn more about Azure Developer CLI [here](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/overview).
- `infra` folder contains infra-as-code files (Bicep) needed to provision the Azure resource.

## 💬 Register the app on ChatGPT
- Copy the container app link and paste it to ChatGPT plugin
13 changes: 11 additions & 2 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
#!/bin/sh
PLUGIN_HOSTNAME=$(echo "$PLUGIN_HOSTNAME" | sed 's/^"//' | sed 's/"$//')
sed -i 's|https://your-app-url.com|'$PLUGIN_HOSTNAME'|g' ./ai-plugin.json ./openapi.yaml
sed -i 's|https://your-app-url.com|'$PLUGIN_HOSTNAME'|g' ./.well-known/ai-plugin.json ./.well-known/openapi.yaml ./main.py

exec uvicorn main:app --host 0.0.0.0 --port "${PORT:-${WEBSITES_PORT:-8080}}"
exec uvicorn main:app --host 0.0.0.0 --port "${PORT:-${WEBSITES_PORT:-8080}}"


#!/bin/sh
set -eu

./hostconfig.sh

# Heroku uses PORT, Azure App Services uses WEBSITES_PORT, Fly.io uses 8080 by default
exec uvicorn server.main:app --host 0.0.0.0 --port "${PORT:-${WEBSITES_PORT:-8080}}"
69 changes: 69 additions & 0 deletions hostconfig.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/bin/sh
set -eu

# # Set the plugin hostname for Codespaces
# # Check if CODESPACES environment variable is set to true
# if [ "$CODESPACES" = "true" ]; then
# # If CODESPACES is true and PLUGIN_HOSTNAME is undefined or empty, set PLUGIN_HOSTNAME
# if [ -z "$PLUGIN_HOSTNAME" ]; then
# # Check if CODESPACE_NAME and GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN are set
# if [ -z "$CODESPACE_NAME" ] || [ -z "$GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN" ]; then
# echo "CODESPACE_NAME and/or GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN environment variables are not set."
# exit 1
# fi
# # Set PLUGIN_HOSTNAME to the expanded version of the URL
# PLUGIN_HOSTNAME="https://$CODESPACE_NAME-8000.$GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN"
# fi
# else
# # If CODESPACES is not true, check if PLUGIN_HOSTNAME is set
# if [ -z "$PLUGIN_HOSTNAME" ]; then
# echo "PLUGIN_HOSTNAME environment variable is not set."
# exit 1
# fi
# fi

# Set the plugin hostname for Azure in the azd environment
if [ "$SERVICE_API_URI" = "true" ]; then
PLUGIN_HOSTNAME=$(echo "$SERVICE_API_URI" | sed 's/^"//' | sed 's/"$//')
echo "PLUGIN_HOSTNAME environment variable is successfully set to Azure SERVICE_API_URI."
else
echo "PLUGIN_HOSTNAME environment variable is not set."
exit 1
fi

# Input JSON file
json_input_file="./.well-known/ai-plugin.json"

# Input YAML file
yaml_input_file="./.well-known/openapi.yaml"

# Create temporary files to store the modified JSON and YAML
temp_json_file=$(mktemp)
temp_yaml_file=$(mktemp)

# Read the JSON file and perform the substitutions using jq
jq --arg plugin_hostname "$PLUGIN_HOSTNAME" '
.api.url = ($plugin_hostname + "/.well-known/openapi.yaml") |
.logo_url = ($plugin_hostname + "/.well-known/logo.png")
' "$json_input_file" > "$temp_json_file"

# Find the line number where the "servers:" key is located in the YAML file
servers_line_number=$(grep -n "servers:" "$yaml_input_file" | cut -d: -f1)

# Update the YAML file using sed and awk
awk -v line_number="$servers_line_number" -v plugin_hostname="$PLUGIN_HOSTNAME" '
NR == line_number + 1 {
sub(/url: .*/, "url: " plugin_hostname)
}
{ print }
' "$yaml_input_file" > "$temp_yaml_file"

# Overwrite the original JSON file with the modified contents
mv "$temp_json_file" "$json_input_file"

# Overwrite the original YAML file with the modified contents
mv "$temp_yaml_file" "$yaml_input_file"

# Print success messages
echo "$json_input_file has been updated successfully."
echo "$yaml_input_file file has been updated successfully."
Loading