Add JSON Schema
This commit is contained in:
@@ -22,6 +22,7 @@ import {createParser} from 'eventsource-parser'
|
||||
import {Ollama} from 'ollama/dist/browser.mjs'
|
||||
import { applyChatTemplate } from "./templates/chatTemplate";
|
||||
import { OobaParams } from "./prompt";
|
||||
import { extractJSON, getOpenAIJSONSchema } from "./templates/jsonSchema";
|
||||
|
||||
|
||||
|
||||
@@ -485,13 +486,9 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model'
|
||||
: (!requestModel) ? 'gpt-3.5-turbo'
|
||||
: requestModel,
|
||||
messages: formatedChat,
|
||||
// temperature: temperature,
|
||||
max_tokens: maxTokens,
|
||||
// presence_penalty: arg.PresensePenalty || (db.PresensePenalty / 100),
|
||||
// frequency_penalty: arg.frequencyPenalty || (db.frequencyPenalty / 100),
|
||||
logit_bias: bias,
|
||||
stream: false,
|
||||
// top_p: db.top_p,
|
||||
|
||||
})
|
||||
|
||||
@@ -503,6 +500,13 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model'
|
||||
body.user = getOpenUserString()
|
||||
}
|
||||
|
||||
if(db.jsonSchemaEnabled){
|
||||
body.response_format = {
|
||||
"type": "json_schema",
|
||||
"json_schema": getOpenAIJSONSchema()
|
||||
}
|
||||
}
|
||||
|
||||
if(aiModel === 'openrouter'){
|
||||
if(db.openrouterFallback){
|
||||
body.route = "fallback"
|
||||
@@ -641,6 +645,7 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model'
|
||||
const transtream = new TransformStream<Uint8Array, StreamResponseChunk>( {
|
||||
async transform(chunk, control) {
|
||||
dataUint = Buffer.from(new Uint8Array([...dataUint, ...chunk]))
|
||||
let JSONreaded:{[key:string]:string} = {}
|
||||
try {
|
||||
const datas = dataUint.toString().split('\n')
|
||||
let readed:{[key:string]:string} = {}
|
||||
@@ -649,7 +654,17 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model'
|
||||
try {
|
||||
const rawChunk = data.replace("data: ", "")
|
||||
if(rawChunk === "[DONE]"){
|
||||
control.enqueue(readed)
|
||||
if(db.extractJson && db.jsonSchemaEnabled){
|
||||
for(const key in readed){
|
||||
const extracted = extractJSON(readed[key], db.extractJson)
|
||||
JSONreaded[key] = extracted
|
||||
}
|
||||
console.log(JSONreaded)
|
||||
control.enqueue(JSONreaded)
|
||||
}
|
||||
else{
|
||||
control.enqueue(readed)
|
||||
}
|
||||
return
|
||||
}
|
||||
const choices = JSON.parse(rawChunk).choices
|
||||
@@ -674,7 +689,17 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model'
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
control.enqueue(readed)
|
||||
if(db.extractJson && db.jsonSchemaEnabled){
|
||||
for(const key in readed){
|
||||
const extracted = extractJSON(readed[key], db.extractJson)
|
||||
JSONreaded[key] = extracted
|
||||
}
|
||||
console.log(JSONreaded)
|
||||
control.enqueue(JSONreaded)
|
||||
}
|
||||
else{
|
||||
control.enqueue(readed)
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
@@ -741,6 +766,21 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model'
|
||||
if(res.ok){
|
||||
try {
|
||||
if(multiGen && dat.choices){
|
||||
if(db.extractJson && db.jsonSchemaEnabled){
|
||||
|
||||
const c = dat.choices.map((v:{
|
||||
message:{content:string}
|
||||
}) => {
|
||||
const extracted = extractJSON(v.message.content, db.extractJson)
|
||||
return ["char",extracted]
|
||||
})
|
||||
|
||||
return {
|
||||
type: 'multiline',
|
||||
result: c
|
||||
}
|
||||
|
||||
}
|
||||
return {
|
||||
type: 'multiline',
|
||||
result: dat.choices.map((v) => {
|
||||
@@ -751,11 +791,33 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model'
|
||||
}
|
||||
|
||||
if(dat?.choices[0]?.text){
|
||||
if(db.extractJson && db.jsonSchemaEnabled){
|
||||
try {
|
||||
const parsed = JSON.parse(dat.choices[0].text)
|
||||
const extracted = extractJSON(parsed, db.extractJson)
|
||||
return {
|
||||
type: 'success',
|
||||
result: extracted
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return {
|
||||
type: 'success',
|
||||
result: dat.choices[0].text
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: 'success',
|
||||
result: dat.choices[0].text
|
||||
}
|
||||
}
|
||||
if(db.extractJson && db.jsonSchemaEnabled){
|
||||
return {
|
||||
type: 'success',
|
||||
result: extractJSON(dat.choices[0].message.content, db.extractJson)
|
||||
}
|
||||
}
|
||||
const msg:OpenAIChatFull = (dat.choices[0].message)
|
||||
return {
|
||||
type: 'success',
|
||||
|
||||
@@ -144,7 +144,6 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter
|
||||
const matchAll = isGlobal ? data.matchAll(reg) : [data.match(reg)]
|
||||
data = data.replace(reg, "")
|
||||
for(const matched of matchAll){
|
||||
console.log(matched)
|
||||
if(matched){
|
||||
const inData = matched[0]
|
||||
let out = outScript.replace('@@move_top ', '').replace('@@move_bottom ', '')
|
||||
@@ -163,7 +162,6 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter
|
||||
}
|
||||
return v
|
||||
})
|
||||
console.log(out)
|
||||
if(outScript.startsWith('@@move_top') || pscript.actions.includes('move_top')){
|
||||
data = out + '\n' +data
|
||||
}
|
||||
@@ -257,8 +255,6 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter
|
||||
})
|
||||
}
|
||||
|
||||
console.log(parsedScripts)
|
||||
|
||||
if(orderChanged){
|
||||
parsedScripts.sort((a, b) => b.order - a.order) //sort by order
|
||||
}
|
||||
|
||||
172
src/ts/process/templates/jsonSchema.ts
Normal file
172
src/ts/process/templates/jsonSchema.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
import { risuChatParser } from "src/ts/parser"
|
||||
import { DataBase } from "src/ts/storage/database"
|
||||
import { get } from "svelte/store"
|
||||
|
||||
export function convertInterfaceToSchema(int:string){
|
||||
if(!int.startsWith('interface ') && !int.startsWith('export interface ')){
|
||||
return JSON.parse(int)
|
||||
}
|
||||
|
||||
int = risuChatParser(int)
|
||||
|
||||
type SchemaProp = {
|
||||
"type": "array"|"string"|"number"|"boolean",
|
||||
"items"?:SchemaProp
|
||||
"enum"?:string[]
|
||||
"const"?:string
|
||||
}
|
||||
|
||||
const lines = int.split('\n')
|
||||
let schema = {
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {} as {[key:string]:SchemaProp},
|
||||
"required": [] as string[],
|
||||
}
|
||||
for(let i = 1; i < lines.length; i++){
|
||||
let content = lines[i].trim()
|
||||
if(content === '{'){
|
||||
continue
|
||||
}
|
||||
if(content === '}'){
|
||||
continue
|
||||
}
|
||||
if(content === ''){
|
||||
continue
|
||||
}
|
||||
|
||||
let placeHolders:string[] = []
|
||||
|
||||
content = content
|
||||
.replace(/\\"/gu, '\uE9b4a')
|
||||
.replace(/\\'/gu, '\uE9b4b')
|
||||
.replace(/"(.+?)"/gu, function(match, p1){
|
||||
placeHolders.push(match)
|
||||
return `\uE9b4d${placeHolders.length - 1}`
|
||||
})
|
||||
.replace(/'(.+?)'/gu, function(match, p1){
|
||||
placeHolders.push(`"${p1}"`)
|
||||
return `\uE9b4d${placeHolders.length - 1}`
|
||||
})
|
||||
|
||||
.split('//')[0].trim() //remove comments
|
||||
|
||||
.replace(/((number)|(string)|(boolean))\[\]/gu, 'Array<$1>')
|
||||
|
||||
if(content.endsWith(',') || content.endsWith(';')){
|
||||
content = content.slice(0, -1)
|
||||
}
|
||||
|
||||
let spData = content.replace(/ /g, '').split(':')
|
||||
|
||||
if(spData.length !== 2){
|
||||
throw "SyntaxError Found"
|
||||
}
|
||||
|
||||
let [property,typeData] = spData
|
||||
|
||||
switch(typeData){
|
||||
case 'string':
|
||||
case 'number':
|
||||
case 'boolean':{
|
||||
schema.properties[property] = {
|
||||
type: typeData
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'Array<string>':
|
||||
case 'Array<number>':
|
||||
case 'Array<boolean>':{
|
||||
const ogType = typeData.slice(6,-1)
|
||||
|
||||
schema.properties[property] = {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: ogType as 'string'|'number'|'boolean'
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
default:{
|
||||
const types = typeData.split("|")
|
||||
const strings:string[] = []
|
||||
for(const t of types){
|
||||
if(!t.startsWith('\uE9b4d')){
|
||||
throw "Unsupported Type Detected"
|
||||
}
|
||||
const textIndex = t.replace('\uE9b4d','')
|
||||
const text = placeHolders[parseInt(textIndex)]
|
||||
const textParsed = JSON.parse(text.replace(/\uE9b4a/gu, '\\"').replace(/\uE9b4b/gu, "\\'"))
|
||||
strings.push(textParsed)
|
||||
}
|
||||
if(strings.length === 1){
|
||||
schema.properties[property] = {
|
||||
type: 'string',
|
||||
const: strings[0]
|
||||
}
|
||||
}
|
||||
else{
|
||||
schema.properties[property] = {
|
||||
type: 'string',
|
||||
enum: strings
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
schema.required.push(property)
|
||||
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
||||
export function getOpenAIJSONSchema(){
|
||||
const db = get(DataBase)
|
||||
const schema = {
|
||||
"name": "format",
|
||||
"strict": db.strictJsonSchema,
|
||||
"schema": convertInterfaceToSchema(db.jsonSchema)
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
||||
export function extractJSON(data:string, format:string){
|
||||
const extract = (data:any, format:string) => {
|
||||
try {
|
||||
if(data === undefined || data === null){
|
||||
return ''
|
||||
}
|
||||
|
||||
const fp = format.split('.')
|
||||
const current = data[fp[0]]
|
||||
|
||||
if(current === undefined){
|
||||
return ''
|
||||
}
|
||||
else if(fp.length === 1){
|
||||
return `${current ?? ''}`
|
||||
}
|
||||
else if(typeof current === 'object'){
|
||||
return extractJSON(current, fp.slice(1).join('.'))
|
||||
}
|
||||
else if(Array.isArray(current)){
|
||||
const index = parseInt(fp[1])
|
||||
return extractJSON(current[index], fp.slice(1).join('.'))
|
||||
}
|
||||
else{
|
||||
return `${current ?? ''}`
|
||||
}
|
||||
} catch (error) {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
try {
|
||||
format = risuChatParser(format)
|
||||
data = data.trim()
|
||||
if(data.startsWith('{')){
|
||||
return extract(JSON.parse(data), format)
|
||||
}
|
||||
} catch (error) {}
|
||||
return data
|
||||
}
|
||||
@@ -435,6 +435,7 @@ export function setDatabase(data:Database){
|
||||
data.falModel ??= 'fal-ai/flux/dev'
|
||||
data.falLoraScale ??= 1
|
||||
data.customCSS ??= ''
|
||||
data.strictJsonSchema ??= true
|
||||
changeLanguage(data.language)
|
||||
DataBase.set(data)
|
||||
}
|
||||
@@ -729,6 +730,10 @@ export interface Database{
|
||||
moduleIntergration: string
|
||||
customCSS: string
|
||||
betaMobileGUI:boolean
|
||||
jsonSchemaEnabled:boolean
|
||||
jsonSchema:string
|
||||
strictJsonSchema:boolean
|
||||
extractJson:string
|
||||
}
|
||||
|
||||
export interface customscript{
|
||||
|
||||
Reference in New Issue
Block a user