AI एक्सटेंशन
AI एक्सटेंशन एक टूलबार बटन जोड़ता है जो प्रॉम्प्ट डायलॉग खोलता है, अनुरोध आपके चुने प्रदाता को भेजता है, स्ट्रीम होता परिणाम दिखाता है, और मोड के अनुसार क्रिया बटन (डालें / बदलें / अंत में जोड़ें) से उसे एडिटर में डालता है।
<Tiptapify
:content="content"
:items="['ai', '|', 'bold', 'italic']"
:ai="{
aiProvider,
model: 'gpt-4.1-mini',
stream: true,
}"
/>ai prop true (डिफ़ॉल्ट के साथ विकल्प) या एक विकल्प ऑब्जेक्ट (TiptapifyAiOptions) स्वीकार करता है। टूलबार का ai आइटम हमेशा पंजीकृत रहता है, लेकिन aiProvider सेट न हो तो बटन अक्षम रहता है।
चलने वाले डेमो के लिए AI उदाहरण और विकल्पों के पूरे संदर्भ के लिए Props देखें।
आर्किटेक्चर
Tiptapify.vue Toolbar item `ai` Dialog.vue
resolves the `ai` -> Button.vue -> builds the request
prop into a resolved -> (disabled without -> calls aiProvider(request, context, stream)
options object and -> aiProvider) -> streams chunks into result/reasoning fields
provides it (inject -> opens the dialog -> the action button (Insert/Replace/Append) places the result into the editor
'tiptapifyAi')Tiptapifyaiprop को सामान्यीकृत करता है:ai: trueखाली विकल्प ऑब्जेक्ट बन जाता है,promptExamplesस्थानीयकृत डिफ़ॉल्ट पर लौट आता है, और परिणामinject('tiptapifyAi')(TiptapifyAiResolvedOptionsयाfalse) के ज़रिए वंशज कंपोनेंट को उपलब्ध होता है।- टूलबार आइटम (
src/components/Toolbar/ai.ts)Button.vueरेंडर करता है। बटनaiProviderकी उपलब्धता जाँचता है औरDialog.vueखोलता है। Dialog.vueपूरे अनुरोध जीवनचक्र का मालिक है: अनुरोध बनाना, प्रदाता को कॉल करना, स्ट्रीमिंग और परिणाम डालना।
प्रदाता अनुबंध
type TiptapifyAiProvider = (
request: TiptapifyAiRequest,
context: TiptapifyAiEditorContext,
stream?: TiptapifyAiStream,
) => Promise<TiptapifyAiResponse | TiptapifyAiOpenAiResponse | string>प्रदाता एक साधारण async फ़ंक्शन है जिसे आप लागू करते हैं। tiptapify खुद कोई API कॉल नहीं करता — OpenAI-संगत request ऑब्जेक्ट को संभाल सकने वाली कोई भी चीज़ चलती है (fetch, कोई SDK, बैकएंड एडेप्टर)। अपवाद aiEndpoint बैकएंड मोड है (बैकएंड जनरेशन देखें), जहाँ tiptapify अनुरोध आपके एंडपॉइंट पर भेजता है।
अनुरोध (TiptapifyAiRequest) आपके chatCompletionOptions से फैलाया जाता है, फिर प्लगइन उसमें और जोड़ता है:
| फ़ील्ड | स्रोत |
|---|---|
messages | दिया गया हो तो createMessages(context), अन्यथा डिफ़ॉल्ट जोड़ी: एक सिस्टम संदेश (आपका systemPrompt या बिल्ट-इन डिफ़ॉल्ट) और एक उपयोगकर्ता संदेश, जिसमें प्रॉम्प्ट, चुना हुआ एडिटर टेक्स्ट, उसके आसपास का टेक्स्ट और केवल चयन के लिए (या कुछ चुना न होने पर पूरे टेक्स्ट के लिए) उत्तर देने का नियम शामिल होता है |
model | model विकल्प, सेट होने पर |
temperature | temperature विकल्प, सेट होने पर |
stream | stream विकल्प, या chatCompletionOptions.stream |
enable_thinking / reasoning_effort | सोच का टॉगल / प्रयास ड्रॉपडाउन — नीचे देखें |
संदर्भ (TiptapifyAiEditorContext) प्रदाता को एडिटर की पूरी स्थिति देता है: prompt, selectedText, textBefore / textAfter (चयन के पैराग्राफ़ का उसके आसपास का टेक्स्ट, हर तरफ़ अधिकतम 500 अक्षर), text, html, json और mode (insert / replace / append)।
जो चयन किसी शब्द के भीतर शुरू या खत्म होता है, वह पूरा शब्द ले लेता है, और डिफ़ॉल्ट संदेश चयन के आसपास का टेक्स्ट केवल चयन के लिए उत्तर देने के नियम के साथ भेजते हैं: केवल एक टुकड़ा देने पर मॉडल उसे पूरा टेक्स्ट समझकर पूरा कर देता है, और बदलाव चयन के बाद वाले हिस्से को दोहरा देता।
उत्तर: प्रदाता कंटेंट को साधारण string के रूप में, { content } (TiptapifyAiResponse) के रूप में, या OpenAI-संगत choices पेलोड (TiptapifyAiOpenAiResponse) के रूप में लौटा सकता है। डायलॉग उसी क्रम में कंटेंट निकालता है और शुरुआती खाली जगह हटा देता है। अस्वीकार होने पर त्रुटि संदेश डायलॉग में दिखता है (या त्रुटि Error इंस्टेंस न हो तो ai.error) और डायलॉग खुला रहता है।
बैकएंड जनरेशन (aiEndpoint)
aiProvider लागू करने के बजाय AI सुविधा को किसी बैकएंड LLM एंडपॉइंट की ओर इंगित करें:
// example
const ai = {
aiEndpoint: '/api/ai/generate',
model: 'gpt-4.1-mini',
stream: true,
}फिर tiptapify अनुरोध खुद बनाकर एंडपॉइंट पर पोस्ट करता है। दोनों सेट होने पर स्पष्ट aiProvider हमेशा प्राथमिकता पाता है।
अनुरोध — एक न्यूनतम JSON पेलोड (TiptapifyAiBackendRequest):
| फ़ील्ड | कब भेजा जाता है |
|---|---|
prompt | हमेशा — डायलॉग में टाइप किया गया अनुरोध |
instruction | हमेशा — आपका systemPrompt, कस्टम createMessages का सिस्टम संदेश, या बिल्ट-इन डिफ़ॉल्ट |
stream | केवल stream: true होने पर |
thinking | केवल डायलॉग का सोच टॉगल चालू होने पर |
reasoning_effort | केवल जब उपयोगकर्ता ने कोई स्तर चुना हो (default नहीं) |
model | केवल model विकल्प सेट होने पर |
उत्तर — उदारता से स्वीकार किया जाता है: साधारण स्ट्रिंग, { content } ऑब्जेक्ट, या OpenAI-संगत choices पेलोड।
स्ट्रीमिंग — जब अनुरोध में stream: true हो, तो उत्तर की बॉडी SSE स्ट्रीम के रूप में पढ़ी जाती है (data: पंक्तियाँ, जो data: [DONE] से समाप्त होती हैं)। स्वीकृत चंक प्रारूप:
- OpenAI-शैली —
data: {"choices":[{"delta":{"content":"…", "reasoning_content":"…"}}]} - सरल —
data: {"type":"content"|"reasoning","data":"…"} - सपाट —
data: {"content":"…","reasoning_content":"…"} - सादा टेक्स्ट —
data: some text(गैर-JSONdataपंक्ति कंटेंट चंक है)
content चंक onChunk के ज़रिए परिणाम फ़ील्ड अपडेट करते हैं, reasoning_content चंक onReasoning के ज़रिए सोच पैनल को भरते हैं। अगर बैकएंड stream को अनदेखा करके साधारण बॉडी के साथ उत्तर देता है, तो वही बॉडी उपयोग होती है। रोकें पर क्लिक या डायलॉग बंद करने से अनुरोध रुक जाता है; आंशिक रूप से स्ट्रीम हुआ कंटेंट परिणाम फ़ील्ड में रखा जाता है।
हेडर — aiHeaders (एक Record<string, string>) Content-Type: application/json के ऊपर मिलाए जाते हैं। aiHeaders में Authorization हेडर जैसा है वैसा रखा जाता है; अन्यथा tokenProvider का टोकन Authorization: Bearer <token> के रूप में भेजा जाता है।
त्रुटियाँ — गैर-OK उत्तर बैकएंड का त्रुटि संदेश (JSON { error } / { message } या कच्ची बॉडी) डायलॉग में दिखाता है।
स्ट्रीमिंग
जब stream: true हो (या chatCompletionOptions.stream true हो), डायलॉग तीसरे आर्गुमेंट के रूप में एक TiptapifyAiStream ऑब्जेक्ट देता है:
type TiptapifyAiStream = {
signal: AbortSignal,
onChunk: (chunk: string) => void,
onReasoning: (chunk: string) => void,
}signal— इसेfetch(या अपनी SDK कॉल) को दें। उपयोगकर्ता के रोकें पर क्लिक करने या डायलॉग बंद करने पर यह रुक जाता है, ताकि चल रहे अनुरोध चलते न रहें।onChunk— हर कंटेंट चंक को परिणाम फ़ील्ड में जोड़ें। जनरेट करते समय परिणाम फ़ील्ड केवल-पढ़ने वाला रहता है और आंशिक रूप से स्ट्रीम हुआ कंटेंट रखा जाता है (आप रोक सकते हैं, फिर उसे डाल सकते हैं या छोड़ सकते हैं)।onReasoning— मॉडल का तर्क/सोच आउटपुट (जैसे Qwen/DeepSeek/o-series काreasoning_contentडेल्टा) परिणाम के ऊपर समेटे जा सकने वाले सोच पैनल को भेजें। स्ट्रीमिंग के दौरान पैनल अपने-आप खुलता और स्क्रॉल होता है। तर्क आउटपुट केवल दिखाने के लिए है और एडिटर में कभी नहीं डाला जाता।
जो प्रदाता स्ट्रीम नहीं करते वे stream आर्गुमेंट को अनदेखा करते हैं — यह वैकल्पिक है।
सोच और तर्क प्रयास
डायलॉग के दो उपयोगकर्ता नियंत्रण अनुरोध के फ़ील्ड से जुड़ते हैं। दोनों विकल्पों के ज़रिए वैकल्पिक हैं और प्लगइन के अधीन हैं — इन फ़ील्ड को आप chatCompletionOptions में खुद सेट नहीं करते (और नहीं करना चाहिए)।
thinking: trueसोच टॉगल दिखाता है (डिफ़ॉल्ट रूप से चालू, हर डायलॉग खुलने पर रीसेट)। प्लगइन टॉगल का मान अनुरोध मेंenable_thinking: true | falseके रूप में लिखता है।thinkingगायब हो याthinking: falseटॉगल छिपा देता है और प्लगइनenable_thinking: falseभेजता है, ताकि जो मॉडल डिफ़ॉल्ट रूप से सोचते हैं वे न सोचें।reasoningEffort: { options, default? }तर्क प्रयास ड्रॉपडाउन दिखाता है।optionsवे कस्टम प्रयास स्तर सूचीबद्ध करता है जो आप देना चाहते हैं ('low' | 'medium' | 'high' | 'xhigh' | 'max'); एक्सटेंशन खुद हमेशा एकdefaultआइटम जोड़ता है (सोच-मोड का आधार), इसलिए आप अपने कॉन्फ़िग मेंdefaultकभी सूचीबद्ध नहीं करते।defaultफ़ील्ड डायलॉग खुलते समय आपके कस्टम स्तरों में से एक को पहले से चुनता है — छोड़ने परdefaultपहले से चुना रहता है। मान हर बार खुलने पर रीसेट होता है। ड्रॉपडाउन तभी रेंडर होता है जबoptionsगैर-खाली सूची हो,thinkingसक्षम हो और सोच टॉगल चालू हो।showReasoning(डिफ़ॉल्टfalse) समेटे जा सकने वाले सोच पैनल को नियंत्रित करता है, जो मॉडल की तर्क प्रक्रिया (reasoning_contentडेल्टा) स्ट्रीम करता है। सोच चालू रखते हुए वह पैनल दिखाने के लिएshowReasoning: trueसेट करें — मॉडल फिर भी तर्क करता है, परिणाम उसी तरह लागू होता है, केवल चरण-दर-चरण तर्क अतिरिक्त रूप से दिखाया जाता है।
क्योंकि प्रदाता (खास तौर पर vLLM/LM Studio) एक ही अनुरोध में enable_thinking और reasoning_effort को अस्वीकार करते हैं, प्लगइन दोनों में से ठीक एक भेजता है:
| स्थिति | अनुरोध फ़ील्ड |
|---|---|
सोच चालू, प्रयास = default (या कोई प्रयास कॉन्फ़िगर नहीं) | enable_thinking (टॉगल से) |
सोच चालू, प्रयास = low / medium / high / xhigh / max | reasoning_effort (ड्रॉपडाउन से); टॉगल अक्षम रहता है |
सोच बंद (टॉगल बंद, या कॉन्फ़िग में thinking सक्षम नहीं) | enable_thinking: false; reasoning_effort नहीं भेजा जाता |
त्रुटियाँ
डायलॉग विफल अनुरोध को कच्ची त्रुटि दिखाने के बजाय समझाता है, और उसके बाद सेवा का अपना संदेश जोड़ता है:
| क्या हुआ | स्पष्टीकरण |
|---|---|
ब्राउज़र को कोई उत्तर नहीं मिला (Failed to fetch, NetworkError…, Load failed) | सेवा बंद है, पहुँच से बाहर है, या CORS ने उत्तर रोक दिया; ब्राउज़र कंसोल बताता है कि कौन सा |
401 / 403 | API कुंजी या पहुँच अधिकार जाँचें |
404 | एंडपॉइंट का पथ जाँचें |
408 / 504 / टाइमआउट | सेवा ने समय पर उत्तर नहीं दिया |
429 | बहुत अधिक अनुरोध |
5xx | सेवा उपलब्ध नहीं है (मॉडल लोड हो रहा है, व्यस्त है) |
aiEndpoint के साथ स्थिति उत्तर से आती है। कस्टम aiProvider को वही स्पष्टीकरण status वाली त्रुटि फेंककर मिलते हैं:
if (!response.ok) {
const body = await response.json().catch(() => null)
throw Object.assign(new Error(body?.error?.message ?? ''), { status: response.status })
}कोई भी अन्य त्रुटि अपने संदेश के साथ दिखाई जाती है।
परिणाम डालना
क्रिया बटन पर — डालें, बदलें या अंत में जोड़ें, मोड के अनुसार — डायलॉग result को एडिटर में रखता है:
- मोड — डायलॉग का क्रिया बटन स्प्लिट बटन है। मुख्य भाग परिणाम को पहले से चुने मोड (डालें / बदलें / अंत में जोड़ें) में लागू करता है:
modeविकल्प उसे पहले से चुनता है, या डायलॉग खुलते समय टेक्स्ट चुना हुआ हो तो बदलें और अन्यथा डालें। बटन के दाईं ओर छोटा "और" (⋮) भाग बाकी मोड वाला मेन्यू खोलता है — उनमें से किसी पर क्लिक करने से परिणाम तुरंत उसी मोड में लागू हो जाता है। डालें परिणाम को कर्सर पर रखता है, बदलें चुने हुए टेक्स्ट (जो उत्पादित करें पर क्लिक के समय पकड़ा गया) को बदलता है, और अंत में जोड़ें उसे दस्तावेज़ के अंत में जोड़ता है। - अक्षर सीमा — जब एडिटर
limitके साथ कॉन्फ़िगर हो (CharacterCountएक्सटेंशन हर उस गैर-पेस्ट ट्रांज़ैक्शन को अस्वीकार करता है जो दस्तावेज़ को सीमा से बड़ा करे), तो परिणाम फ़ील्ड के नीचेgenerated / limitअक्षर काउंटर दिखता है (स्ट्रीमिंग के दौरान लाइव, सीमा पार होने पर हल्का लाल) और परिणाम सीमा से बड़ा होने पर जनरेशन के तुरंत बाद डायलॉगai.limit_exceededचेतावनी दिखाता है। तब डालें बटन और उसका वैकल्पिक-मोड मेन्यू अक्षम हो जाते हैं: परिणाम पूरा-या-कुछ नहीं की तरह लागू होता है, इसलिए लागू करने से पहले उसे छोटा करना होगा।