feat: add image translation feature and enhance regex list functionality
This commit is contained in:
@@ -836,4 +836,5 @@ export const languageEnglish = {
|
|||||||
home: "Home",
|
home: "Home",
|
||||||
showSavingIcon: "Show Saving Icon",
|
showSavingIcon: "Show Saving Icon",
|
||||||
pluginVersionWarn: "This is {{plugin_version}} version of the plugin. which is not compatible with this version of RisuAI. please update the plugin to {{required_version}} version.",
|
pluginVersionWarn: "This is {{plugin_version}} version of the plugin. which is not compatible with this version of RisuAI. please update the plugin to {{required_version}} version.",
|
||||||
|
imageTranslation: "Image Translation",
|
||||||
}
|
}
|
||||||
@@ -472,6 +472,9 @@
|
|||||||
<span class="text-textcolor2 text-sm">{language.risuMDesc}</span>
|
<span class="text-textcolor2 text-sm">{language.risuMDesc}</span>
|
||||||
{:else if $alertStore.submsg === 'preset'}
|
{:else if $alertStore.submsg === 'preset'}
|
||||||
<span class="text-textcolor2 text-sm">{language.risupresetDesc}</span>
|
<span class="text-textcolor2 text-sm">{language.risupresetDesc}</span>
|
||||||
|
{#if cardExportType2 === 'preset' && (DBState.db.botPresets[DBState.db.botPresetsId].image || DBState.db.botPresets[DBState.db.botPresetsId].regex?.length > 0)}
|
||||||
|
<span class="text-red-500 text-sm">Use RisuRealm to share the preset. Preset with image or regexes cannot be exported for now.</span>
|
||||||
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<span class="text-textcolor2 text-sm">{language.ccv3Desc}</span>
|
<span class="text-textcolor2 text-sm">{language.ccv3Desc}</span>
|
||||||
{#if cardExportType2 !== 'charx' && isCharacterHasAssets(DBState.db.characters[$selectedCharID])}
|
{#if cardExportType2 !== 'charx' && isCharacterHasAssets(DBState.db.characters[$selectedCharID])}
|
||||||
|
|||||||
@@ -2,9 +2,216 @@
|
|||||||
import { language } from "src/lang";
|
import { language } from "src/lang";
|
||||||
import TextInput from "../UI/GUI/TextInput.svelte";
|
import TextInput from "../UI/GUI/TextInput.svelte";
|
||||||
import TextAreaInput from "../UI/GUI/TextAreaInput.svelte";
|
import TextAreaInput from "../UI/GUI/TextAreaInput.svelte";
|
||||||
|
import Button from "../UI/GUI/Button.svelte";
|
||||||
|
import { selectSingleFile } from "src/ts/util";
|
||||||
|
import { requestChatData } from "src/ts/process/request";
|
||||||
|
import { alertError } from "src/ts/alert";
|
||||||
|
|
||||||
let selLang = $state("en");
|
let selLang = $state("en");
|
||||||
let prompt = $state("");
|
let prompt = $state('extract text chunk from the image, with all the positions and background color, and translate them to {{slot}} in a JSON format.Format of: \n\n [\n {\n "bg_hex_color": string\n "content": string\n "text_hex_color": string,\n "x_max": number,\n "x_min": number,\n "y_max": number,\n "y_min": number\n "translation": string,\n }\n]\n\n each properties is:\n - x_min, y_min, x_max, y_max: range of 0 (most left/top point of the image) to 1 (most bottom/right point of the image), it is the bounding boxes of the original text chunk.\n - bg_hex_color is the color of the background.\n - text_hex_color is the color of the text.\n - translation is the translated text.\n - content is the original text chunk.');
|
||||||
|
let canvas: HTMLCanvasElement;
|
||||||
|
let ctx: CanvasRenderingContext2D;
|
||||||
|
let inputImage: HTMLImageElement;
|
||||||
|
let output = $state('')
|
||||||
|
let loading = $state(false);
|
||||||
|
let aspectRatio = 1;
|
||||||
|
|
||||||
|
async function imageTranslate() {
|
||||||
|
if(loading){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loading = true;
|
||||||
|
try {
|
||||||
|
const file = await selectSingleFile(['png', 'jpg', 'jpeg','gif','webp','avif']);
|
||||||
|
if (!file){
|
||||||
|
loading = false;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!ctx){
|
||||||
|
ctx = canvas.getContext('2d');
|
||||||
|
}
|
||||||
|
const img = new Image();
|
||||||
|
inputImage = img;
|
||||||
|
img.src = URL.createObjectURL(new Blob([file.data]));
|
||||||
|
await img.decode();
|
||||||
|
aspectRatio = img.width / img.height;
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const data = canvas.toDataURL('image/png');
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"type": "ARRAY",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"y_min": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"x_min": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"y_max": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"x_max": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"bg_hex_color": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"text_hex_color": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"translation": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"y_min",
|
||||||
|
"x_min",
|
||||||
|
"y_max",
|
||||||
|
"x_max",
|
||||||
|
"content",
|
||||||
|
"translation",
|
||||||
|
"bg_hex_color",
|
||||||
|
"text_hex_color"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const d = await requestChatData({
|
||||||
|
formated: [{
|
||||||
|
role: 'user',
|
||||||
|
content: prompt.replace('{{slot}}', selLang),
|
||||||
|
multimodals: [{
|
||||||
|
type: 'image',
|
||||||
|
base64: data,
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
bias: {},
|
||||||
|
schema: JSON.stringify(schema)
|
||||||
|
}, 'translate')
|
||||||
|
|
||||||
|
if(d.type === 'streaming' || d.type === 'multiline'){
|
||||||
|
loading = false;
|
||||||
|
return alertError('This model is not supported in the playground')
|
||||||
|
}
|
||||||
|
|
||||||
|
if(d.type !== 'success'){
|
||||||
|
alertError(d.result)
|
||||||
|
}
|
||||||
|
|
||||||
|
output = d.result
|
||||||
|
output = JSON.stringify(JSON.parse(d.result), null, 2);
|
||||||
|
loading = false;
|
||||||
|
render()
|
||||||
|
} catch (error) {
|
||||||
|
alertError(JSON.stringify(error))
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function render() {
|
||||||
|
if(!inputImage){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(!ctx){
|
||||||
|
ctx = canvas.getContext('2d');
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
ctx.drawImage(inputImage, 0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
const data = JSON.parse(output);
|
||||||
|
|
||||||
|
for (const item of data) {
|
||||||
|
let [x_min, y_min, x_max, y_max] = [item.x_min, item.y_min, item.x_max, item.y_max];
|
||||||
|
|
||||||
|
if(x_min <= 1){
|
||||||
|
x_min *= canvas.width;
|
||||||
|
y_min *= canvas.height;
|
||||||
|
x_max *= canvas.width;
|
||||||
|
y_max *= canvas.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fillStyle = item.bg_hex_color;
|
||||||
|
ctx.fillRect(x_min, y_min, x_max - x_min, y_max - y_min);
|
||||||
|
// ctx.fillStyle = item.text_hex_color;
|
||||||
|
// ctx.fillText(item.translation, x_min, y_min);
|
||||||
|
|
||||||
|
//make text wrap, and fit the text in the box
|
||||||
|
const text = item.translation;
|
||||||
|
const maxWidth = x_max - x_min;
|
||||||
|
const maxHeight = y_max - y_min;
|
||||||
|
const textSizes = [288, 216, 192, 144, 120, 108, 96, 84, 76, 72, 68, 64, 60, 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 18, 16, 14, 12, 10];
|
||||||
|
let lineHeight = 0;
|
||||||
|
|
||||||
|
for(let i = 0; i < textSizes.length; i++){
|
||||||
|
ctx.font = `${textSizes[i]}px Arial`;
|
||||||
|
lineHeight = textSizes[i] * 1.2;
|
||||||
|
const lines = text.split('\n');
|
||||||
|
let totalHeight = 0;
|
||||||
|
let x = 0
|
||||||
|
for (let n = 0; n < lines.length; n++) {
|
||||||
|
let testLine = lines[n];
|
||||||
|
let metrics = ctx.measureText(testLine);
|
||||||
|
let testWidth = metrics.width;
|
||||||
|
x += testWidth;
|
||||||
|
if(testWidth > maxWidth){
|
||||||
|
totalHeight = maxHeight + 1;
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if(x > maxWidth){
|
||||||
|
totalHeight += lineHeight;
|
||||||
|
x = testWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(x, maxWidth, totalHeight, maxHeight, textSizes[i])
|
||||||
|
if(totalHeight < maxHeight){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let words = text.split(' ');
|
||||||
|
let line = '';
|
||||||
|
let y = y_min + lineHeight;
|
||||||
|
for (let n = 0; n < words.length; n++) {
|
||||||
|
let testLine = line + words[n] + ' ';
|
||||||
|
let metrics = ctx.measureText(testLine);
|
||||||
|
let testWidth = metrics.width;
|
||||||
|
if (testWidth > maxWidth && n > 0) {
|
||||||
|
ctx.fillStyle = item.text_hex_color;
|
||||||
|
ctx.fillText(line, x_min, y);
|
||||||
|
line = words[n] + ' ';
|
||||||
|
y += lineHeight;
|
||||||
|
} else {
|
||||||
|
line = testLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.fillStyle = item.text_hex_color;
|
||||||
|
ctx.fillText(line, x_min, y);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('rendered')
|
||||||
|
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
@@ -13,3 +220,32 @@
|
|||||||
|
|
||||||
<span class="text-textcolor text-lg mt-4">{language.prompt}</span>
|
<span class="text-textcolor text-lg mt-4">{language.prompt}</span>
|
||||||
<TextAreaInput bind:value={prompt} />
|
<TextAreaInput bind:value={prompt} />
|
||||||
|
|
||||||
|
<Button className="mt-4" onclick={imageTranslate}>
|
||||||
|
{language.imageTranslation}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{#if output}
|
||||||
|
<span class="text-textcolor text-lg mt-4">JSON</span>
|
||||||
|
<TextAreaInput bind:value={output} className="overflow-x-auto" onchange={render} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<canvas class="mt-2" bind:this={canvas} class:blur-effect={loading}></canvas>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.blur-effect {
|
||||||
|
filter: blur(5px);
|
||||||
|
animation: blur-animation 1s infinite alternate;
|
||||||
|
}
|
||||||
|
@keyframes blur-animation {
|
||||||
|
0% {
|
||||||
|
filter: blur(5px);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
filter: blur(10px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
filter: blur(5px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
import ToolConvertion from "./ToolConvertion.svelte";
|
import ToolConvertion from "./ToolConvertion.svelte";
|
||||||
import { joinMultiuserRoom } from "src/ts/sync/multiuser";
|
import { joinMultiuserRoom } from "src/ts/sync/multiuser";
|
||||||
import PlaygroundSubtitle from "./PlaygroundSubtitle.svelte";
|
import PlaygroundSubtitle from "./PlaygroundSubtitle.svelte";
|
||||||
|
import PlaygroundImageTrans from "./PlaygroundImageTrans.svelte";
|
||||||
|
|
||||||
let easterEggTouch = $state(0)
|
let easterEggTouch = $state(0)
|
||||||
|
|
||||||
@@ -89,6 +90,11 @@
|
|||||||
}}>
|
}}>
|
||||||
<h1 class="text-2xl font-bold text-start">{language.subtitles}</h1>
|
<h1 class="text-2xl font-bold text-start">{language.subtitles}</h1>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="bg-darkbg rounded-md p-6 flex flex-col transition-shadow hover:ring-1" onclick={() => {
|
||||||
|
PlaygroundStore.set(10)
|
||||||
|
}}>
|
||||||
|
<h1 class="text-2xl font-bold text-start">{language.imageTranslation}</h1>
|
||||||
|
</button>
|
||||||
<button class="bg-darkbg rounded-md p-6 flex flex-col transition-shadow hover:ring-1" onclick={() => {
|
<button class="bg-darkbg rounded-md p-6 flex flex-col transition-shadow hover:ring-1" onclick={() => {
|
||||||
PlaygroundStore.set(101)
|
PlaygroundStore.set(101)
|
||||||
}}>
|
}}>
|
||||||
@@ -148,6 +154,9 @@
|
|||||||
{#if $PlaygroundStore === 9}
|
{#if $PlaygroundStore === 9}
|
||||||
<PlaygroundSubtitle/>
|
<PlaygroundSubtitle/>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if $PlaygroundStore === 10}
|
||||||
|
<PlaygroundImageTrans/>
|
||||||
|
{/if}
|
||||||
{#if $PlaygroundStore === 101}
|
{#if $PlaygroundStore === 101}
|
||||||
<ToolConvertion/>
|
<ToolConvertion/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
import Sortable from "sortablejs";
|
import Sortable from "sortablejs";
|
||||||
import { sleep, sortableOptions } from "src/ts/util";
|
import { sleep, sortableOptions } from "src/ts/util";
|
||||||
import { onDestroy, onMount } from "svelte";
|
import { onDestroy, onMount } from "svelte";
|
||||||
import { PlusIcon } from "lucide-svelte";
|
import { DownloadIcon, FolderUpIcon, PlusIcon } from "lucide-svelte";
|
||||||
|
import { exportRegex, importRegex } from "src/ts/process/scripts";
|
||||||
interface Props {
|
interface Props {
|
||||||
value?: customscript[];
|
value?: customscript[];
|
||||||
buttons?: boolean
|
buttons?: boolean
|
||||||
@@ -78,14 +79,22 @@
|
|||||||
</div>
|
</div>
|
||||||
{/key}
|
{/key}
|
||||||
{#if buttons}
|
{#if buttons}
|
||||||
<button class="w-full mt-2 rounded-md text-textcolor2 hover:text-textcolor focus-within:text-textcolor" onclick={() => {
|
<div class="flex gap-2 mt-2">
|
||||||
value.push({
|
<button class="rounded-md text-textcolor2 hover:text-textcolor focus-within:text-textcolor" onclick={() => {
|
||||||
comment: "",
|
value.push({
|
||||||
in: "",
|
comment: "",
|
||||||
out: "",
|
in: "",
|
||||||
type: "editinput"
|
out: "",
|
||||||
})
|
type: "editinput"
|
||||||
}}>
|
})
|
||||||
<PlusIcon />
|
}}>
|
||||||
</button>
|
<PlusIcon />
|
||||||
|
</button>
|
||||||
|
<button class="rounded-md text-textcolor2 hover:text-textcolor focus-within:text-textcolor" onclick={() => {
|
||||||
|
exportRegex(value)
|
||||||
|
}}><DownloadIcon /></button>
|
||||||
|
<button class="rounded-md text-textcolor2 hover:text-textcolor focus-within:text-textcolor" onclick={async () => {
|
||||||
|
value = await importRegex(value)
|
||||||
|
}}><FolderUpIcon /></button>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -547,10 +547,40 @@ export async function loadData() {
|
|||||||
else{
|
else{
|
||||||
await forageStorage.Init()
|
await forageStorage.Init()
|
||||||
|
|
||||||
|
LoadingStatusState.text = "Loading Local Save File..."
|
||||||
|
let gotStorage:Uint8Array = await forageStorage.getItem('database/database.bin') as unknown as Uint8Array
|
||||||
|
LoadingStatusState.text = "Decoding Local Save File..."
|
||||||
|
if(checkNullish(gotStorage)){
|
||||||
|
gotStorage = encodeRisuSaveLegacy({})
|
||||||
|
await forageStorage.setItem('database/database.bin', gotStorage)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const decoded = await decodeRisuSave(gotStorage)
|
||||||
|
console.log(decoded)
|
||||||
|
setDatabase(decoded)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
const backups = await getDbBackups()
|
||||||
|
let backupLoaded = false
|
||||||
|
for(const backup of backups){
|
||||||
|
try {
|
||||||
|
LoadingStatusState.text = `Reading Backup File ${backup}...`
|
||||||
|
const backupData:Uint8Array = await forageStorage.getItem(`database/dbbackup-${backup}.bin`) as unknown as Uint8Array
|
||||||
|
setDatabase(
|
||||||
|
await decodeRisuSave(backupData)
|
||||||
|
)
|
||||||
|
backupLoaded = true
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
if(!backupLoaded){
|
||||||
|
throw "Your save file is corrupted"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(await forageStorage.checkAccountSync()){
|
if(await forageStorage.checkAccountSync()){
|
||||||
LoadingStatusState.text = "Checking Account Sync..."
|
LoadingStatusState.text = "Checking Account Sync..."
|
||||||
let gotStorage:Uint8Array = await (forageStorage.realStorage as AccountStorage).getItem('database/database.bin', (v) => {
|
let gotStorage:Uint8Array = await (forageStorage.realStorage as AccountStorage).getItem('database/database.bin', (v) => {
|
||||||
LoadingStatusState.text = `Loading Save File ${(v*100).toFixed(2)}%`
|
LoadingStatusState.text = `Loading Remote Save File ${(v*100).toFixed(2)}%`
|
||||||
})
|
})
|
||||||
if(checkNullish(gotStorage)){
|
if(checkNullish(gotStorage)){
|
||||||
gotStorage = encodeRisuSaveLegacy({})
|
gotStorage = encodeRisuSaveLegacy({})
|
||||||
@@ -578,37 +608,8 @@ export async function loadData() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
LoadingStatusState.text = "Rechecking Account Sync..."
|
||||||
LoadingStatusState.text = "Loading Save File..."
|
await forageStorage.checkAccountSync()
|
||||||
let gotStorage:Uint8Array = await forageStorage.getItem('database/database.bin') as unknown as Uint8Array
|
|
||||||
LoadingStatusState.text = "Decoding Save File..."
|
|
||||||
if(checkNullish(gotStorage)){
|
|
||||||
gotStorage = encodeRisuSaveLegacy({})
|
|
||||||
await forageStorage.setItem('database/database.bin', gotStorage)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const decoded = await decodeRisuSave(gotStorage)
|
|
||||||
console.log(decoded)
|
|
||||||
setDatabase(decoded)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
const backups = await getDbBackups()
|
|
||||||
let backupLoaded = false
|
|
||||||
for(const backup of backups){
|
|
||||||
try {
|
|
||||||
LoadingStatusState.text = `Reading Backup File ${backup}...`
|
|
||||||
const backupData:Uint8Array = await forageStorage.getItem(`database/dbbackup-${backup}.bin`) as unknown as Uint8Array
|
|
||||||
setDatabase(
|
|
||||||
await decodeRisuSave(backupData)
|
|
||||||
)
|
|
||||||
backupLoaded = true
|
|
||||||
} catch (error) {}
|
|
||||||
}
|
|
||||||
if(!backupLoaded){
|
|
||||||
throw "Your save file is corrupted"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LoadingStatusState.text = "Checking Drive Sync..."
|
LoadingStatusState.text = "Checking Drive Sync..."
|
||||||
const isDriverMode = await checkDriverInit()
|
const isDriverMode = await checkDriverInit()
|
||||||
if(isDriverMode){
|
if(isDriverMode){
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {createParser} from 'eventsource-parser'
|
|||||||
import {Ollama} from 'ollama/dist/browser.mjs'
|
import {Ollama} from 'ollama/dist/browser.mjs'
|
||||||
import { applyChatTemplate } from "./templates/chatTemplate";
|
import { applyChatTemplate } from "./templates/chatTemplate";
|
||||||
import { OobaParams } from "./prompt";
|
import { OobaParams } from "./prompt";
|
||||||
import { extractJSON, getOpenAIJSONSchema } from "./templates/jsonSchema";
|
import { extractJSON, getGeneralJSONSchema, getOpenAIJSONSchema } from "./templates/jsonSchema";
|
||||||
import { getModelInfo, LLMFlags, LLMFormat, type LLMModel } from "../model/modellist";
|
import { getModelInfo, LLMFlags, LLMFormat, type LLMModel } from "../model/modellist";
|
||||||
|
|
||||||
|
|
||||||
@@ -39,6 +39,8 @@ interface requestDataArgument{
|
|||||||
continue?:boolean
|
continue?:boolean
|
||||||
chatId?:string
|
chatId?:string
|
||||||
noMultiGen?:boolean
|
noMultiGen?:boolean
|
||||||
|
schema?:string
|
||||||
|
extractJson?:string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RequestDataArgumentExtended extends requestDataArgument{
|
interface RequestDataArgumentExtended extends requestDataArgument{
|
||||||
@@ -357,6 +359,7 @@ export async function requestChatDataMain(arg:requestDataArgument, model:ModelMo
|
|||||||
targ.abortSignal = abortSignal
|
targ.abortSignal = abortSignal
|
||||||
targ.modelInfo = getModelInfo(targ.aiModel)
|
targ.modelInfo = getModelInfo(targ.aiModel)
|
||||||
targ.mode = model
|
targ.mode = model
|
||||||
|
targ.extractJson = arg.extractJson ?? db.extractJson
|
||||||
if(targ.aiModel === 'reverse_proxy'){
|
if(targ.aiModel === 'reverse_proxy'){
|
||||||
targ.modelInfo.internalID = db.customProxyRequestModel
|
targ.modelInfo.internalID = db.customProxyRequestModel
|
||||||
targ.modelInfo.format = db.customAPIFormat
|
targ.modelInfo.format = db.customAPIFormat
|
||||||
@@ -694,10 +697,10 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise<requestDat
|
|||||||
body.seed = db.generationSeed
|
body.seed = db.generationSeed
|
||||||
}
|
}
|
||||||
|
|
||||||
if(db.jsonSchemaEnabled){
|
if(db.jsonSchemaEnabled || arg.schema){
|
||||||
body.response_format = {
|
body.response_format = {
|
||||||
"type": "json_schema",
|
"type": "json_schema",
|
||||||
"json_schema": getOpenAIJSONSchema()
|
"json_schema": getOpenAIJSONSchema(arg.schema)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -862,9 +865,9 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise<requestDat
|
|||||||
try {
|
try {
|
||||||
const rawChunk = data.replace("data: ", "")
|
const rawChunk = data.replace("data: ", "")
|
||||||
if(rawChunk === "[DONE]"){
|
if(rawChunk === "[DONE]"){
|
||||||
if(db.extractJson && db.jsonSchemaEnabled){
|
if(arg.extractJson && (db.jsonSchemaEnabled || arg.schema)){
|
||||||
for(const key in readed){
|
for(const key in readed){
|
||||||
const extracted = extractJSON(readed[key], db.extractJson)
|
const extracted = extractJSON(readed[key], arg.extractJson)
|
||||||
JSONreaded[key] = extracted
|
JSONreaded[key] = extracted
|
||||||
}
|
}
|
||||||
console.log(JSONreaded)
|
console.log(JSONreaded)
|
||||||
@@ -897,9 +900,9 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise<requestDat
|
|||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(db.extractJson && db.jsonSchemaEnabled){
|
if(arg.extractJson && (db.jsonSchemaEnabled || arg.schema)){
|
||||||
for(const key in readed){
|
for(const key in readed){
|
||||||
const extracted = extractJSON(readed[key], db.extractJson)
|
const extracted = extractJSON(readed[key], arg.extractJson)
|
||||||
JSONreaded[key] = extracted
|
JSONreaded[key] = extracted
|
||||||
}
|
}
|
||||||
console.log(JSONreaded)
|
console.log(JSONreaded)
|
||||||
@@ -974,12 +977,12 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise<requestDat
|
|||||||
if(res.ok){
|
if(res.ok){
|
||||||
try {
|
try {
|
||||||
if(arg.multiGen && dat.choices){
|
if(arg.multiGen && dat.choices){
|
||||||
if(db.extractJson && db.jsonSchemaEnabled){
|
if(arg.extractJson && (db.jsonSchemaEnabled || arg.schema)){
|
||||||
|
|
||||||
const c = dat.choices.map((v:{
|
const c = dat.choices.map((v:{
|
||||||
message:{content:string}
|
message:{content:string}
|
||||||
}) => {
|
}) => {
|
||||||
const extracted = extractJSON(v.message.content, db.extractJson)
|
const extracted = extractJSON(v.message.content, arg.extractJson)
|
||||||
return ["char",extracted]
|
return ["char",extracted]
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -999,10 +1002,10 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise<requestDat
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(dat?.choices[0]?.text){
|
if(dat?.choices[0]?.text){
|
||||||
if(db.extractJson && db.jsonSchemaEnabled){
|
if(arg.extractJson && (db.jsonSchemaEnabled || arg.schema)){
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(dat.choices[0].text)
|
const parsed = JSON.parse(dat.choices[0].text)
|
||||||
const extracted = extractJSON(parsed, db.extractJson)
|
const extracted = extractJSON(parsed, arg.extractJson)
|
||||||
return {
|
return {
|
||||||
type: 'success',
|
type: 'success',
|
||||||
result: extracted
|
result: extracted
|
||||||
@@ -1020,10 +1023,10 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise<requestDat
|
|||||||
result: dat.choices[0].text
|
result: dat.choices[0].text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(db.extractJson && db.jsonSchemaEnabled){
|
if(arg.extractJson && (db.jsonSchemaEnabled || arg.schema)){
|
||||||
return {
|
return {
|
||||||
type: 'success',
|
type: 'success',
|
||||||
result: extractJSON(dat.choices[0].message.content, db.extractJson)
|
result: extractJSON(dat.choices[0].message.content, arg.extractJson)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const msg:OpenAIChatFull = (dat.choices[0].message)
|
const msg:OpenAIChatFull = (dat.choices[0].message)
|
||||||
@@ -1693,6 +1696,13 @@ async function requestGoogleCloudVertex(arg:RequestDataArgumentExtended):Promise
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(db.jsonSchemaEnabled || arg.schema){
|
||||||
|
body.generation_config.response_mime_type = "application/json"
|
||||||
|
body.generation_config.response_schema = getGeneralJSONSchema(arg.schema, ['$schema','additionalProperties'])
|
||||||
|
console.log(body.generation_config.response_schema)
|
||||||
|
}
|
||||||
|
|
||||||
let url = ''
|
let url = ''
|
||||||
|
|
||||||
if(arg.customURL){
|
if(arg.customURL){
|
||||||
@@ -1756,6 +1766,13 @@ async function requestGoogleCloudVertex(arg:RequestDataArgumentExtended):Promise
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(arg.extractJson && (db.jsonSchemaEnabled || arg.schema)){
|
||||||
|
for(let i=0;i<rDatas.length;i++){
|
||||||
|
const extracted = extractJSON(rDatas[i], arg.extractJson)
|
||||||
|
rDatas[i] = extracted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(rDatas.length > 1){
|
if(rDatas.length > 1){
|
||||||
const thought = rDatas.splice(rDatas.length-2, 1)[0]
|
const thought = rDatas.splice(rDatas.length-2, 1)[0]
|
||||||
rDatas[rDatas.length-1] = `<Thoughts>${thought}</Thoughts>\n\n${rDatas.join('\n\n')}`
|
rDatas[rDatas.length-1] = `<Thoughts>${thought}</Thoughts>\n\n${rDatas.join('\n\n')}`
|
||||||
@@ -1827,6 +1844,12 @@ async function requestGoogleCloudVertex(arg:RequestDataArgumentExtended):Promise
|
|||||||
processDataItem(res.data)
|
processDataItem(res.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(arg.extractJson && (db.jsonSchemaEnabled || arg.schema)){
|
||||||
|
for(let i=0;i<rDatas.length;i++){
|
||||||
|
const extracted = extractJSON(rDatas[i], arg.extractJson)
|
||||||
|
rDatas[i] = extracted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(rDatas.length > 1){
|
if(rDatas.length > 1){
|
||||||
const thought = rDatas.splice(rDatas.length-2, 1)[0]
|
const thought = rDatas.splice(rDatas.length-2, 1)[0]
|
||||||
@@ -2539,7 +2562,7 @@ async function requestClaude(arg:RequestDataArgumentExtended):Promise<requestDat
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
text += "Error:" + JSON.parse(e.data).error?.message
|
text += "Error:" + JSON.parse(e.data).error?.message
|
||||||
if(db.extractJson && db.jsonSchemaEnabled){
|
if(arg.extractJson && (db.jsonSchemaEnabled || arg.schema)){
|
||||||
controller.enqueue({
|
controller.enqueue({
|
||||||
"0": extractJSON(text, db.jsonSchema)
|
"0": extractJSON(text, db.jsonSchema)
|
||||||
})
|
})
|
||||||
@@ -2618,7 +2641,7 @@ async function requestClaude(arg:RequestDataArgumentExtended):Promise<requestDat
|
|||||||
result: JSON.stringify(res.data)
|
result: JSON.stringify(res.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(db.extractJson && db.jsonSchemaEnabled){
|
if(arg.extractJson && db.jsonSchemaEnabled){
|
||||||
return {
|
return {
|
||||||
type: 'success',
|
type: 'success',
|
||||||
result: extractJSON(resText, db.jsonSchema)
|
result: extractJSON(resText, db.jsonSchema)
|
||||||
|
|||||||
@@ -522,7 +522,6 @@ export async function generateAIImage(genPrompt:string, currentChar:character, n
|
|||||||
if(!auth){
|
if(!auth){
|
||||||
db.account = JSON.parse(localStorage.getItem("fallbackRisuToken"))
|
db.account = JSON.parse(localStorage.getItem("fallbackRisuToken"))
|
||||||
auth = db?.account?.token
|
auth = db?.account?.token
|
||||||
db.account.useSync = true
|
|
||||||
}
|
}
|
||||||
const da = await globalFetch(keiServerURL() + '/imaggen', {
|
const da = await globalFetch(keiServerURL() + '/imaggen', {
|
||||||
body: {
|
body: {
|
||||||
|
|||||||
@@ -120,14 +120,33 @@ export function convertInterfaceToSchema(int:string){
|
|||||||
return schema
|
return schema
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOpenAIJSONSchema(){
|
export function getOpenAIJSONSchema(schema?:string){
|
||||||
const db = getDatabase()
|
const db = getDatabase()
|
||||||
const schema = {
|
return {
|
||||||
"name": "format",
|
"name": "format",
|
||||||
"strict": db.strictJsonSchema,
|
"strict": db.strictJsonSchema,
|
||||||
"schema": convertInterfaceToSchema(db.jsonSchema)
|
"schema": convertInterfaceToSchema(schema ?? db.jsonSchema)
|
||||||
}
|
}
|
||||||
return schema
|
}
|
||||||
|
|
||||||
|
export function getGeneralJSONSchema(schema?:string, excludes:string[] = []){
|
||||||
|
const db = getDatabase()
|
||||||
|
|
||||||
|
function process(data:any){
|
||||||
|
const keys = Object.keys(data)
|
||||||
|
for(const key of keys){
|
||||||
|
if(excludes.includes(key)){
|
||||||
|
delete data[key]
|
||||||
|
}
|
||||||
|
if(typeof data[key] === 'object'){
|
||||||
|
data[key] = process(data[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
const d = convertInterfaceToSchema(schema ?? db.jsonSchema)
|
||||||
|
return process(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function extractJSON(data:string, format:string){
|
export function extractJSON(data:string, format:string){
|
||||||
|
|||||||
@@ -176,9 +176,11 @@ export class AccountStorage{
|
|||||||
const db = getDatabase()
|
const db = getDatabase()
|
||||||
this.auth = db?.account?.token
|
this.auth = db?.account?.token
|
||||||
if(!this.auth){
|
if(!this.auth){
|
||||||
db.account = JSON.parse(localStorage.getItem("fallbackRisuToken"))
|
try {
|
||||||
this.auth = db?.account?.token
|
db.account = JSON.parse(localStorage.getItem("fallbackRisuToken"))
|
||||||
db.account.useSync = true
|
this.auth = db?.account?.token
|
||||||
|
db.account.useSync = true
|
||||||
|
} catch (error) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export class AutoStorage{
|
|||||||
if(localStorage.getItem('dosync') === 'avoid'){
|
if(localStorage.getItem('dosync') === 'avoid'){
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if((localStorage.getItem('dosync') === 'sync' || db.account?.useSync) && (localStorage.getItem('accountst') !== 'able')){
|
if((localStorage.getItem('dosync') === 'sync' || db?.account?.useSync) && (localStorage.getItem('accountst') !== 'able')){
|
||||||
const keys = await this.realStorage.keys()
|
const keys = await this.realStorage.keys()
|
||||||
let i = 0;
|
let i = 0;
|
||||||
const accountStorage = new AccountStorage()
|
const accountStorage = new AccountStorage()
|
||||||
|
|||||||
@@ -312,6 +312,11 @@ export class ChatTokenizer {
|
|||||||
encoded += await this.tokenizeMultiModal(multimodal)
|
encoded += await this.tokenizeMultiModal(multimodal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(data.thoughts && data.thoughts.length > 0){
|
||||||
|
for(const thought of data.thoughts){
|
||||||
|
encoded += (await encode(thought)).length + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
return encoded
|
return encoded
|
||||||
}
|
}
|
||||||
async tokenizeChats(data:OpenAIChat[]){
|
async tokenizeChats(data:OpenAIChat[]){
|
||||||
|
|||||||
Reference in New Issue
Block a user