To get started building, we are making available a set of simple plugins that cover different authentication schemas and use cases. From our simple no authentication todo list plugin to the more powerful retrieval plugin, these examples provide a glimpse into what we hope to make possible with plugins.
building을 시작하기 위해 다양한 인증 스키마 및 사용 사례를 다루는 간단한 플러그인 세트를 제공하고 있습니다. 단순한 no authentication todo list 플러그인에서 더 강력한 검색 플러그인에 이르기까지 이 예제는 플러그인으로 가능하게 하고자 하는 것을 엿볼 수 있는 기회를 제공합니다.
During development, you can run the plugin locally on your computer or through a cloud development environment like GitHub Codespaces, Replit, or CodeSandbox.
개발 중에 플러그인을 컴퓨터에서 로컬로 실행하거나 GitHub Codespaces, Replit 또는 CodeSandbox와 같은 클라우드 개발 환경을 통해 실행할 수 있습니다.
Learn how to build a simple todo list plugin with no auth
To start, define an ai-plugin.json file with the following fields:
시작하려면 다음 필드를 사용하여 ai-plugin.json 파일을 정의합니다.
{
"schema_version": "v1",
"name_for_human": "TODO Plugin (no auth)",
"name_for_model": "todo",
"description_for_human": "Plugin for managing a TODO list, you can add, remove and view your TODOs.",
"description_for_model": "Plugin for managing a TODO list, you can add, remove and view your TODOs.",
"auth": {
"type": "none"
},
"api": {
"type": "openapi",
"url": "PLUGIN_HOSTNAME/openapi.yaml",
"is_user_authenticated": false
},
"logo_url": "PLUGIN_HOSTNAME/logo.png",
"contact_email": "support@example.com",
"legal_info_url": "https://example.com/legal"
}
Note the PLUGIN_HOSTNAME should be the actual hostname of your plugin server.
PLUGIN_HOSTNAME은 플러그인 서버의 실제 호스트 이름이어야 합니다.
Next, we can define the API endpoints to create, delete, and fetch todo list items for a specific user.
다음으로 특정 사용자에 대한 할 일 목록 항목을 생성, 삭제 및 가져오기 위해 API 끝점을 정의할 수 있습니다.
import json
import quart
import quart_cors
from quart import request
# Note: Setting CORS to allow chat.openapi.com is required for ChatGPT to access your plugin
app = quart_cors.cors(quart.Quart(__name__), allow_origin="https://chat.openai.com")
_TODOS = {}
@app.post("/todos/<string:username>")
async def add_todo(username):
request = await quart.request.get_json(force=True)
if username not in _TODOS:
_TODOS[username] = []
_TODOS[username].append(request["todo"])
return quart.Response(response='OK', status=200)
@app.get("/todos/<string:username>")
async def get_todos(username):
return quart.Response(response=json.dumps(_TODOS.get(username, [])), status=200)
@app.delete("/todos/<string:username>")
async def delete_todo(username):
request = await quart.request.get_json(force=True)
todo_idx = request["todo_idx"]
if 0 <= todo_idx < len(_TODOS[username]):
_TODOS[username].pop(todo_idx)
return quart.Response(response='OK', status=200)
@app.get("/logo.png")
async def plugin_logo():
filename = 'logo.png'
return await quart.send_file(filename, mimetype='image/png')
@app.get("/.well-known/ai-plugin.json")
async def plugin_manifest():
host = request.headers['Host']
with open("ai-plugin.json") as f:
text = f.read()
# This is a trick we do to populate the PLUGIN_HOSTNAME constant in the manifest
text = text.replace("PLUGIN_HOSTNAME", f"https://{host}")
return quart.Response(text, mimetype="text/json")
@app.get("/openapi.yaml")
async def openapi_spec():
host = request.headers['Host']
with open("openapi.yaml") as f:
text = f.read()
# This is a trick we do to populate the PLUGIN_HOSTNAME constant in the OpenAPI spec
text = text.replace("PLUGIN_HOSTNAME", f"https://{host}")
return quart.Response(text, mimetype="text/yaml")
def main():
app.run(debug=True, host="0.0.0.0", port=5002)
if __name__ == "__main__":
main()
Last, we need to set up and define a OpenAPI specification to match the endpoints defined on our local or remote server. You do not need to expose the full functionality of your API via the specification and can instead choose to let ChatGPT have access to only certain functionality.
마지막으로 로컬 또는 원격 서버에 정의된 엔드포인트와 일치하도록 OpenAPI 사양을 설정하고 정의해야 합니다. 사양을 통해 API의 전체 기능을 노출할 필요는 없으며 대신 ChatGPT가 특정 기능에만 액세스하도록 선택할 수 있습니다.
There are also many tools that will automatically turn your server definition code into an OpenAPI specification so you don’t need to do it manually. In the case of the Python code above, the OpenAPI specification will look like:
서버 정의 코드를 OpenAPI 사양으로 자동 변환하여 수동으로 수행할 필요가 없도록 하는 많은 도구도 있습니다. 위 Python 코드의 경우 OpenAPI 사양은 다음과 같습니다.
openapi: 3.0.1
info:
title: TODO Plugin
description: A plugin that allows the user to create and manage a TODO list using ChatGPT. If you do not know the user's username, ask them first before making queries to the plugin. Otherwise, use the username "global".
version: 'v1'
servers:
- url: PLUGIN_HOSTNAME
paths:
/todos/{username}:
get:
operationId: getTodos
summary: Get the list of todos
parameters:
- in: path
name: username
schema:
type: string
required: true
description: The name of the user.
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/getTodosResponse'
post:
operationId: addTodo
summary: Add a todo to the list
parameters:
- in: path
name: username
schema:
type: string
required: true
description: The name of the user.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/addTodoRequest'
responses:
"200":
description: OK
delete:
operationId: deleteTodo
summary: Delete a todo from the list
parameters:
- in: path
name: username
schema:
type: string
required: true
description: The name of the user.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/deleteTodoRequest'
responses:
"200":
description: OK
components:
schemas:
getTodosResponse:
type: object
properties:
todos:
type: array
items:
type: string
description: The list of todos.
addTodoRequest:
type: object
required:
- todo
properties:
todo:
type: string
description: The todo to add to the list.
required: true
deleteTodoRequest:
type: object
required:
- todo_idx
properties:
todo_idx:
type: integer
description: The index of the todo to delete.
required: true
Learn how to build a simple todo list plugin with service level auth
To start, define an ai-plugin.json file with the following fields:
시작하려면 다음 필드를 사용하여 ai-plugin.json 파일을 정의합니다.
{
"schema_version": "v1",
"name_for_human": "TODO Plugin (service level auth)",
"name_for_model": "todo",
"description_for_human": "Plugin for managing a TODO list, you can add, remove and view your TODOs.",
"description_for_model": "Plugin for managing a TODO list, you can add, remove and view your TODOs.",
"auth": {
"type": "service_http",
"authorization_type": "bearer",
"verification_tokens": {
"openai": "758e9ef7984b415688972d749f8aa58e"
}
},
"api": {
"type": "openapi",
"url": "https://example.com/openapi.yaml",
"is_user_authenticated": false
},
"logo_url": "https://example.com/logo.png",
"contact_email": "support@example.com",
"legal_info_url": "https://example.com/legal"
}
Notice that the verification token is required for service level authentication plugins. The token is generated during the plugin installation process in the ChatGPT web UI.
서비스 수준 인증 플러그인에는 확인 토큰이 필요합니다. 토큰은 ChatGPT 웹 UI에서 플러그인 설치 프로세스 중에 생성됩니다.
Next, we can define the API endpoints to create, delete, and fetch todo list items for a specific user. The endpoints also check that the user is authenticated.
다음으로 특정 사용자에 대한 할 일 목록 항목을 생성, 삭제 및 가져오기 위해 API 끝점을 정의할 수 있습니다. 끝점은 또한 사용자가 인증되었는지 확인합니다.
import json
import quart
import quart_cors
from quart import request
# Note: Setting CORS to allow chat.openapi.com is required for ChatGPT to access your plugin
app = quart_cors.cors(quart.Quart(__name__), allow_origin="https://chat.openai.com")
_SERVICE_AUTH_KEY = "REPLACE_ME"
_TODOS = {}
def assert_auth_header(req):
assert req.headers.get(
"Authorization", None) == f"Bearer {_SERVICE_AUTH_KEY}"
@app.post("/todos/<string:username>")
async def add_todo(username):
assert_auth_header(quart.request)
request = await quart.request.get_json(force=True)
if username not in _TODOS:
_TODOS[username] = []
_TODOS[username].append(request["todo"])
return quart.Response(response='OK', status=200)
@app.get("/todos/<string:username>")
async def get_todos(username):
assert_auth_header(quart.request)
return quart.Response(response=json.dumps(_TODOS.get(username, [])), status=200)
@app.delete("/todos/<string:username>")
async def delete_todo(username):
assert_auth_header(quart.request)
request = await quart.request.get_json(force=True)
todo_idx = request["todo_idx"]
if 0 <= todo_idx < len(_TODOS[username]):
_TODOS[username].pop(todo_idx)
return quart.Response(response='OK', status=200)
@app.get("/logo.png")
async def plugin_logo():
filename = 'logo.png'
return await quart.send_file(filename, mimetype='image/png')
@app.get("/.well-known/ai-plugin.json")
async def plugin_manifest():
host = request.headers['Host']
with open("ai-plugin.json") as f:
text = f.read()
return quart.Response(text, mimetype="text/json")
@app.get("/openapi.yaml")
async def openapi_spec():
host = request.headers['Host']
with open("openapi.yaml") as f:
text = f.read()
return quart.Response(text, mimetype="text/yaml")
def main():
app.run(debug=True, host="0.0.0.0", port=5002)
if __name__ == "__main__":
main()
Last, we need to set up and define a OpenAPI specification to match the endpoints defined on our local or remote server. In general, the OpenAPI specification would look the same regardless of the authentication method. Using an automatic OpenAPI generator will reduce the chance of errors when creating your OpenAPI specification so it is worth exploring the options.
마지막으로 로컬 또는 원격 서버에 정의된 엔드포인트와 일치하도록 OpenAPI 사양을 설정하고 정의해야 합니다. 일반적으로 OpenAPI 사양은 인증 방법에 관계없이 동일하게 보입니다. 자동 OpenAPI 생성기를 사용하면 OpenAPI 사양을 생성할 때 오류 가능성이 줄어들므로 옵션을 탐색해 볼 가치가 있습니다.
openapi: 3.0.1
info:
title: TODO Plugin
description: A plugin that allows the user to create and manage a TODO list using ChatGPT. If you do not know the user's username, ask them first before making queries to the plugin. Otherwise, use the username "global".
version: 'v1'
servers:
- url: https://example.com
paths:
/todos/{username}:
get:
operationId: getTodos
summary: Get the list of todos
parameters:
- in: path
name: username
schema:
type: string
required: true
description: The name of the user.
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/getTodosResponse'
post:
operationId: addTodo
summary: Add a todo to the list
parameters:
- in: path
name: username
schema:
type: string
required: true
description: The name of the user.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/addTodoRequest'
responses:
"200":
description: OK
delete:
operationId: deleteTodo
summary: Delete a todo from the list
parameters:
- in: path
name: username
schema:
type: string
required: true
description: The name of the user.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/deleteTodoRequest'
responses:
"200":
description: OK
components:
schemas:
getTodosResponse:
type: object
properties:
todos:
type: array
items:
type: string
description: The list of todos.
addTodoRequest:
type: object
required:
- todo
properties:
todo:
type: string
description: The todo to add to the list.
required: true
deleteTodoRequest:
type: object
required:
- todo_idx
properties:
todo_idx:
type: integer
description: The index of the todo to delete.
required: true
Learn how to build a simple sports stats plugin
This plugin is an example of a simple sports stats API. Please keep in mind our domain policy and usage policies when considering what to build.
이 플러그인은 간단한 스포츠 통계 API의 예입니다. 무엇을 구축할지 고려할 때 도메인 정책 및 사용 정책을 염두에 두십시오.
To start, define an ai-plugin.json file with the following fields:
시작하려면 다음 필드를 사용하여 ai-plugin.json 파일을 정의합니다.
{
"schema_version": "v1",
"name_for_human": "Sport Stats",
"name_for_model": "sportStats",
"description_for_human": "Get current and historical stats for sport players and games.",
"description_for_model": "Get current and historical stats for sport players and games. Always display results using markdown tables.",
"auth": {
"type": "none"
},
"api": {
"type": "openapi",
"url": "PLUGIN_HOSTNAME/openapi.yaml",
"is_user_authenticated": false
},
"logo_url": "PLUGIN_HOSTNAME/logo.png",
"contact_email": "support@example.com",
"legal_info_url": "https://example.com/legal"
}
Note the PLUGIN_HOSTNAME should be the actual hostname of your plugin server.
PLUGIN_HOSTNAME은 플러그인 서버의 실제 호스트 이름이어야 합니다.
Next, we define a mock API for a simple sports service plugin.
다음으로 간단한 스포츠 서비스 플러그인을 위한 모의 API를 정의합니다.
import json
import requests
import urllib.parse
import quart
import quart_cors
from quart import request
# Note: Setting CORS to allow chat.openapi.com is required for ChatGPT to access your plugin
app = quart_cors.cors(quart.Quart(__name__), allow_origin="https://chat.openai.com")
HOST_URL = "https://example.com"
@app.get("/players")
async def get_players():
query = request.args.get("query")
res = requests.get(
f"{HOST_URL}/api/v1/players?search={query}&page=0&per_page=100")
body = res.json()
return quart.Response(response=json.dumps(body), status=200)
@app.get("/teams")
async def get_teams():
res = requests.get(
"{HOST_URL}/api/v1/teams?page=0&per_page=100")
body = res.json()
return quart.Response(response=json.dumps(body), status=200)
@app.get("/games")
async def get_games():
query_params = [("page", "0")]
limit = request.args.get("limit")
query_params.append(("per_page", limit or "100"))
start_date = request.args.get("start_date")
if start_date:
query_params.append(("start_date", start_date))
end_date = request.args.get("end_date")
if end_date:
query_params.append(("end_date", end_date))
seasons = request.args.getlist("seasons")
for season in seasons:
query_params.append(("seasons[]", str(season)))
team_ids = request.args.getlist("team_ids")
for team_id in team_ids:
query_params.append(("team_ids[]", str(team_id)))
res = requests.get(
f"{HOST_URL}/api/v1/games?{urllib.parse.urlencode(query_params)}")
body = res.json()
return quart.Response(response=json.dumps(body), status=200)
@app.get("/stats")
async def get_stats():
query_params = [("page", "0")]
limit = request.args.get("limit")
query_params.append(("per_page", limit or "100"))
start_date = request.args.get("start_date")
if start_date:
query_params.append(("start_date", start_date))
end_date = request.args.get("end_date")
if end_date:
query_params.append(("end_date", end_date))
player_ids = request.args.getlist("player_ids")
for player_id in player_ids:
query_params.append(("player_ids[]", str(player_id)))
game_ids = request.args.getlist("game_ids")
for game_id in game_ids:
query_params.append(("game_ids[]", str(game_id)))
res = requests.get(
f"{HOST_URL}/api/v1/stats?{urllib.parse.urlencode(query_params)}")
body = res.json()
return quart.Response(response=json.dumps(body), status=200)
@app.get("/season_averages")
async def get_season_averages():
query_params = []
season = request.args.get("season")
if season:
query_params.append(("season", str(season)))
player_ids = request.args.getlist("player_ids")
for player_id in player_ids:
query_params.append(("player_ids[]", str(player_id)))
res = requests.get(
f"{HOST_URL}/api/v1/season_averages?{urllib.parse.urlencode(query_params)}")
body = res.json()
return quart.Response(response=json.dumps(body), status=200)
@app.get("/logo.png")
async def plugin_logo():
filename = 'logo.png'
return await quart.send_file(filename, mimetype='image/png')
@app.get("/.well-known/ai-plugin.json")
async def plugin_manifest():
host = request.headers['Host']
with open("ai-plugin.json") as f:
text = f.read()
# This is a trick we do to populate the PLUGIN_HOSTNAME constant in the manifest
text = text.replace("PLUGIN_HOSTNAME", f"https://{host}")
return quart.Response(text, mimetype="text/json")
@app.get("/openapi.yaml")
async def openapi_spec():
host = request.headers['Host']
with open("openapi.yaml") as f:
text = f.read()
# This is a trick we do to populate the PLUGIN_HOSTNAME constant in the OpenAPI spec
text = text.replace("PLUGIN_HOSTNAME", f"https://{host}")
return quart.Response(text, mimetype="text/yaml")
def main():
app.run(debug=True, host="0.0.0.0", port=5001)
if __name__ == "__main__":
main()
Last, we define our OpenAPI specification:
마지막으로 OpenAPI 사양을 정의합니다.
openapi: 3.0.1
info:
title: Sport Stats
description: Get current and historical stats for sport players and games.
version: 'v1'
servers:
- url: PLUGIN_HOSTNAME
paths:
/players:
get:
operationId: getPlayers
summary: Retrieves all players from all seasons whose names match the query string.
parameters:
- in: query
name: query
schema:
type: string
description: Used to filter players based on their name. For example, ?query=davis will return players that have 'davis' in their first or last name.
responses:
"200":
description: OK
/teams:
get:
operationId: getTeams
summary: Retrieves all teams for the current season.
responses:
"200":
description: OK
/games:
get:
operationId: getGames
summary: Retrieves all games that match the filters specified by the args. Display results using markdown tables.
parameters:
- in: query
name: limit
schema:
type: string
description: The max number of results to return.
- in: query
name: seasons
schema:
type: array
items:
type: string
description: Filter by seasons. Seasons are represented by the year they began. For example, 2018 represents season 2018-2019.
- in: query
name: team_ids
schema:
type: array
items:
type: string
description: Filter by team ids. Team ids can be determined using the getTeams function.
- in: query
name: start_date
schema:
type: string
description: A single date in 'YYYY-MM-DD' format. This is used to select games that occur on or after this date.
- in: query
name: end_date
schema:
type: string
description: A single date in 'YYYY-MM-DD' format. This is used to select games that occur on or before this date.
responses:
"200":
description: OK
/stats:
get:
operationId: getStats
summary: Retrieves stats that match the filters specified by the args. Display results using markdown tables.
parameters:
- in: query
name: limit
schema:
type: string
description: The max number of results to return.
- in: query
name: player_ids
schema:
type: array
items:
type: string
description: Filter by player ids. Player ids can be determined using the getPlayers function.
- in: query
name: game_ids
schema:
type: array
items:
type: string
description: Filter by game ids. Game ids can be determined using the getGames function.
- in: query
name: start_date
schema:
type: string
description: A single date in 'YYYY-MM-DD' format. This is used to select games that occur on or after this date.
- in: query
name: end_date
schema:
type: string
description: A single date in 'YYYY-MM-DD' format. This is used to select games that occur on or before this date.
responses:
"200":
description: OK
/season_averages:
get:
operationId: getSeasonAverages
summary: Retrieves regular season averages for the given players. Display results using markdown tables.
parameters:
- in: query
name: season
schema:
type: string
description: Defaults to the current season. A season is represented by the year it began. For example, 2018 represents season 2018-2019.
- in: query
name: player_ids
schema:
type: array
items:
type: string
description: Filter by player ids. Player ids can be determined using the getPlayers function.
responses:
"200":
description: OK
Learn how to build a semantic search and retrieval plugin
The ChatGPT retrieval plugin is a more fully featured code example. The scope of the plugin is large, so we encourage you to read through the code to see what a more advanced plugin looks like.
ChatGPT 검색 플러그인은 더 완전한 기능을 갖춘 코드 예제입니다. 플러그인의 범위는 넓기 때문에 코드를 자세히 읽고 고급 플러그인이 어떻게 생겼는지 확인하는 것이 좋습니다.
The retrieval plugin includes:
검색 플러그인에는 다음이 포함됩니다.
- Support for multiple vector databases providers
- 여러 벡터 데이터베이스 공급자 지원
- All 4 different authentication methods
- 4가지 인증 방법 모두
- Multiple different API features
- 다양한 API 기능
'Open AI > CHAT PLUGINS' 카테고리의 다른 글
Chat GPT Plugin - Plugin policies (0) | 2023.04.04 |
---|---|
Chat GPT Plugin - Plugins in production (0) | 2023.04.04 |
Chat GPT Plugin - Authentication (0) | 2023.04.04 |
Chat GPT Plugin - Getting started (0) | 2023.03.27 |
Chat GPT Plugin - Introduction - PlugIn flow (0) | 2023.03.25 |