Skip to content

Commit

Permalink
formbricks guide
Browse files Browse the repository at this point in the history
  • Loading branch information
luona-dev committed Dec 3, 2023
1 parent abce84b commit 44316c8
Show file tree
Hide file tree
Showing 6 changed files with 583 additions and 0 deletions.
37 changes: 37 additions & 0 deletions assets/guides/formbricks-in-gpts/example_surveys_instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
You adhere to the following commands:

/feedback [feedback] - Check if the [feedback] is an english sentence and not just nonesense. If so, call the app.formbricks.com API with the clpmo....zmqf operation and the following payload:
{
"surveyId": "clpmo...ijqz",
"finished": false,
"ttc": {
"k4fh8...nidq": 1
},
"data": {
"k4fh8...nidq": {feedback}
}
}

/vote [choice1, ...] - For each choice, check if it is one of the following choices "[latest]Vue.js GPT ", "[latest] FastAPI GPT", "[latest] TailwindCSS GPT", "All of them ❤️" or very close to it. If not, ask the user for clarification. If you can assign each choice to one of the valid choices, call the app.formbricks.com API with the clpmo....zmqf operation and the following payload:
{
"surveyId": "clpmo...ijqz",
"finished": false,
"ttc": {
"o0ynm...kob9": 1
},
"data": {
"o0ynm...kob9": {choices}
}
}

/rate [rating] - Check if the [rating] is a number between 1 and 5. If so, call the app.formbricks.com API with the clpmo....zmqf operation and the following payload:
{
"surveyId": "clpmo...ijqz",
"finished": false,
"ttc": {
"iu1kr...fzrm": 1
},
"data": {
"iu1kr...fzrm": {rating}
}
}
87 changes: 87 additions & 0 deletions assets/guides/formbricks-in-gpts/example_surveys_oapi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
"openapi": "3.1.0",
"info": {
"title": "Formbricks API",
"version": "v1.0.0"
},
"servers": [
{
"url": "https://app.formbricks.com/api/v1"
}
],
"paths": {
"/client/clpmo...zmqf/responses": {
"post": {
"operationId": "clpmo...zmqf",
"x-openai-isConsequential": false,
"requestBody": {
"type": "object",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/FormbricksResponse"
}
}
}
},
"responses": {
"200": {
"description": "OK"
}
}
}
}
},
"components": {
"schemas": {
"FormbricksResponse": {
"type": "object",
"properties": {
"surveyId": {
"type": "string"
},
"finished": {
"type": "boolean"
},
"ttc": {
"type": "object",
"properties": {
"k4fh8...nidq": {
"type": "number"
},
"o0ynm...kob9": {
"type": "number"
},
"iu1k3...fzrm": {
"type": "number"
}
}
},
"data": {
"type": "object",
"properties": {
"k4fh8...nidq": {
"type": "string"
},
"o0ynm...kob9": {
"type": "array",
"items": {
"type": "string"
}
},
"iu1k3...fzrm": {
"type": "number"
}
}
}
},
"required": [
"surveyId",
"ttc",
"finished",
"data"
]
}
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
234 changes: 234 additions & 0 deletions assets/guides/formbricks-in-gpts/formbricks_to_gpt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
import http.client
import json


def get_surveys(host: str, api_key: str) -> None:
"""
Get all surveys from the Formbricks API and save them to surveys.json
"""
surveys = []
try:
conn = http.client.HTTPSConnection(host, timeout=10)
headers = {
'x-api-key': api_key
}
conn.request("GET", "/api/v1/management/surveys", headers=headers)
res = conn.getresponse()
data = res.read()
data = json.loads(data.decode("utf-8"))
finally:
conn.close()
json.dump(data, open("surveys.json", "w"), indent=4)


def compose_openapi(surveys: dict, server: str) -> None:
"""
Read surveys.json and create an OpenAI Actions compatibly OpenAPI 3.0 JSON file
"""
added_env_ids = []
paths = {}
for survey in surveys["data"]:
if survey["environmentId"] not in added_env_ids:
path = f"/client/{survey['environmentId']}/responses"
paths[path] = {
"post": {
"operationId": survey['environmentId'],
"x-openai-isConsequential": False,
"requestBody": {
"type": "object",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/FormbricksResponse"
}
}
}
},
"responses": {
"200": {
"description": "OK"
}
}
}
}
added_env_ids.append(survey["environmentId"])
for question in survey["questions"]:
schema_base["components"]["schemas"]["FormbricksResponse"]["properties"]["ttc"]["properties"][question["id"]] = {"type": "number"}
if question["type"] == "openText":
schema_base["components"]["schemas"]["FormbricksResponse"]["properties"]["data"]["properties"][question["id"]] = {"type": "string"}
elif question["type"] == "rating":
schema_base["components"]["schemas"]["FormbricksResponse"]["properties"]["data"]["properties"][question["id"]] = {"type": "number"}
elif question["type"] == "multipleChoiceMulti":
schema_base["components"]["schemas"]["FormbricksResponse"]["properties"]["data"]["properties"][question["id"]] = {"type": "array", "items": {"type": "string"}}
else:
raise Exception(f"\n\nThe formbricks question type: \"{question['type']}\" is not covered by this script.\nIt should be super easy to add though.\nJust check what type/object the Formbricks API expects for \"{question['type']}\" and add a check to the 'compose_instructions' function. Then add an example command to 'question_type_to_command\nPlease create a PR to https://github.com/luona-dev/latestGPTs if you implement it.")
schema_base["paths"] = paths
schema_base["servers"] = [{"url": f"https://{host}/api/v1"}]
json.dump(schema_base, open("surveys_oapi.json", "w"), indent=4)
print("Saved OpenAPI scheme for Actions to surveys_oapi.json")


def compose_instructions(surveys: dict):
"""Composes the instructions for your GPT so it can handle the Formbricks endpoints"""
if len(surveys["data"]) > 1:
print("WARNING: You have more than one survey. This scripts logic to compose instructions is very limited and works on a per question type basis. You may take this as a starting point but will have to adjust it to your needs.")
commands = []
added_question_types = []
for survey in surveys["data"]:
for question in survey["questions"]:
if question["type"] not in added_question_types:
added_question_types.append(question["type"])
if question["type"] == "openText":
commands.append(question_type_to_command[question["type"]].format(host="app.formbricks.com",
operation_id=survey["environmentId"],
survey_id=survey["id"],
question_id=question["id"]))
elif question["type"] == "rating":
commands.append(question_type_to_command[question["type"]].format(host="app.formbricks.com",
operation_id=survey["environmentId"],
survey_id=survey["id"],
question_id=question["id"],
range=question["range"]))
elif question["type"] == "multipleChoiceMulti":
choices = ", ".join([f"\"{choice['label']}\"" for choice in question["choices"]])
commands.append(question_type_to_command[question["type"]].format(host="app.formbricks.com",
operation_id=survey["environmentId"],
survey_id=survey["id"],
question_id=question["id"],
choices=choices))
else:
raise Exception(f"\n\nThe formbricks question type: \"{question['type']}\" is not covered by this script.\nIt should be super easy to add though.\nJust check what type/object the Formbricks API expects for \"{question['type']}\" and add a check to the 'compose_instructions' function. Then add an example command to 'question_type_to_command\nPlease create a PR to https://github.com/luona-dev/latestGPTs if you implement it.")
else:
print(f"WARNING: You have more than one question of type \"{question['type']}\". This scripts logic to compose instructions is very limited and works on a per question type basis. You may take this as a starting point but will have to adjust it to your needs.")
with open("surveys_instructions.md", "w") as f:
f.write(command_base)
for command in commands:
f.write(command)
print("Saved instructions to surveys_instructions.md")

schema_base = {
"openapi": "3.1.0",
"info": {
"title": "Formbricks API",
"version": "v1.0.0",
},
"servers": [],
"paths": {},
"components": {
"schemas": {
"FormbricksResponse": {
"type": "object",
"properties": {
"surveyId": {
"type": "string"
},
"finished": {
"type": "boolean"
},
"ttc": {
"type": "object",
"properties": {}
},
"data": {
"type": "object",
"properties": {}
}
},
"required": ["surveyId", "ttc", "finished", "data"]
}
}
}
}


command_base = "You adhere to the following commands:\n"


question_type_to_command = dict()

question_type_to_command["openText"] = """
/feedback [feedback] - Check if the [feedback] is an english sentence and not just nonesense. If so, call the {host} API with the {operation_id} operation and the following payload:
{{
"surveyId": "{survey_id}",
"finished": false,
"ttc": {{
"{question_id}": 1
}},
"data": {{
"{question_id}": {{feedback}}
}}
}}
"""

question_type_to_command["multipleChoiceMulti"] = """
/vote [choice1, ...] - For each choice, check if it is one of the following choices {choices} or very close to it. If not, ask the user for clarification. If you can assign each choice to one of the valid choices, call the {host} API with the {operation_id} operation and the following payload:
{{
"surveyId": "{survey_id}",
"finished": false,
"ttc": {{
"{question_id}": 1
}},
"data": {{
"{question_id}": {{choices}}
}}
}}
"""

question_type_to_command["rating"] = """
/rate [rating] - Check if the [rating] is a number between 1 and {range}. If so, call the {host} API with the {operation_id} operation and the following payload:
{{
"surveyId": "{survey_id}",
"finished": false,
"ttc": {{
"{question_id}": 1
}},
"data": {{
"{question_id}": {{rating}}
}}
}}
"""



if __name__ == "__main__":
import sys

command = sys.argv[1]
if command == "--help":
print("Usage: python formbricks_to_gpt.py [COMMAND]")
print("COMMANDS:")
print(" --help: Display this help message")
print(" get [API_KEY] [HOST (default: app.formbricks.com)]: Get all your surveys from the Formbricks API and save them to surveys.json")
print (" oapi [PATH (default surveys.json)] [HOST (default: app.formbricks.com)]: Read surveys.json and OpenAI Actions compatibly OpenAPI 3.0 JSON file")
print(" instructions [PATH (default surveys.json)]: Read surveys.json and create example instructions for your GPT so it can handle the Formbricks endpoints")
exit(0)
if command == "get":
api_key = sys.argv[2]
if not api_key:
raise Exception("Please provide an API key as the first argument")
if api_key == "--help":
print("Usage: python formbricks_to_gpt.py get [API_KEY] [HOST (default: app.formbricks.com)]")
print("API_KEY: Your Formbricks Management API key")
print("HOST: The host to connect to. Default is app.formbricks.com")
exit(0)
host = sys.argv[3] if len(sys.argv) > 3 else "app.formbricks.com"
get_surveys(host=host, api_key=api_key)
print("Saved surveys to surveys.json\nRemeber to delete your Management API key and/or remove the last command from your shell history.")
if command == "oapi":
path = sys.argv[2] if len(sys.argv) > 2 else "surveys.json"
if path == "--help":
print("Usage: python formbricks_to_gpt.py oapi [PATH (default surveys.json)] [HOST (default: app.formbricks.com)]")
print("PATH: The path to the surveys.json file. Default is surveys.json")
print("HOST: The host to use for the required \"servers\" field in the OpenAPI scheme. Default is app.formbricks.com")
exit(0)
host = sys.argv[3] if len(sys.argv) > 3 else "app.formbricks.com"
surveys = json.load(open(path))
compose_openapi(surveys, server=host)
if command == "instructions":
path = sys.argv[2] if len(sys.argv) > 2 else "surveys.json"
if path == "--help":
print("Usage: python formbricks_to_gpt.py instructions [PATH (default surveys.json)]")
print("PATH: The path to the surveys.json file. Default is surveys.json")
exit(0)
surveys = json.load(open(path))
compose_instructions(surveys)
Loading

0 comments on commit 44316c8

Please sign in to comment.