学习如何构建一个带有服务级认证的简单待办事项插件。
首先,查看服务级认证页面,然后定义一个 ai-plugin.json 文件,其中包含以下字段:
```json
{
"schema_version": "v1",
"name_for_human": "待办事项列表(服务认证)",
"name_for_model": "todo",
"description_for_human": "管理您的待办事项列表。您可以添加、删除和查看您的待办事项。",
"description_for_model": "用于管理待办事项列表的插件,您可以添加、删除和查看您的待办事项。",
"auth": {
"type": "service_http",
"authorization_type": "bearer",
"verification_tokens": {
"openai": "请用在ChatGPT用户界面中生成的验证令牌替换此字符串"
}
},
"api": {
"type": "openapi",
"url": "https://example.com/openapi.yaml"
},
"logo_url": "https://example.com/logo.png",
"contact_email": "support@example.com",
"legal_info_url": "https://example.com/legal"
}
```
请注意,服务级认证插件需要验证令牌。在您设置服务访问令牌后,该令牌是在ChatGPT web UI中的插件安装过程中生成的。
您还需要将 "example.com" 更新为您的远程服务器的名称。
接下来,我们可以定义API端点,为特定用户创建、删除和获取待办事项列表。端点还检查用户是否已认证。
```python
import json
import quart
import quart_cors
from quart import requestapp = quart_cors.cors(quart.Quart(__name__))
# 此密钥可以是任何内容,尽管您可能希望随机生成一个序列。
_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()
```
最后,我们需要设置并定义一个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: OKcomponents:
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
评论