Bagaimana cara memasukkan ChatGPT ke editor WYSIWYG – Beragampengetahuan
Dengan semua hype, kecerdasan buatan (atau lebih tepatnya pembelajaran mesin (ml) dan model bahasa besar (LL.M.) di mana-mana. Secara pribadi, saya mungkin tidak terlalu sering menggunakan ChatGPT (dan alternatif serupa), tetapi saya mengandalkan GitHub Copilot (untuk pelengkapan otomatis pintar dalam Kode VS) atau Grammarly (untuk mengedit posting blog saya) setiap hari seperti itu.
Saya pikir kita masih jauh dari terobosan AGI, dan teknologi saat ini tidak cukup untuk membawa kita ke sana (untungnya atau tidak).Artinya, kami telah menelusuri hingga “Augmentasi Kecerdasan Buatan” Aplikasi, yang di atas mungkin tidak memiliki sistem AI terbaik, tetapi mereka mengintegrasikannya dengan cara terbaik.
Itulah mengapa ini adalah proses yang menarik, menjelajahi API OpenAI dan mencoba mengintegrasikannya ke dalam Rich Text Editor (RTE) Vrite – CMS tanpa kepala open source saya.
Contents
Editor WYSIWYG yang diperluas
Singkatnya, Vrite adalah CMS tanpa kepala untuk konten teknis, seperti blog pemrograman atau dokumentasi perangkat lunak. Ini dapat dilihat sebagai dua aplikasi dalam satu – dasbor Kanban untuk manajemen konten dan editor WYSIWYG untuk menulis, dengan pengembangan tambahan seperti editor cuplikan tersemat dan fitur ramah pemformat.
Tambahan utama terbaru untuk Vrite adalah sistem ekstensi awal yang memudahkan untuk membangun integrasi dan memperluas kemampuan Vrite.Bagi saya ini sepertinya cara sempurna untuk membawa ChatGPT ke dalam editor – sebagai memperluas.
operasi blok
Untuk dapat mengintegrasikan ChatGPT ke dalam editor menggunakan sistem ekstensi, API baru harus diperkenalkan.saya menyebutnya API manipulasi blok Karena dirancang khusus untuk menambahkan tindakan cepat ke editor, beroperasi pada blok konten tingkat atas seperti paragraf, tajuk, atau gambar, seperti:

Menggunakan Block Actions API, ekstensi dapat membaca konten JSON dari blok aktif dan memperbaruinya dengan konten di HTML, seperti halnya di Vrite API (di satu sisi, mem-parsing output JSON lebih mudah, dan di sisi lain tangan, HTML ) cocok untuk mengubah konten menjadi ).
Dari sisi UI, tindakan blok muncul sebagai tombol di sisi blok yang dipilih secara aktif. Mereka dapat langsung meminta tindakan saat diklik, atau – seperti yang dilakukan ChatGPT – membuka menu tarik-turun yang meminta pengguna untuk detail lebih lanjut.

Tombol-tombol harus diposisikan secara mutlak, yang membutuhkan ekstensi TipTap khusus dan penggalian lebih dalam ProseMirror yang mendasarinya (kedua perpustakaan mendukung editor Vrite).
Proses ini pada dasarnya bermuara pada penentuan posisi dan ukuran node blok, dengan pilihan seluruh node tingkat atas atau hanya anak-anaknya (kode sumber):
// ...
const BlockActionMenuPlugin = Extension.create({
// ...
onSelectionUpdate() {
const { selection } = this.editor.state;
const isTextSelection = selection instanceof TextSelection;
const selectedNode = selection.$from.node(1) || selection.$from.nodeAfter;
if (!selectedNode) {
box.style.display = 'none';
return;
}
const { view } = this.editor;
const node =
view.nodeDOM(selection.$from.pos) ||
view.nodeDOM(selection.$from.pos - selection.$from.parentOffset) ||
view.domAtPos(selection.$from.pos)?.node;
if (!node) return;
const blockParent = getBlockParent(node);
const parentPos = document
.getElementById('pm-container')
?.getBoundingClientRect();
const childPos = blockParent?.getBoundingClientRect();
if (!parentPos || !childPos) return;
const relativePos = {
top: childPos.top - parentPos.top,
right: childPos.right - parentPos.right,
bottom: childPos.bottom - parentPos.bottom,
left: childPos.left - parentPos.left,
};
let rangeFrom = selection.$from.pos;
let rangeTo = selection.$to.pos;
box.style.top = `${relativePos.top}px`;
box.style.left = `${relativePos.left + parentPos.width}px`;
box.style.display = 'block';
if (isTextSelection) {
try {
const p = findParentAtDepth(selection.$from, 1);
rangeFrom = p.start - 1;
rangeTo = p.start + p.node.nodeSize - 1;
} catch (e) {
box.style.display = 'none';
}
}
// ...
},
});
Ganti konten editor
Bagian kedua berkaitan dengan proses aktual penggantian konten blok dengan konten yang baru disajikan. Hal tersulit di sini adalah mendapatkan rentang yang benar (posisi awal dan akhir di ProseMirror) dari node blok. Ini diperlukan untuk mengganti rentang dengan benar dengan perintah TipTap.
Jika Anda mencermati cuplikan kode terakhir — kodenya sudah ada. Pada setiap pembaruan pemilihan, jangkauan pemblokiran dan pemosisian UI tindakan pemblokiran diperbarui.
Jauh lebih mudah untuk benar-benar mengganti rentang dengan konten baru. Yang harus dilakukan hanyalah mengubah HTML menjadi JSON yang mengikuti skema dan melibatkan perintah yang sesuai (kode sumber):
// ...
const replaceContent = (content) => {
unlock.clear();
setLocked(true);
if (range()) {
let size = 0;
const nodeOrFragment = createNodeFromContent(
content,
props.state.editor.schema
);
if (nodeOrFragment instanceof PMNode) {
size = nodeOrFragment.nodeSize;
} else {
size = nodeOrFragment.size;
}
props.state.editor
.chain()
.focus()
.insertContentAt(
range()!,
generateJSON(content, props.state.editor.extensionManager.extensions)
)
.scrollIntoView()
.focus()
.run();
setRange({ from: range()!.from, to: range()!.from + size - 1 });
computeDropdownPosition()();
}
unlock();
};
// ...
ini replaceContent() Fungsi kemudian dapat dipanggil dari jarak jauh dari kotak pasir yang diperluas dengan mengirimkan pesan yang sesuai ke bingkai utama.
Untuk mengaktifkan kasus penggunaan seperti integrasi ChatGPT, di mana konten akan diperbarui (yaitu diganti) beberapa kali berturut-turut sebelum proses selesai, fungsi juga mengunci editor dan memperbarui ruang lingkup untuk waktu yang singkat saat fungsi dipanggil, dan UI penentuan posisi untuk setiap panggilan. Tetapi mengapa ini perlu?
Integrasi API dengan OpenAI
Proses pengintegrasian API OpenAI didokumentasikan dengan baik dalam dokumentasi resminya. Mengingat SDK resmi disediakan, seluruh proses dapat dilakukan hanya dengan beberapa baris kode:
async ({ ctx, input }) => {
const configuration = new Configuration({
apiKey: ctx.fastify.config.OPENAI_API_KEY,
organization: ctx.fastify.config.OPENAI_ORGANIZATION,
});
const openai = new OpenAIApi(configuration);
const response = await openai.createChatCompletion({
model: 'gpt-3.5-turbo',
messages: [{ role: 'user', content: input.prompt }],
});
};
Sekarang, itu semua benar, tetapi hanya jika Anda bersedia menunggu +20 detik Satu respons! Itu banyak untuk satu permintaan.Dari mengubah lokasi server hingga mengoptimalkan permintaan melalui pembatasan max_tokens Atau menyesuaikan parameter lain yang valid. Semuanya bermuara pada fakta bahwa LLM saat ini (setidaknya pada level GPT-3) masih cukup lambat.
Karena itu, aplikasi ChatGPT masih dianggap cukup cepat dan responsif.Ini berkat penggunaan streaming dan peristiwa yang dikirim server (SSE).
Tanggapan GPT Obrolan Aliran
Penyelesaian obrolan API OpenAI dan titik akhir lainnya mendukung streaming melalui peristiwa yang dikirim server, yang pada dasarnya menjaga koneksi terbuka di mana token baru dikirim segera setelah tersedia.
Sayangnya SDK Node.js resmi tidak mendukung streaming dan mengharuskan Anda menggunakan solusi untuk membuatnya berfungsi, menghasilkan lebih banyak kode, hanya untuk menghubungkan ke API (sumber):
async ({ ctx, input }) => {
const configuration = new Configuration({
apiKey: ctx.fastify.config.OPENAI_API_KEY,
organization: ctx.fastify.config.OPENAI_ORGANIZATION,
});
const openai = new OpenAIApi(configuration);
const response = await openai.createChatCompletion(
{
model: 'gpt-3.5-turbo',
stream: true,
messages: [{ role: 'user', content: input.prompt }],
},
{ responseType: 'stream' }
);
ctx.res.raw.writeHead(200, {
...ctx.res.getHeaders(),
'content-type': 'text/event-stream',
'cache-control': 'no-cache',
connection: 'keep-alive',
});
return new Promise<void>((resolve) => {
const responseData = response.data as unknown as {
on(event: string, data: (data: string) => void): void;
};
responseData.on('data', (data) => {
const lines = data
.toString()
.split('\n')
.filter((line) => line.trim() !== '');
for (const line of lines) {
const message = line.replace(/^data: /, '');
if (message === '[DONE]') {
ctx.res.raw.end();
resolve();
continue;
}
try {
const parsed = JSON.parse(message);
const content = parsed.choices[0].delta.content || '';
if (content) {
ctx.res.raw.write(`data: ${encodeURIComponent(content)}`);
ctx.res.raw.write('\n\n');
}
} catch (error) {
console.error('Could not JSON parse stream message', message, error);
}
}
});
});
};
Selain itu, Anda juga harus mendukung streaming antara server API dan klien web, untuk Vrite ini berarti mengintegrasikan SSE dengan Fastify dan tRPC. Bukan solusi terbersih, tapi masih sangat stabil.
Dari ujung depan (tepatnya memperluas kotak pasir), koneksi ke titik akhir streaming baru harus dibuat dan data yang masuk ditangani dengan benar (kode sumber):
import { fetchEventSource } from "@microsoft/fetch-event-source";
// ...
const generate = async (context: ExtensionBlockActionViewContext): Promise<void> => {
const includeContext = context.temp.includeContext as boolean;
const prompt = context.temp.prompt as string;
let content = "";
context.setTemp("$loading", true);
window.currentRequestController = new AbortController();
window.currentRequestController.signal.addEventListener("abort", () => {
context.setTemp("$loading", false);
context.refreshContent();
});
await fetchEventSource("https://extensions.vrite.io/gpt", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "text/event-stream"
},
body: JSON.stringify({
prompt: includeContext ? `"${gfmTransformer(context.content)}"\n\n${prompt}` : prompt
}),
signal: window.currentRequestController?.signal,
async onopen() {
return;
},
onerror(error) {
context.setTemp("$loading", false);
context.refreshContent();
context.notify({ text: "Error while generating content", type: "error" });
throw error;
},
onmessage(event) {
const partOfContent = decodeURIComponent(event.data);
content += partOfContent;
context.replaceContent(marked.parse(content));
},
onclose() {
context.setTemp("$loading", false);
context.refreshContent();
}
});
};
Sayangnya, API Web EventSource untuk menangani SSE (dibangun di sebagian besar browser modern) hanya mendukung GET permintaan ketika a POST meminta lebih besar body Data JSON diperlukan. Sebagai alternatif, Anda dapat menggunakan Fetch API atau perpustakaan siap pakai seperti Fetch Event Source dari Microsoft.
Demikian pula, dengan mengaktifkan streaming, Anda sekarang akan menerima token baru segera setelah tersedia.Mengingat API OpenAI menggunakan Markdown dalam format responsnya, token yang masuk perlu disatukan dan diuraikan sebagai HTML untuk menerima replaceContent Fungsi. Untuk ini, saya menggunakan parser Marked.js.
Sekarang, dengan setiap token baru, respons yang lebih besar sedang dibangun. Setiap kali markup baru muncul, Markdown lengkap diuraikan dan konten diperbarui, menghasilkan “efek seperti mengetik” yang bagus.
Meskipun proses ini memiliki beberapa overhead, itu tidak terlihat digunakan, sedangkan Markdown hanya mem-parsing dengan setiap markup baru karena mungkin berisi, misalnya, akhir dari blok kode atau akhir paragraf yang diformat. Jadi, meskipun proses ini dapat dioptimalkan, namun tidak akan menghasilkan peningkatan kinerja yang terlihat dalam banyak kasus.
Terakhir, perlu dicatat bahwa menggunakan AbortController, yang dapat digunakan untuk menghentikan streaming kapan saja sesuai keinginan pengguna. Ini sangat berguna untuk respons yang lebih lama.

garis bawah
Secara keseluruhan, saya senang dengan hasilnya. Berkat penguraian Markdown, aliran data, pengetikan yang baik, dan integrasi yang baik dengan blok konten editor yang ada – semuanya ini bersama-sama menciptakan pengalaman pengguna yang menarik.
Sekarang, pasti ada ruang untuk perbaikan. Block Actions API dan Ekstensi Vrite secara keseluruhan masih memiliki banyak pekerjaan pengembangan yang harus dilakukan sebelum dapat dibuat oleh pengguna lain. Peningkatan UI/UX lainnya yang perlu dipertimbangkan, seperti mengoperasikan beberapa potongan sekaligus (konteks tambahan untuk ChatGPT misalnya) dan menampilkan inline UI (seperti Notion AI) alih-alih mengaburkan tampilan, hanyalah beberapa contoh yang saya pertimbangkan. Meskipun demikian, perlu beberapa waktu sebelum ide-ide ini diterapkan dengan baik.

Vrite lebih dari sekadar editor yang disempurnakan GPT. Ini adalah CMS sumber terbuka lengkap yang berfokus pada konten teknis seperti blog pemrograman, termasuk editor kode, API, dasbor admin Kanban, dan integrasi penerbitan yang mudah. Jadi jika Anda tertarik untuk mencobanya dan mungkin menggunakannya untuk memperkuat blog Anda, pastikan untuk memeriksanya!
rencana pengembangan website
metode pengembangan website
jelaskan beberapa rencana untuk pengembangan website, proses pengembangan website, kekuatan dan kelemahan bisnis pengembangan website
, jasa pengembangan website, tahap pengembangan website, biaya pengembangan website
#Bagaimana #cara #memasukkan #ChatGPT #editor #WYSIWYG