您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Experiment to use ChatGPT to summarize page sections.
/* jshint browser: true, jquery: true */ /* globals $, mw, OO */ // ==UserScript== // @name Wikipedia ChatGPT section summaries // @namespace http://tampermonkey.net/ // @version 0.1 // @description Experiment to use ChatGPT to summarize page sections. // @author tonythomas01 // @license GPL-3.0-or-later // @match https://*.wikipedia.org/* // @icon https://doc.wikimedia.org/oojs-ui/master/demos/dist/themes/wikimediaui/images/icons/robot.svg // @grant none // ==/UserScript== (function () { const GPTModel = "gpt-3.5-turbo"; const HuggingFacesModelsMap = { "FacebookBartLargeCNN": "facebook/bart-large-cnn", }; function main() { $('.mw-headline').each(function () { var sectionHeading = $(this); const editLink = sectionHeading.siblings('.mw-editsection'); const immediateParent = sectionHeading.parent(); if (immediateParent.is('h2') || immediateParent.is('.mw-heading2')) { var summarizeLink = $('<span>') .addClass('mw-summarysection') .addClass('mw-editsection') .css('margin-left', '0.5em') .append( $('<span>') .addClass('mw-editsection-bracket') .text('[') ) .append( $('<a>') .attr('href', '#') .text('summarize') .click(function (e) { e.preventDefault(); summarizeSection(sectionHeading); }) ) .append( $('<span>') .addClass('mw-editsection-bracket') .text(']') ); editLink.after(summarizeLink); } }); } function loadMain() { if (!window.$) { return setTimeout(loadMain, 50); } $(main); } loadMain(); function fetchSummaryUsingOpenAPI(fixedPromptForChatGPT, openAPIKey, sectionText, callback) { const gptQuery = fixedPromptForChatGPT + sectionText; console.log("To GPT: ", gptQuery); $.ajax({ url: "https://api.openai.com/v1/chat/completions", method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + openAPIKey, }, data: JSON.stringify({ model: "gpt-3.5-turbo", messages: [ { role: "user", content: gptQuery } ], temperature: 0.7 }), success: function (response) { const responseContent = response.choices[0].message.content; callback(null, responseContent); }, error: function (error) { callback(error); } }); } function fetchSummaryUsingHuggingFacesModel(apiKeySecret, modelName, sectionText, callback) { $.ajax({ url: "https://api-inference.huggingface.co/models/" + modelName, method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + apiKeySecret, }, data: JSON.stringify({ inputs: sectionText }), success: function (response) { const responseContent = response[0].summary_text; callback(null, responseContent); }, error: function (error) { callback(error); } }); } function onSummaryFetch(error, responseContent) { if (error) { console.log(error); } else { console.log("LLM model responded: " + responseContent); } } function getSectionTextUnderHeading(namespace, sectionParent) { if (sectionParent.parent().is('.mw-heading')) { return getSectionText( sectionParent ); } else { return $.Deferred().resolve( $('<div>').append( sectionParent.nextUntil('h2').clone() ).prop('innerText') ); } } var discussionToolsInfo; /** * @param {jQuery} $heading * @return {jQuery.Promise<Array>} */ function getSectionData( $heading ) { var dataPromise; if ( discussionToolsInfo ) { dataPromise = $.Deferred().resolve( discussionToolsInfo ); } else { dataPromise = mw.loader.using( [ 'mediawiki.api' ] ).then( function () { return new mw.Api().get( { action: 'discussiontoolspageinfo', page: mw.config.get( 'wgPageName' ), prop: 'threaditemshtml', format: 'json', formatversion: 2, } ); } ).then( function ( data ) { discussionToolsInfo = data; return data; } ); } var sectionId = $heading.find( '.mw-headline' ).data( 'mw-thread-id' ); var sectionContent = []; var processReplies = function ( reply ) { if ( reply.type === 'comment' ) { sectionContent.push( { type: 'comment', level: reply.level, author: reply.author, text: getCommentTextFromHtml( reply.html ) } ); } else if ( reply.type === 'heading' ) { sectionContent.push( { type: 'heading', level: reply.level, headingLevel: reply.headingLevel, text: getCommentTextFromHtml( reply.html ) } ); for ( var i = 0; i < reply.replies.length; i++ ) { processReplies( reply.replies[i] ); } } else { console.log( 'Unexpected type: ' + reply.type, reply ); } }; return dataPromise.then( function ( data ) { for ( var i = 0; i < data.discussiontoolspageinfo.threaditemshtml.length; i++ ) { var section = data.discussiontoolspageinfo.threaditemshtml[i]; if ( section.id === sectionId ) { sectionContent.push( { type: 'heading', level: 0, headingLevel: section.level, text: getCommentTextFromHtml( section.html ) } ); for ( var j = 0; j < section.replies.length; j++ ) { processReplies( section.replies[j] ); } } } return sectionContent; } ); } /** * @param {string} html * @return {string} */ function getCommentTextFromHtml( html ) { return $.parseHTML( '<div>' + html + '</div>' ).map( el => el.innerText || '' ).join( '' ); } /** * @param {jQuery} $heading * @return {jQuery.Promise<string>} */ function getSectionText( $heading ) { return getSectionData( $heading ).then( function ( data ) { var sectionText = ''; for ( var i = 0; i < data.length; i++ ) { var item = data[i]; if ( item.type === 'heading' ) { sectionText += '\t'.repeat( item.level ) + '='.repeat( item.headingLevel ) + item.text + '='.repeat( item.headingLevel ) + '\n\n'; } else if ( item.type === 'comment' ) { sectionText += ( item.author + ': ' + item.text ).replace( /^|\n/g, '$&' + '\t'.repeat( item.level ) ); } sectionText += '\n\n'; } return sectionText; } ); } var apiKey = null; /** * @return {string} */ function getApiKey() { if ( apiKey ) { return apiKey; } apiKey = localStorage.getItem( 'LLMApiKey' ); if ( apiKey ) { return apiKey; } apiKey = prompt( 'Please enter your OpenAI API key from https://platform.openai.com/account/api-keys' ); if ( !apiKey ) { throw new Error( 'Section summary requires an API key!' ); } localStorage.setItem( 'LLMApiKey', apiKey ); } function summarizeSection(sectionHeading) { const sectionParent = sectionHeading.parent(); const selectedLLMModel = GPTModel; const LLMApiKey = getApiKey(); var fixedPromptForChatGPT = "Summarize the following section in less than 50 words: "; const namespace = mw.config.get("wgCanonicalNamespace"); if (namespace === "Talk") { fixedPromptForChatGPT = "Summarize the following section in less than 50 words. See that each row represents a " + "reply from a user with the Username presented right before (talk). Use the usernames when summarizing. \n"; } getSectionTextUnderHeading(namespace, sectionParent).then(function(sectionText) { console.log("Found Section Text:", sectionText); switch (selectedLLMModel) { case GPTModel: fetchSummaryUsingOpenAPI(fixedPromptForChatGPT, LLMApiKey, sectionText, onSummaryFetch); return; default: fetchSummaryUsingHuggingFacesModel(LLMApiKey, HuggingFacesModelsMap[selectedLLMModel], sectionText, onSummaryFetch); return; } }); } })();