/* * @author User:Nullnominal <https://en.wikipedia.org/wiki/User:Nullnominal> * @license CC-BY-SA-4.0 * * * // Imports * @typedef {import('@types/j
/*
* @author User:Nullnominal <https://en.wikipedia.org/wiki/User:Nullnominal>
* @license CC-BY-SA-4.0
*
*
* // Imports
* @typedef {import('@types/jquery')} $
* @typedef {import("types-mediawiki")} mw
* @typedef {import("@types/oojs-ui")} OO
* @typedef {import("./wpAIToolsProvider.d.ts").default} Provider
*
*/
/// <reference path="wpAIToolsProvider.d.ts" />
/**
* @typedef {{
* provider: Provider,
* }} AITools;
* @typedef {{
* score: number,
* reasoning: string,
* quote: string,
* domStartId: string
* domEndId: string
* }} Verdict;
* @typedef {{
* tool: string,
* author: string,
* oldID: string,
* dom: string,
* finalVerdict: {
* score: number,
* reasoning: string
* },
* verdict: Verdict[]
* }} FlowResponse
*/
mw.loader.using("ext.aitools.Provider", "oojs-ui-windows").done(() => {
class FlowChecker extends OO.ui.TabPanelLayout {
/**
* @type {Provider};
*/
provider;
constructor() {
super("Flow", {
label: "Flow",
});
/**
* @type {Provider}
*/
this.provider = AITools.provider._instance;
this.$element.append(this.createUI().$element);
this.provider.addTool(this);
}
/**
* @argument {FlowResponse} resp;
*/
createPopup(resp) {
const root = new OO.ui.TabPanelLayout("Flow Response", {
label: "Flow Response",
});
let body;
if (mw.config.get("wgRevisionId") !== resp.oldID) {
body = new OO.ui.StackLayout({
continuous: true,
padded: true,
content: [
new OO.ui.PanelLayout({
text: "Error",
padded: true,
framed: true,
}),
new OO.ui.PanelLayout({
padded: true,
text: `The current page
(${mw.config.get("wgPageName").replace("_", "")}
@
${mw.config.get("wgRevisionId")})
is not the page that this was file was generated on `,
}),
new OO.ui.ButtonWidget({
href: mw.util.getUrl(`Special:PermaLink/${resp.oldID}`),
label: "Go to Correct Page",
title: "Go to Page",
}),
],
});
} else {
body = new OO.ui.StackLayout({
continuous: true,
padded: true,
content: [
new OO.ui.PanelLayout({
text: `Author: ${resp.author}`,
}),
new OO.ui.PanelLayout({
text: `Tool: ${resp.tool}`,
}),
new OO.ui.StackLayout({
continuous: true,
padded: true,
framed: true,
content: [
new OO.ui.PanelLayout({
text: `Overall Quality: ${resp.finalVerdict.score * 100}%`,
}),
new OO.ui.PanelLayout({
text: resp.finalVerdict.reasoning,
}),
],
}),
new OO.ui.StackLayout({
continuous: true,
padded: true,
framed: true,
content: resp.verdict.map(this.createVerdict),
}),
],
});
}
root.$element.append(body.$element);
this.provider.addTool(root);
}
/**
*
* @param {string} title
* @returns {Promise<string>}
*/
async getPageContents() {
const pageRequest = await api.get({
action: "query",
format: "json",
prop: "revisions",
revisions: mw.config.get("wgCurRevisionId"),
formatversion: "2",
rvprop: "content",
rvslots: "main",
});
const rev = pageRequest.query.pages[0].revisions[0];
return rev.slots.main.content;
}
/**
* @argument {Verdict} verdict;
*/
createVerdict(verdict) {
return new OO.ui.StackLayout({
padded: true,
framed: true,
continuous: true,
content: [
new OO.ui.PanelLayout({
text: `Quote Quality: ${verdict.score * 100}%`,
}),
new OO.ui.PanelLayout({
text: verdict.reasoning,
}),
new OO.ui.PanelLayout({
padded: true,
text: `"${verdict.quote}`,
}),
],
});
}
callAI() {}
/**
* @argument {{score: number, reasoning: string, quote: string}} verdict
* @returns {Verdict}
*/
_getVerdictLocations(verdicts, wikitext, dom) {
const elements = document.createTreeWalker(dom, NodeFilter.SHOW_ELEMENT);
/**
* @type {{
* [k: string]: {
* start: number,
* end: number,
* currentStart: number,
* currentEnd: number,
* startElement: HTMLElement
* endElement: HTMLElement
* }
* }}
*/
const map = {};
verdicts.map((e) => {
match = wikitext.match(e.quote);
map[e.quote] = {
start: match.index,
end: match.index + e.quote.length,
currentStart: 0,
currentEnd: wikitext.length,
startElement: elements[0],
endElement: elements[0],
};
});
let elm;
while ((elm = elements.nextNode())) {
let data = elm.getAttribute("data-parsoid");
if (data === null || !data.includes("dsr")) continue;
try {
data = JSON.parse(data);
} catch (e) {
data = JSON.parse("{" + data.match(/\"dsr\":.*/));
}
const start = data.dsr[0];
const end = data.dsr[1];
for (let quote of quotes) {
if (map[quote].start >= start && start >= map[quote].currentStart) {
map[quote].currentStart = start;
map[quote].startElement = elm;
}
if (map[quote].end <= end && end <= map[quote].currentEnd) {
map[quote].currentEnd = end;
map[quote].endElement = elm;
}
}
}
return verdicts.map((e) => {
e.domStartId = map[e.quote].startElement.id;
e.domEndId = map[e.quote].endElement.id;
});
}
/**
* @argument {{
* verdicts: {
* score: number,
* reasoning: string,
* quote: string
* }[],
* removed: number[],
* added: Verdict[],
* finalVerdict: {
* score: number,
* reasoning: string
* }
* }} aiResp
* @argument {string} wikitext
*
*/
async createDataPackage(aiResp, wikitext, title) {
const rest = new mw.Rest();
let resp = {};
resp["oldid"] = mw.gonfig.get("wgRevisionId");
const parser = new DomParser();
const dom = parser.parseFromString(await rest.post(`/transform/wikitext/to/html/${title}/${resp["oldid"]}`, {
wikitext: "string",
body_only: true,
stash: true
}), "text/html");
let verdicts = aiResp.verdicts.filter((v, i) => {
return !(i in aiResp.removed);
});
resp["verdicts"] = this._getVerdictLocations(verdics.concat(aiResp.added), wikitext, dom);
resp["finalVerdict"] = aiResp.finalVerdict;
resp["tool"] = this.provider.APIName;
resp["oldid"] = mw.config.get("wgRevisionId");
resp["author"] = mw.user.getName();
resp["dom"] = dom.getElementsByClassName("mw-parser-output")[0].outerHTML;
return resp;
}
createUI() {
const callAIButton = new OO.ui.ButtonWidget({
disabled: this.provider.canUseAI(),
label: "Check!",
});
const uploadWidget = new OO.ui.SelectFileInputWidget({
accept: "text/json",
});
uploadWidget.on(
"change",
(async (currentFiles) => {
this.createPopup(JSON.parse(await currentFiles[0].text()));
}).bind(this),
);
callAIButton.on("click", this.callAI.bind(this));
return new OO.ui.StackLayout({
continuous: true,
content: [
new OO.ui.StackLayout({
framed: true,
padded: false,
continuous: true,
content: [
new OO.ui.PanelLayout({
padded: true,
$content: $("<h3>Live LLM</h3>"),
}),
new OO.ui.PanelLayout({
padded: true,
content: [
new OO.ui.TextInputWidget({
value: mw.config.get("wgPageName").replace("_", " "),
disabled: true,
}),
],
}),
new OO.ui.PanelLayout({
padded: true,
content: [callAIButton],
}),
],
}),
new OO.ui.StackLayout({
framed: true,
padded: true,
continuous: true,
content: [
new OO.ui.PanelLayout({
$content: $("<h3>Previous Analysis</h3>"),
}),
uploadWidget,
],
}),
],
});
}
}
new FlowChecker();
});
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.