qGuide: Request to OpenAI API
Send the Input-string and ApiKey-string to the Responses API via CORS. The responses (the sentence generated by the model) will be displayed in streaming format. Depending on the prompt settings, various support functions to assist task operators can be provided, such as a typo check function, a text rewrite function, and a list of possible reasons for rejection.
Input / Output
- ← STRING (STRING_TEXTFIELD)
q_input - ← STRING (STRING_TEXTFIELD)
q_instruction - ← STRING (STRING_TEXTFIELD)
q_openAiApiKey - →
pre#user_result - →
div#user_status
Code Example
HTML/JavaScript (click to close)
<style>
/* AI呼び出しボタン */
.user_aiBtn {
border: 1px solid #ccc;
background-color: #fff;
padding: 6px 12px;
border-radius: 20px;
cursor: pointer;
font-size: 13px;
transition: all 0.2s ease;
color: #333;
margin-right: 8px; /* ボタン間の余白 */
margin-bottom: 12px;
}
.user_aiBtn:hover {
background-color: #f0f0f0;
border-color: #bbb;
}
.user_aiBtn:disabled {
opacity: 0.5;
cursor: not-allowed;
background-color: #eee;
}
/* 結果表示エリア */
#user_result {
background-color: #f9f9f9;
border: 1px solid #ddd;
border-radius: 4px;
padding: 12px;
min-height: 8em;
white-space: pre-wrap;
word-break: break-word;
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
font-size: 14px;
line-height: 1.6;
color: #222;
margin-top: 4px;
}
/* ステータス表示エリア */
#user_status {
font: 12px/1.4 system-ui, sans-serif;
opacity: 0.75;
margin-top: 8px;
min-height: 1.4em;
}
/* タイピングカーソルのアニメーション */
@keyframes blink {
50% { opacity: 0; }
}
.user_cursor {
display: inline-block;
width: 8px;
height: 1em;
background-color: #333;
margin-left: 2px;
animation: blink 1s step-end infinite;
vertical-align: text-bottom;
}
</style>
<button type="button" class="user_aiBtn">gpt-5-nano</button>
<button type="button" class="user_aiBtn">gpt-5-mini</button>
<button type="button" class="user_aiBtn">gpt-5</button>
<button type="button" class="user_aiBtn">gpt-5-codex</button>
<button type="button" class="user_aiBtn">gpt-4o</button>
<button type="button" class="user_aiBtn">gpt-4o-mini</button>
<pre id="user_result"></pre>
<div id="user_status"></div>
<script>
// --- 全てのボタンにイベントリスナーを設定 ---
const aiButtons = document.querySelectorAll(".user_aiBtn");
aiButtons.forEach(button => {
button.addEventListener("click", async (event) => {
// --- Questetraのデータ項目から値を取得 ---
const strKey = qbpms.form.get("q_openAiApiKey");
const strInstruction = qbpms.form.get("q_instruction") || ""; // 任意
const strInput = qbpms.form.get("q_input");
// --- 必要なHTML要素を取得 ---
const resultElement = document.getElementById("user_result");
const statusElement = document.getElementById("user_status");
// --- クリックされたボタンのテキストをモデル名として取得 ---
const modelName = event.currentTarget.innerText.trim();
// --- 入力チェック ---
if (!strKey || !strInput) {
statusElement.innerText = "APIキーと入力内容は必須です。";
resultElement.innerText = "";
return;
}
// --- UIを処理中状態に更新 ---
aiButtons.forEach(btn => btn.disabled = true);
statusElement.innerText = "AIに接続中...";
resultElement.innerHTML = '<span class="user_cursor"></span>';
const cursor = resultElement.querySelector(".user_cursor");
try {
// --- APIリクエスト(Responses API / SSE ストリーミング) ---
const response = await fetch("https://api.openai.com/v1/responses", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${strKey}`
},
body: JSON.stringify({
model: modelName,
instructions: strInstruction,
input: strInput,
stream: true
})
});
// --- エラーレスポンスの処理 ---
if (!response.ok) {
let msg = `API Error: ${response.status}`;
try {
const err = await response.json();
msg += `\n${JSON.stringify(err, null, 2)}`;
} catch (_) {}
throw new Error(msg);
}
statusElement.innerText = "AIが文章を生成中...";
// --- Responses API のSSEを読み取り(event: と data: を処理)---
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let currentEvent = "";
const flushLines = (chunkText) => {
buffer += chunkText;
const lines = buffer.split(/\r?\n/);
buffer = lines.pop() || ""; // 最後の不完全行はbufferに戻す
for (const line of lines) {
if (!line.trim()) continue;
if (line.startsWith("event:")) {
currentEvent = line.replace(/^event:\s*/, "").trim();
continue;
}
if (line.startsWith("data:")) {
const dataRaw = line.replace(/^data:\s*/, "").trim();
if (dataRaw === "[DONE]") {
// 公式の終端トークン
return "DONE";
}
try {
const data = JSON.parse(dataRaw);
if (currentEvent === "response.output_text.delta" && data.delta) {
cursor.insertAdjacentText("beforebegin", data.delta);
}
// モデル側で最終化イベント
if (currentEvent === "response.completed") {
return "DONE";
}
// エラーイベント(まれ)
if (currentEvent === "response.error") {
throw new Error(data?.error?.message || "Unknown streaming error");
}
} catch (e) {
// JSONでない行は無視(キープアライブ等)
console.error("SSE JSON parse error:", dataRaw, e);
}
}
}
};
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value, { stream: true });
const status = flushLines(text);
if (status === "DONE") break;
}
statusElement.innerText = "生成が完了しました。";
} catch (error) {
console.error("Request failed:", error);
statusElement.innerText = "エラーが発生しました。";
document.getElementById("user_result").innerText = String(error.message || error);
} finally {
// --- UIを元に戻す ---
const c = document.querySelector("#user_result .user_cursor");
c?.remove();
aiButtons.forEach(btn => btn.disabled = false);
}
});
});
</script>HTML/JavaScript (chat completions API version)
<style>
/* AI呼び出しボタン */
.user_aiBtn {
border: 1px solid #ccc;
background-color: #fff;
padding: 6px 12px;
border-radius: 20px;
cursor: pointer;
font-size: 13px;
transition: all 0.2s ease;
color: #333;
margin-right: 8px; /* ボタン間の余白 */
margin-bottom: 12px;
}
.user_aiBtn:hover {
background-color: #f0f0f0;
border-color: #bbb;
}
.user_aiBtn:disabled {
opacity: 0.5;
cursor: not-allowed;
background-color: #eee;
}
/* 結果表示エリア */
#user_result {
background-color: #f9f9f9;
border: 1px solid #ddd;
border-radius: 4px;
padding: 12px;
min-height: 8em;
white-space: pre-wrap;
word-break: break-word;
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
font-size: 14px;
line-height: 1.6;
color: #222;
margin-top: 4px;
}
/* ステータス表示エリア */
#user_status {
font: 12px/1.4 system-ui, sans-serif;
opacity: 0.75;
margin-top: 8px;
min-height: 1.4em;
}
/* タイピングカーソルのアニメーション */
@keyframes blink {
50% { opacity: 0; }
}
.user_cursor {
display: inline-block;
width: 8px;
height: 1em;
background-color: #333;
margin-left: 2px;
animation: blink 1s step-end infinite;
vertical-align: text-bottom;
}
</style>
<button type="button" class="user_aiBtn">gpt-5-nano</button>
<button type="button" class="user_aiBtn">gpt-5-mini</button>
<button type="button" class="user_aiBtn">gpt-5</button>
<button type="button" class="user_aiBtn">gpt-5-codex</button>
<button type="button" class="user_aiBtn">gpt-4o</button>
<button type="button" class="user_aiBtn">gpt-4o-mini</button>
<pre id="user_result"></pre>
<div id="user_status"></div>
<script>
// --- 全てのボタンにイベントリスナーを設定 ---
const aiButtons = document.querySelectorAll(".user_aiBtn");
aiButtons.forEach(button => {
button.addEventListener("click", async (event) => {
// --- Questetraのデータ項目から値を取得 ---
const strKey = qbpms.form.get("q_openAiApiKey");
const strInstruction = qbpms.form.get("q_instruction");
const strInput = qbpms.form.get("q_input");
// --- 必要なHTML要素を取得 ---
const resultElement = document.getElementById("user_result");
const statusElement = document.getElementById("user_status");
// --- クリックされたボタンのテキストをモデル名として取得 ---
const modelName = event.currentTarget.innerText.trim();
// --- 入力チェック ---
if (!strKey || !strInput) {
statusElement.innerText = "APIキーと入力内容は必須です。";
resultElement.innerText = "";
return;
}
// --- UIを処理中状態に更新 ---
aiButtons.forEach(btn => btn.disabled = true);
statusElement.innerText = "AIに接続中...";
resultElement.innerHTML = '<span class="user_cursor"></span>';
const cursor = resultElement.querySelector(".user_cursor");
try {
// --- APIリクエストを送信 ---
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${strKey}`
},
body: JSON.stringify({
model: modelName,
messages: [
{ role: "system", content: strInstruction },
{ role: "user", content: strInput }
],
stream: true
})
});
// --- エラーレスポンスの処理 ---
if (!response.ok) {
const errorData = await response.json();
throw new Error(`API Error: ${response.status}\n${JSON.stringify(errorData, null, 2)}`);
}
statusElement.innerText = "AIが文章を生成中...";
// --- レスポンスをストリーミングで処理 ---
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split("\n").filter(line => line.trim().startsWith("data:"));
for (const line of lines) {
const rawJson = line.replace(/^data: /, "").trim();
if (rawJson === "[DONE]") break;
try {
const parsed = JSON.parse(rawJson);
const content = parsed.choices[0]?.delta?.content || "";
if (content) {
cursor.insertAdjacentText('beforebegin', content);
}
} catch (e) {
console.error("JSON parse error:", rawJson, e);
}
}
}
statusElement.innerText = "生成が完了しました。";
} catch (error) {
console.error("Request failed:", error);
statusElement.innerText = "エラーが発生しました。";
resultElement.innerText = error.message;
} finally {
// --- UIを元に戻す ---
cursor?.remove();
aiButtons.forEach(btn => btn.disabled = false);
}
});
});
</script>
Freely modifiable HTML/JavaScript code, MIT License. No warranty of any kind.
(Decoration using JavaScript is only available in the Professional edition: M213)
(Decoration using JavaScript is only available in the Professional edition: M213)
Capture



