Created
June 21, 2023 09:59
-
-
Save ryo-rm/55bd812f7858f3e871b7bf1ecda414d7 to your computer and use it in GitHub Desktop.
ESMで動作確認用の画面を作るやつ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from flask import ( | |
Flask, | |
request, | |
) | |
app = Flask(__name__) | |
@app.route("/chat", methods=["POST"]) | |
def chat_post(): | |
args = request.json | |
question = args["question"] | |
id = args["id"] | |
# 適当な回答を返す | |
return {"id": id, "message": f"{question}って何?"} | |
@app.route("/chat", methods=["GET"]) | |
def chat_get(): | |
return """ | |
<html> | |
<head> | |
<title>Chat</title> | |
</head> | |
<body> | |
<div id="app"></div> | |
<script type="module"> | |
import { | |
html, | |
render, | |
useState, | |
useReducer, | |
} from "https://esm.sh/htm/preact/standalone"; | |
function App() { | |
const [state, dispatch] = useReducer( | |
(state, action) => { | |
switch (action.type) { | |
case "INPUT_QUESTION": { | |
return { | |
...state, | |
question: action.payload.question, | |
}; | |
} | |
case "SUCCESS": { | |
const next = state.messages.slice(0, -1); | |
return { | |
...state, | |
id: action.payload.id, | |
question: "", | |
messages: [ | |
...next, | |
{ | |
content: action.payload.message, | |
user: "system", | |
} | |
], | |
submitting: false, | |
error: null, | |
}; | |
} | |
case "SUBMIT": { | |
return { | |
...state, | |
messages: [ | |
...state.messages, | |
{ | |
content: action.payload.question, | |
user: "user", | |
}, | |
{ | |
content: "質問を送信しています...", | |
user: "system", | |
} | |
], | |
submitting: true, | |
error: null, | |
}; | |
} | |
case "ERROR": { | |
const next = state.messages.slice(0, -1); | |
return { | |
...state, | |
submitting: false, | |
error: action.payload.error, | |
messages: [ | |
...next, | |
{ | |
content: action.payload.error, | |
user: "system", | |
} | |
], | |
}; | |
} | |
default: | |
return state; | |
} | |
}, | |
{ | |
id: "", | |
messages: [], | |
submitting: false, | |
question: "", | |
error: null, | |
} | |
); | |
const sendQuestion = async () => { | |
if (state.submitting) { | |
return; | |
} | |
if (!state.question) { | |
dispatch({ | |
type: "ERROR", | |
payload: { | |
error: "質問を入力してください", | |
}, | |
}); | |
return; | |
} | |
dispatch({ | |
type: "SUBMIT", | |
payload: { | |
question: state.question, | |
}, | |
}); | |
const response = await fetch("/chat", { | |
method: "POST", | |
headers: { | |
"Content-Type": "application/json", | |
}, | |
body: JSON.stringify({ | |
question: state.question, | |
id: state.id, | |
}), | |
}); | |
if (response.ok) { | |
const json = await response.json(); | |
dispatch({ | |
type: "SUCCESS", | |
payload: { | |
id: json.id, | |
message: json.message, | |
}, | |
}); | |
} else { | |
dispatch({ | |
type: "ERROR", | |
payload: { | |
error: response.statusText, | |
}, | |
}); | |
} | |
}; | |
return html` | |
<div> | |
<div>${state.error}</div> | |
<div>${state.messages.map((message) => html`<div | |
style=${{ | |
whiteSpace: "pre-wrap", | |
wordBreak: "break-all", | |
maxWidth: "70%", | |
textAlign: message.user === "system" ? "left" : "right", | |
marginLeft: message.user === "system" ? "0" : "auto", | |
marginRight: message.user === "system" ? "auto" : "0", | |
}} | |
> | |
${message.content}</div> | |
`)}</div> | |
<div> | |
<input | |
type="text" | |
value=${state.question} | |
onInput=${(e) => dispatch({ type: "INPUT_QUESTION", payload: { question: e.target.value } })} | |
/> | |
<button onClick=${sendQuestion} disabled=${state.submitting}>${state.submitting ? "送信中" : "送信"}</button> | |
</div> | |
</div> | |
`; | |
} | |
render(html`<${App} name="App" />`, document.getElementById("app")); | |
</script> | |
</body> | |
</html> | |
""" | |
if __name__ == "__main__": | |
print("start") | |
app.run(debug=True, host="0.0.0.0", port=3000) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment