Skip to content

Commit

Permalink
chatapi: docker Implementation of node proxy service for openAI API (f…
Browse files Browse the repository at this point in the history
…ixes #7153) (#7168)

Co-authored-by: dogi <dogi@users.noreply.github.com>
  • Loading branch information
Mutugiii and dogi authored Jul 19, 2023
1 parent 3984638 commit fdf6213
Show file tree
Hide file tree
Showing 27 changed files with 594 additions and 18 deletions.
108 changes: 108 additions & 0 deletions .github/workflows/planet-chat.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: Planet Chat API Builder

on:
push:
branches-ignore:
- l10n_i18n
release:
types: [published]
workflow_dispatch:

env:
DOCKER_ORG: treehouses
DOCKER_REPO_TAG: planet-tags
DOCKER_REPO: planet

jobs:

build-prepare:
name: Preparing
runs-on: ubuntu-latest
outputs:
planet_version: ${{ steps.step1.outputs.version }}
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Setting Env
id: step1
run: |
version=$(jq '.version' package.json | sed -e 's/^"//' -e 's/"$//')
echo "::set-output name=version::$version"
build:
name: Chat API build
needs: [ build-prepare ]
runs-on: ubuntu-latest
env:
PLANET_VERSION: ${{ needs.build-prepare.outputs.planet_version }}

strategy:
matrix:
arch: [ amd64, arm, arm64 ]

steps:
- name: Checkout Code
uses: actions/checkout@v2

- name: Docker login
run: docker login -u ${{ secrets.DOCKERUSERNAME }} -p ${{ secrets.DOCKERAPIKEY }}

- name: Build image
run: |
repo="$DOCKER_ORG/$DOCKER_REPO_TAG:${{ matrix.arch }}-chatapi-$PLANET_VERSION-$GITHUB_REF_NAME-${GITHUB_SHA::8}"
branchrepo="$DOCKER_ORG/$DOCKER_REPO_TAG:${{ matrix.arch }}-chatapi-$PLANET_VERSION-$GITHUB_REF_NAME"
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker build -f './docker/chatapi/${{ matrix.arch }}-Dockerfile' -t $repo .
docker images
docker tag $repo $branchrepo
docker push $repo
docker push $branchrepo
multiarch:
name: Manifest build
needs: [ build-prepare, build ]
env:
PLANET_VERSION: ${{ needs.build-prepare.outputs.planet_version }}
runs-on: ubuntu-latest

steps:
- name: Docker login
run: docker login -u ${{ secrets.DOCKERUSERNAME }} -p ${{ secrets.DOCKERAPIKEY }}

- name: Multiarch Deploy
run: |
sudo wget -O /usr/local/bin/manifest_tool https://github.com/estesp/manifest-tool/releases/download/v0.7.0/manifest-tool-linux-amd64
sudo chmod +x /usr/local/bin/manifest_tool
mkdir -p /tmp/MA_manifests
latesttag="$DOCKER_ORG/$DOCKER_REPO_TAG:chatapi-$PLANET_VERSION-$GITHUB_REF_NAME-${GITHUB_SHA::8}" yq e -n '.image = strenv(latesttag)' | \
amd64tag="$DOCKER_ORG/$DOCKER_REPO_TAG:amd64-chatapi-$PLANET_VERSION-$GITHUB_REF_NAME-${GITHUB_SHA::8}" yq e '.manifests[0].image = strenv(amd64tag)' - | \
yq e '.manifests[0].platform.architecture = "amd64"' - | \
yq e '.manifests[0].platform.os = "linux"' - | \
armtag="$DOCKER_ORG/$DOCKER_REPO_TAG:arm-chatapi-$PLANET_VERSION-$GITHUB_REF_NAME-${GITHUB_SHA::8}" yq e '.manifests[1].image = strenv(armtag)' - | \
yq e '.manifests[1].platform.architecture = "arm"' - | \
yq e '.manifests[1].platform.os = "linux"' - | \
arm64tag="$DOCKER_ORG/$DOCKER_REPO_TAG:arm64-chatapi-$PLANET_VERSION-$GITHUB_REF_NAME-${GITHUB_SHA::8}" yq e '.manifests[2].image = strenv(arm64tag)' - | \
yq e '.manifests[2].platform.architecture = "arm64"' - | \
yq e '.manifests[2].platform.os = "linux"' - | \
tee /tmp/MA_manifests/MA_chatapi_latest.yaml
manifest_tool push from-spec /tmp/MA_manifests/MA_chatapi_latest.yaml
- name: Multiarch Deploy Versioned
if: ${{ github.event_name == 'release' }}
run: |
sudo wget -O /usr/local/bin/manifest_tool https://github.com/estesp/manifest-tool/releases/download/v0.7.0/manifest-tool-linux-amd64
sudo chmod +x /usr/local/bin/manifest_tool
mkdir -p /tmp/MA_manifests
versiontag="$DOCKER_ORG/$DOCKER_REPO:chatapi-$PLANET_VERSION" yq e -n '.image = strenv(versiontag)' | \
yq e '.tags |= . + ["chatapi"] ' - | \
amd64tag="$DOCKER_ORG/$DOCKER_REPO_TAG:amd64-chatapi-$PLANET_VERSION-$GITHUB_REF_NAME-${GITHUB_SHA::8}" yq e '.manifests[0].image = strenv(amd64tag)' - | \
yq e '.manifests[0].platform.architecture = "amd64"' - | \
yq e '.manifests[0].platform.os = "linux"' - | \
armtag="$DOCKER_ORG/$DOCKER_REPO_TAG:arm-chatapi-$PLANET_VERSION-$GITHUB_REF_NAME-${GITHUB_SHA::8}" yq e '.manifests[1].image = strenv(armtag)' - | \
yq e '.manifests[1].platform.architecture = "arm"' - | \
yq e '.manifests[1].platform.os = "linux"' - | \
arm64tag="$DOCKER_ORG/$DOCKER_REPO_TAG:arm64-chatapi-$PLANET_VERSION-$GITHUB_REF_NAME-${GITHUB_SHA::8}" yq e '.manifests[2].image = strenv(arm64tag)' - | \
yq e '.manifests[2].platform.architecture = "arm64"' - | \
yq e '.manifests[2].platform.os = "linux"' - | \
tee /tmp/MA_manifests/MA_chatapi_versioned.yaml
manifest_tool push from-spec /tmp/MA_manifests/MA_chatapi_versioned.yaml
10 changes: 6 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.

# compiled output
/dist
/tmp
/out-tsc
dist/
tmp/
out-tsc/

# dependencies
/node_modules
node_modules/

# IDEs and editors
/.idea
Expand Down Expand Up @@ -51,3 +51,5 @@ package-lock.json
# extra yml files generated
docker/planet-*.yml
planet.yml

.env
2 changes: 2 additions & 0 deletions chatapi/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
139 changes: 139 additions & 0 deletions chatapi/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
{
"root": true,
"plugins": ["@typescript-eslint"],
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2021,
"sourceType": "module"
},
"rules": {
"@typescript-eslint/array-type": ["error", { "default": "array-simple" }],
"@typescript-eslint/brace-style": ["error", "1tbs", { "allowSingleLine": true }],
"@typescript-eslint/comma-spacing": "error",
"@typescript-eslint/consistent-type-assertions": ["error", { "assertionStyle": "as", "objectLiteralTypeAssertions": "allow" }],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/indent": ["error", 2],
"@typescript-eslint/member-delimiter-style": ["error", {
"multiline": {
"delimiter": "semi",
"requireLast": true
},
"singleline": {
"delimiter": "semi",
"requireLast": false
}
}],
"@typescript-eslint/naming-convention": ["error",
{
"selector": "variableLike",
"format": ["camelCase", "UPPER_CASE"]
},
{
"selector": "variable",
"modifiers": ["exported"],
"format": ["PascalCase", "camelCase", "UPPER_CASE"]
},
{
"selector": "typeLike",
"format": ["PascalCase"]
},
{
"selector": "enumMember",
"format": ["UPPER_CASE"]
},
{
"selector": "property",
"modifiers": ["static"],
"format": ["camelCase", "UPPER_CASE"]
},
{
"selector": "property",
"modifiers": ["private"],
"format": ["camelCase", "snake_case"]
},
{
"selector": "method",
"modifiers": ["private"],
"format": ["camelCase"]
},
{
"selector": "interface",
"format": ["PascalCase"]
},
{
"selector": "class",
"format": ["PascalCase"]
}
],
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-inferrable-types": ["error", { "ignoreParameters": true }],
"@typescript-eslint/no-non-null-assertion": "error",
"@typescript-eslint/no-shadow": "error",
"@typescript-eslint/no-unused-expressions": "error",
"@typescript-eslint/no-use-before-define": "error",
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/quotes": ["error", "single"],
"@typescript-eslint/semi": ["error", "always"],
"@typescript-eslint/triple-slash-reference": "error",
"@typescript-eslint/type-annotation-spacing": "error",
"arrow-body-style": "error",
"arrow-parens": ["error", "always"],
"camelcase": "off",
"comma-dangle": "off",
"complexity": "off",
"constructor-super": "error",
"dot-notation": "error",
"eol-last": "error",
"eqeqeq": ["error", "allow-null"],
"guard-for-in": "error",
"id-blacklist": ["error", "any", "number", "string", "boolean", "undefined"],
"id-match": "error",
"max-len": ["error", { "code": 140 }],
"new-parens": "error",
"no-array-constructor": "error",
"no-bitwise": "error",
"no-caller": "error",
"no-cond-assign": "error",
"no-console": ["error", { "allow": ["debug", "info", "time", "timeEnd", "trace"] }],
"no-debugger": "error",
"no-empty": "off",
"no-empty-function": "error",
"no-eval": "error",
"no-extra-semi": "off",
"no-fallthrough": "error",
"no-invalid-this": "error",
"no-irregular-whitespace": "error",
"no-magic-numbers": "off",
"no-misleading-character-class": "error",
"no-multi-spaces": "error",
"no-multi-str": "error",
"no-new-wrappers": "error",
"no-redeclare": "error",
"no-sequences": "error",
"no-shadow-restricted-names": "error",
"no-sparse-arrays": "error",
"no-template-curly-in-string": "error",
"no-throw-literal": "error",
"no-trailing-spaces": "error",
"no-undef-init": "error",
"no-underscore-dangle": "off",
"no-unsafe-finally": "error",
"no-unused-labels": "error",
"no-var": "error",
"object-curly-spacing": ["error", "always"],
"object-shorthand": "error",
"prefer-arrow-callback": "error",
"prefer-const": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
"prefer-template": "error",
"quote-props": "error",
"radix": "error",
"space-before-function-paren": "off",
"spaced-comment": "error",
"use-isnan": "error",
"valid-typeof": "error"
}
}
1 change: 1 addition & 0 deletions chatapi/env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OPENAI_API_KEY=
45 changes: 45 additions & 0 deletions chatapi/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "chatapi",
"version": "0.0.7",
"description": "A proxy service to make API calls to GPT and other LLMs",
"main": "index.ts",
"scripts": {
"start": "ts-node src/index.ts",
"dev": "nodemon --exec ts-node src/index.ts",
"lint": "eslint . --ext .ts",
"lint-fix": "eslint . --ext .ts --fix"
},
"repository": {
"type": "git",
"url": "git+https://github.com/open-learning-exchange/planet.git"
},
"keywords": [
"GPT",
"chat",
"LLM",
"API",
"proxy"
],
"author": "Open Learning Exchange",
"license": "AGPL-3.0",
"bugs": {
"url": "https://github.com/open-learning-exchange/planet/issues"
},
"homepage": "https://github.com/open-learning-exchange/planet#readme",
"dependencies": {
"@types/express": "^4.17.17",
"express": "^4.18.2",
"nano": "^10.1.2",
"openai": "^3.3.0"
},
"devDependencies": {
"@types/node": "^20.3.1",
"@typescript-eslint/eslint-plugin": "^5.60.0",
"@typescript-eslint/parser": "^5.60.0",
"dotenv": "^16.1.4",
"eslint": "^8.43.0",
"nodemon": "^2.0.22",
"ts-node": "^10.9.1",
"typescript": "^5.1.3"
}
}
41 changes: 41 additions & 0 deletions chatapi/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import express from 'express';
import { chatWithGpt } from './services/gpt-prompt.service';
import dotenv from 'dotenv';

dotenv.config();

const app = express();

// Parse JSON bodies (as sent by API clients)
app.use(express.json());

app.get('/', (req: any, res: any) => {
res.status(200).json({
'status': 'Success',
'message': 'OLE Chat API Service',
});
});

app.post('/', async (req: any, res: any) => {
try {
const userInput = req.body.content;

if (userInput && typeof userInput === 'string') {
const response = await chatWithGpt(userInput);
res.status(200).json({
'status': 'Success',
'chat': response?.completionText,
'history': response?.history,
'couchDBResponse': response?.couchSaveResponse
});
} else {
res.status(400).json({ 'error': 'Bad Request', 'message': 'The "content" field must be a non-empty string.' });
}
} catch (error: any) {
res.status(500).json({ 'error': 'Internal Server Error', 'message': error.message });
}
});

const port = process.env.SERVE_PORT || 5000;

app.listen(port, () => console.log(`Server running on port ${port}`)); // eslint-disable-line no-console
4 changes: 4 additions & 0 deletions chatapi/src/models/chat-item.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface ChatItem {
query: string;
response: string;
}
4 changes: 4 additions & 0 deletions chatapi/src/models/chat-message.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface ChatMessage {
role: 'user' | 'assistant';
content: string;
}
5 changes: 5 additions & 0 deletions chatapi/src/models/database-actions.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { DocumentInsertResponse } from 'nano';

export interface DatabaseActions {
insert(data: any): Promise<DocumentInsertResponse>;
}
Loading

0 comments on commit fdf6213

Please sign in to comment.