Skip to content
This repository has been archived by the owner on Jan 5, 2025. It is now read-only.

Commit

Permalink
Merge pull request #104 from openchatai/falta/patch-ci
Browse files Browse the repository at this point in the history
Falta/patch ci
  • Loading branch information
faltawy authored Sep 28, 2023
2 parents 1e59641 + 3757280 commit a1cb686
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 215 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/build-widget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ jobs:
with:
node-version: 18

- name: install pnpm
run: npm install -g pnpm

- name: Install dependencies
run: cd copilot-widget/ && pnpm install

Expand Down
3 changes: 2 additions & 1 deletion llm-server/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,5 @@ cython_debug/
# 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/
.idea/
.env.local
95 changes: 8 additions & 87 deletions llm-server/app.py
Original file line number Diff line number Diff line change
@@ -1,108 +1,29 @@
import logging
import requests
import traceback

from flask import Flask, request
from langchain.chains.openai_functions import create_structured_output_chain
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate

from langchain.utilities.openapi import OpenAPISpec
from utils.base import try_to_match_and_call_api_endpoint
from models.models import AiResponseFormat
from flask import Flask, request, jsonify, Response
from routes.workflow.workflow_controller import workflow
from routes.swagger_controller.swagger_api import swagger_workflow
import json
from routes._swagger.controller import _swagger
from typing import Any, Tuple
from prompts.base import api_base_prompt, non_api_base_prompt
from routes.workflow.workflow_service import run_workflow
from routes.workflow.typings.run_workflow_input import WorkflowData
from utils.detect_multiple_intents import hasSingleIntent, hasMultipleIntents
import os
from dotenv import load_dotenv


load_dotenv()
shared_folder = os.getenv("SHARED_FOLDER", "/app/shared_data/")
logging.basicConfig(level=logging.DEBUG)


app = Flask(__name__)

app.register_blueprint(workflow, url_prefix="/workflow")
app.register_blueprint(swagger_workflow, url_prefix="/swagger_api")
app.register_blueprint(_swagger, url_prefix="/swagger_api")
from routes.root_service import handle_request


## TODO: Implement caching for the swagger file content (no need to load it everytime)
@app.route("/handle", methods=["POST", "OPTIONS"])
def handle():
def handle() -> Response:
data = request.get_json()
text = data.get("text")
swagger_url = data.get("swagger_url")
base_prompt = data.get("base_prompt")
headers = data.get("headers", {})
server_base_url = data.get("server_base_url")

if not base_prompt:
return json.dumps({"error": "base_prompt is required"}), 400

if not text:
return json.dumps({"error": "text is required"}), 400

if not swagger_url:
return json.dumps({"error": "swagger_url is required"}), 400

if swagger_url.startswith("https://"):
pass
else:
swagger_url = shared_folder + swagger_url

print(f"swagger_url::{swagger_url}")
try:
if hasMultipleIntents(text):
result = run_workflow(
WorkflowData(text, swagger_url, headers, server_base_url)
)

return result
except Exception as e:
raise e

if swagger_url.startswith("https://"):
response = requests.get(swagger_url)
if response.status_code == 200:
swagger_text = response.text
else:
return json.dumps({"error": "Failed to fetch Swagger content"}), 500
else:
try:
with open(swagger_url, "r") as file:
swagger_text = file.read()
except FileNotFoundError:
return json.dumps({"error": "File not found"}), 404

swagger_spec = OpenAPISpec.from_text(swagger_text)

try:
json_output = try_to_match_and_call_api_endpoint(swagger_spec, text, headers)
response = handle_request(data)
return jsonify(response)
except Exception as e:
logging.error(f"Failed to call or map API endpoint: {str(e)}")
logging.error("Exception traceback:\n" + traceback.format_exc())
json_output = None

llm = ChatOpenAI(model="gpt-3.5-turbo-0613", temperature=0)

if json_output is None:
prompt_msgs = non_api_base_prompt(base_prompt, text)

else:
prompt_msgs = api_base_prompt(base_prompt, text, json_output)

prompt = ChatPromptTemplate(messages=prompt_msgs)
chain = create_structured_output_chain(AiResponseFormat, llm, prompt, verbose=False)
chain_output = chain.run(question=text)

return json.loads(json.dumps(chain_output.dict())), 200
return jsonify({"error": str(e)})


@app.errorhandler(500)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
from flask import Flask, request, jsonify, Blueprint, request, Response
from flask_pymongo import PyMongo

import json, yaml
from bson import ObjectId
import routes._swagger.service as swagger_service

from utils.db import Database
from typing import Any
import requests

db_instance = Database()
mongo = db_instance.get_db()
swagger_workflow = Blueprint("swagger_workflow", __name__)
_swagger = Blueprint("_swagger", __name__)


@swagger_workflow.route("/", methods=["GET"])
def get_swagger_files() -> Response:
@_swagger.route("/b/<id>", methods=["GET"])
def get_swagger_files(id: str) -> Response:
# Get page and page_size query params
page = int(request.args.get("page", 1))
page_size = int(request.args.get("page_size", 10))
Expand All @@ -26,7 +24,7 @@ def get_swagger_files() -> Response:
# Query for paginated docs
files = [
doc.update({"_id": str(doc["_id"])}) or doc
for doc in mongo.swagger_files.find({}, {}).skip(skip).limit(limit)
for doc in mongo.swagger_files.find({"bot_id": id}, {}).skip(skip).limit(limit)
]

# Get total docs count
Expand All @@ -38,56 +36,23 @@ def get_swagger_files() -> Response:
return jsonify(data)


@swagger_workflow.route("/", methods=["POST"])
def add_swagger_file():
if request.content_type == "application/json":
# JSON file
file_content = request.get_json()

elif "multipart/form-data" in request.content_type:
# Uploaded file
file = request.files.get("file")
if file is None:
return jsonify({"error": "File upload is required"}), 400

if file.filename.endswith(".json"):
try:
file_content = json.load(file)
except json.JSONDecodeError as e:
return (
jsonify({"error": "Invalid JSON format in the uploaded file"}),
400,
)

elif file.filename.endswith(".yaml") or file.filename.endswith(".yml"):
try:
file_content = yaml.safe_load(file)
except yaml.YAMLError as e:
return (
jsonify({"error": "Invalid YAML format in the uploaded file"}),
400,
)

else:
return jsonify({"error": "Unsupported content type"}), 400

# Insert into MongoDB
mongo.swagger_files.insert_one(file_content)

return jsonify({"message": "File added successfully"})


@swagger_workflow.route("/<id>", methods=["GET"])
def get_swagger_file(id: str) -> Response:
file = mongo.swagger_files.find_one({"_id": ObjectId(id)})
@_swagger.route("/b/<id>", methods=["POST"])
def add_swagger_file(id) -> Response:
result = swagger_service.add_swagger_file(request, id)
return jsonify(result)


@_swagger.route("/<_id>", methods=["GET"])
def get_swagger_file(_id: str) -> Response:
file = mongo.swagger_files.find_one({"_id": ObjectId(_id)})
if not file:
return jsonify({"message": "Swagger file not found"})

file["_id"] = str(file["_id"])
return jsonify(file)


@swagger_workflow.route("/transform/<_id>", methods=["GET"])
@_swagger.route("/transform/<_id>", methods=["GET"])
def get_transformed_swagger_file(_id: str) -> Response:
swagger_json = mongo.swagger_files.aggregate(
[
Expand Down Expand Up @@ -138,18 +103,18 @@ def get_transformed_swagger_file(_id: str) -> Response:
return jsonify(list(swagger_json))


@swagger_workflow.route("/<id>", methods=["PUT"])
def update_swagger_file(id: str) -> Response:
@_swagger.route("/<_id>", methods=["PUT"])
def update_swagger_file(_id: str) -> Response:
data = request.get_json()
result = mongo.swagger_files.update_one({"_id": ObjectId(id)}, {"$set": data})
result = mongo.swagger_files.update_one({"_id": ObjectId(_id)}, {"$set": data})
if result.modified_count == 1:
return jsonify({"message": "Swagger file updated successfully"})
return jsonify({"message": "Swagger file not found"})


@swagger_workflow.route("/<id>", methods=["DELETE"])
def delete_swagger_file(id: str) -> Response:
result = mongo.swagger_files.delete_one({"_id": ObjectId(id)})
@_swagger.route("/<_id>", methods=["DELETE"])
def delete_swagger_file(_id: str) -> Response:
result = mongo.swagger_files.delete_one({"_id": ObjectId(_id)})
if result.deleted_count == 1:
return jsonify({"message": "Swagger file deleted successfully"})
return jsonify({"message": "Swagger file not found"})
44 changes: 44 additions & 0 deletions llm-server/routes/_swagger/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import json
import yaml

from utils.db import Database

db_instance = Database()
mongo = db_instance.get_db()
from typing import Dict
from flask import Request


def add_swagger_file(request: Request, id: str) -> Dict[str, str]:
if request.content_type == "application/json":
# JSON file
file_content = request.get_json()

elif "multipart/form-data" in request.content_type:
# Uploaded file
file = request.files.get("file")
if file is None:
return {"error": "File upload is required"}

if file.filename and file.filename.endswith(".json"):
try:
file_content = json.load(file)
except json.JSONDecodeError as e:
return {"error": "Invalid JSON format in uploaded file"}

elif file.filename and (
file.filename.endswith(".yaml") or file.filename.endswith(".yml")
):
try:
file_content = yaml.safe_load(file)
except yaml.YAMLError as e:
return {"error": "Invalid YAML format in uploaded file"}

else:
return {"error": "Unsupported content type"}

# Insert into MongoDB
file_content["bot_id"] = id
mongo.swagger_files.insert_one(file_content)

return {"message": "File added successfully"}
Loading

0 comments on commit a1cb686

Please sign in to comment.