From 701dd0ac8c5e33a01a454dcbe5938719c754f8fe Mon Sep 17 00:00:00 2001 From: kwaroran Date: Thu, 7 Mar 2024 23:28:54 +0900 Subject: [PATCH 1/7] Add null check in smMatcher function --- src/ts/parser.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ts/parser.ts b/src/ts/parser.ts index 946abd1a..17f7935c 100644 --- a/src/ts/parser.ts +++ b/src/ts/parser.ts @@ -737,6 +737,9 @@ const matcher = (p1:string,matcherArg:matcherArg) => { } const smMatcher = (p1:string,matcherArg:matcherArg) => { + if(!p1){ + return null + } const lowerCased = p1.toLocaleLowerCase() const db = matcherArg.db const chara = matcherArg.chara From 62f9501f19e7e357cc7bcef14a5ee40306fe3416 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Fri, 8 Mar 2024 00:00:59 +0900 Subject: [PATCH 2/7] Refactor chat message removal logic --- src/lib/ChatScreens/Chat.svelte | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib/ChatScreens/Chat.svelte b/src/lib/ChatScreens/Chat.svelte index b9199bec..ca7d3e76 100644 --- a/src/lib/ChatScreens/Chat.svelte +++ b/src/lib/ChatScreens/Chat.svelte @@ -31,7 +31,14 @@ let msgDisplay = '' let translated = get(DataBase).autoTranslate - async function rm(){ + async function rm(e:MouseEvent){ + if(e.shiftKey){ + let msg = $CurrentChat.message + msg = msg.slice(0, idx) + $CurrentChat.message = msg + return + } + const rm = $DataBase.askRemoval ? await alertConfirm(language.removeChat) : true if(rm){ if($DataBase.instantRemove){ From 17a7905b11402ea21d28875641b45547852b8cfb Mon Sep 17 00:00:00 2001 From: kwaroran Date: Fri, 8 Mar 2024 04:11:48 +0900 Subject: [PATCH 3/7] make tauri fetchNative wait until init --- src/ts/storage/globalApi.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ts/storage/globalApi.ts b/src/ts/storage/globalApi.ts index 99a33491..142592eb 100644 --- a/src/ts/storage/globalApi.ts +++ b/src/ts/storage/globalApi.ts @@ -1302,6 +1302,7 @@ interface StreamedFetchEndData{ } type StreamedFetchChunk = StreamedFetchChunkData|StreamedFetchHeaderData|StreamedFetchEndData +let streamedFetchListening = false listen('streamed_fetch', (event) => { try { @@ -1311,6 +1312,8 @@ listen('streamed_fetch', (event) => { } catch (error) { console.error(error) } +}).then((v) => { + streamedFetchListening = true }) export async function fetchNative(url:string, arg:{ @@ -1336,6 +1339,9 @@ export async function fetchNative(url:string, arg:{ let resolved = false let error = '' + while(!streamedFetchListening){ + await sleep(100) + } invoke('streamed_fetch', { id: fetchId, url: url, From 633a224bb6aa1354a055b1b07f4cf47cba78c952 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sat, 9 Mar 2024 05:52:18 +0900 Subject: [PATCH 4/7] AddNative StreamedHTTP to Android --- android/app/src/main/java/co/aiclient/risu/StreamedPlugin.java | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 android/app/src/main/java/co/aiclient/risu/StreamedPlugin.java diff --git a/android/app/src/main/java/co/aiclient/risu/StreamedPlugin.java b/android/app/src/main/java/co/aiclient/risu/StreamedPlugin.java new file mode 100644 index 00000000..2c0cc316 --- /dev/null +++ b/android/app/src/main/java/co/aiclient/risu/StreamedPlugin.java @@ -0,0 +1,2 @@ +package co.aiclient.risu;public class StreamedPlugin { +} From 31da44d31039ab5ec5fd6c545be19445420aaa45 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sat, 9 Mar 2024 05:53:30 +0900 Subject: [PATCH 5/7] Add Native streamed http functionality --- .../java/co/aiclient/risu/MainActivity.java | 10 +- .../java/co/aiclient/risu/StreamedPlugin.java | 119 +++++++++++++++++- android/build.gradle | 2 +- src/ts/storage/globalApi.ts | 92 +++++++++----- 4 files changed, 191 insertions(+), 32 deletions(-) diff --git a/android/app/src/main/java/co/aiclient/risu/MainActivity.java b/android/app/src/main/java/co/aiclient/risu/MainActivity.java index da3a5923..ebbee244 100644 --- a/android/app/src/main/java/co/aiclient/risu/MainActivity.java +++ b/android/app/src/main/java/co/aiclient/risu/MainActivity.java @@ -1,5 +1,11 @@ package co.aiclient.risu; - +import android.os.Bundle; import com.getcapacitor.BridgeActivity; -public class MainActivity extends BridgeActivity {} +public class MainActivity extends BridgeActivity { + @Override + public void onCreate(Bundle savedInstanceState) { + registerPlugin(StreamedPlugin.class); + super.onCreate(savedInstanceState); + } +} diff --git a/android/app/src/main/java/co/aiclient/risu/StreamedPlugin.java b/android/app/src/main/java/co/aiclient/risu/StreamedPlugin.java index 2c0cc316..6aa3cff1 100644 --- a/android/app/src/main/java/co/aiclient/risu/StreamedPlugin.java +++ b/android/app/src/main/java/co/aiclient/risu/StreamedPlugin.java @@ -1,2 +1,119 @@ -package co.aiclient.risu;public class StreamedPlugin { +package co.aiclient.risu; + +import android.util.Base64; + +import com.getcapacitor.JSObject; +import com.getcapacitor.Plugin; +import com.getcapacitor.PluginCall; +import com.getcapacitor.PluginMethod; +import com.getcapacitor.annotation.CapacitorPlugin; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Iterator; + +@CapacitorPlugin(name = "streamedFetch") +public class StreamedPlugin extends Plugin { + + + + @PluginMethod() + public void streamedFetch(PluginCall call) { + String id = call.getString("id"); + String urlParam = call.getString("url"); + String bodyString = call.getString("body"); + JSObject headers = call.getObject("headers"); + + + URL url = null; + + try { + url = new URL(urlParam); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + byte[] bodyEncodedByte = bodyString.getBytes("UTF-8"); + byte[] bodyByte = Base64.decode(bodyEncodedByte, Base64.DEFAULT); + Iterator keys = headers.keys(); + urlConnection.setRequestMethod("POST"); + while(keys.hasNext()) { + String key = keys.next(); + if (headers.get(key) instanceof JSONObject) { + urlConnection.setRequestProperty(key, headers.getString(key)); + } + } + urlConnection.setRequestProperty("Content-Length", String.valueOf(bodyByte.length)); + urlConnection.setDoInput(true); + OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream()); + out.write(bodyByte); + try { + InputStream in = new BufferedInputStream(urlConnection.getInputStream()); + int resCode = urlConnection.getResponseCode(); + JSObject resObj = new JSObject(); + JSObject headerObj = new JSObject(); + resObj.put("id", id); + resObj.put("type", "headers"); + resObj.put("status", resCode); + + int i = 0; + while (true){ + String headerName = urlConnection.getHeaderFieldKey(i); + String headerValue = urlConnection.getHeaderField(i); + i++; + + if(headerValue == null){ + break; + } + if(headerName == null){ + continue; + } + + headerObj.put(headerName, headerValue); + } + resObj.put("body", headerObj); + notifyListeners("streamed_fetch", resObj); + + while (true){ + int ableBytes = in.available(); + byte[] buf = new byte[ableBytes]; + int bytesRead = in.read(buf, 0, ableBytes); + if(bytesRead == -1){ + break; + } + byte[] encodedBuf = Base64.encode(buf, Base64.DEFAULT); + JSObject obj = new JSObject(); + obj.put("id", id); + obj.put("body", encodedBuf); + obj.put("type", "chunk"); + notifyListeners("streamed_fetch", obj); + } + JSObject endObj = new JSObject(); + endObj.put("id", id); + endObj.put("type", "end"); + notifyListeners("streamed_fetch", endObj); + } finally { + urlConnection.disconnect(); + } + } catch (IOException e) { + JSObject obj = new JSObject(); + obj.put("error", String.valueOf(e)); + call.resolve(obj); + return; + } catch (JSONException e) { + JSObject obj = new JSObject(); + obj.put("error", String.valueOf(e)); + call.resolve(obj); + return; + } + + JSObject ret = new JSObject(); + ret.put("success", true); + call.resolve(ret); + } } diff --git a/android/build.gradle b/android/build.gradle index 9cc72cb6..07aec2fa 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.0.0' + classpath 'com.android.tools.build:gradle:8.1.3' classpath 'com.google.gms:google-services:4.3.15' // NOTE: Do not place your application dependencies here; they belong diff --git a/src/ts/storage/globalApi.ts b/src/ts/storage/globalApi.ts index 142592eb..83c64b52 100644 --- a/src/ts/storage/globalApi.ts +++ b/src/ts/storage/globalApi.ts @@ -28,6 +28,7 @@ import * as CapFS from '@capacitor/filesystem' import { save } from "@tauri-apps/api/dialog"; import type { RisuModule } from "../process/modules"; import { listen } from '@tauri-apps/api/event' +import { registerPlugin } from '@capacitor/core'; //@ts-ignore export const isTauri = !!window.__TAURI__ @@ -1281,7 +1282,7 @@ export class LocalWriter{ } let fetchIndex = 0 -let tauriNativeFetchData:{[key:string]:StreamedFetchChunk[]} = {} +let nativeFetchData:{[key:string]:StreamedFetchChunk[]} = {} interface StreamedFetchChunkData{ type:'chunk', @@ -1302,19 +1303,39 @@ interface StreamedFetchEndData{ } type StreamedFetchChunk = StreamedFetchChunkData|StreamedFetchHeaderData|StreamedFetchEndData +interface StreamedFetchPlugin { + streamedFetch(options: { id: string, url:string, body:string, headers:{[key:string]:string} }): Promise<{"error":string,"success":boolean}>; + addListener(eventName: 'streamed_fetch', listenerFunc: (data:StreamedFetchChunk) => void): void; +} + let streamedFetchListening = false +let capStreamedFetch:StreamedFetchPlugin|undefined -listen('streamed_fetch', (event) => { - try { - const parsed = JSON.parse(event.payload as string) - const id = parsed.id - tauriNativeFetchData[id]?.push(parsed) - } catch (error) { - console.error(error) - } -}).then((v) => { +if(isTauri){ + listen('streamed_fetch', (event) => { + try { + const parsed = JSON.parse(event.payload as string) + const id = parsed.id + nativeFetchData[id]?.push(parsed) + } catch (error) { + console.error(error) + } + }).then((v) => { + streamedFetchListening = true + }) +} +if(Capacitor.isNativePlatform()){ + capStreamedFetch = registerPlugin('CapacitorHttp', CapacitorHttp) + + capStreamedFetch.addListener('streamed_fetch', (data) => { + try { + nativeFetchData[data.id]?.push(data) + } catch (error) { + console.error(error) + } + }) streamedFetchListening = true -}) +} export async function fetchNative(url:string, arg:{ body:string, @@ -1326,7 +1347,7 @@ export async function fetchNative(url:string, arg:{ let headers = arg.headers ?? {} const db = get(DataBase) let throughProxi = (!isTauri) && (!isNodeServer) && (!db.usePlainFetch) && (!Capacitor.isNativePlatform()) - if(isTauri){ + if(isTauri || Capacitor.isNativePlatform()){ fetchIndex++ if(arg.signal && arg.signal.aborted){ throw new Error('aborted') @@ -1335,34 +1356,49 @@ export async function fetchNative(url:string, arg:{ fetchIndex = 0 } let fetchId = fetchIndex.toString().padStart(5,'0') - tauriNativeFetchData[fetchId] = [] + nativeFetchData[fetchId] = [] let resolved = false let error = '' while(!streamedFetchListening){ await sleep(100) } - invoke('streamed_fetch', { - id: fetchId, - url: url, - headers: JSON.stringify(headers), - body: arg.body, - }).then((res) => { - const parsedRes = JSON.parse(res as string) - if(!parsedRes.success){ - error = parsedRes.body - resolved = true - } - }) + if(isTauri){ + invoke('streamed_fetch', { + id: fetchId, + url: url, + headers: JSON.stringify(headers), + body: arg.body, + }).then((res) => { + const parsedRes = JSON.parse(res as string) + if(!parsedRes.success){ + error = parsedRes.body + resolved = true + } + }) + } + else if(capStreamedFetch){ + capStreamedFetch.streamedFetch({ + id: fetchId, + url: url, + headers: headers, + body: Buffer.from(arg.body).toString('base64'), + }).then((res) => { + if(!res.success){ + error = res.error + resolved = true + } + }) + } let resHeaders:{[key:string]:string} = null let status = 400 const readableStream = new ReadableStream({ async start(controller) { - while(!resolved || tauriNativeFetchData[fetchId].length > 0){ - if(tauriNativeFetchData[fetchId].length > 0){ - const data = tauriNativeFetchData[fetchId].shift() + while(!resolved || nativeFetchData[fetchId].length > 0){ + if(nativeFetchData[fetchId].length > 0){ + const data = nativeFetchData[fetchId].shift() console.log(data) if(data.type === 'chunk'){ const chunk = Buffer.from(data.body, 'base64') From 12cb150009dbb118bd0b3f20730bc1ad27ca8f71 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Thu, 14 Mar 2024 06:25:30 +0900 Subject: [PATCH 6/7] Add new Claude 3 Haiku model --- src/lib/UI/ModelList.svelte | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/UI/ModelList.svelte b/src/lib/UI/ModelList.svelte index 2ca36a75..894cad13 100644 --- a/src/lib/UI/ModelList.svelte +++ b/src/lib/UI/ModelList.svelte @@ -100,6 +100,8 @@ return 'Mistral Small' case 'mistral-medium-latest': return 'Mistral Medium' + case 'claude-3-haiku-20240307': + return 'Claude 3 Haiku (20240307)' default: if(name.startsWith("horde:::")){ const split = name.split(":::") @@ -157,7 +159,8 @@ - + + {#if showUnrec} From a8a83475e395b978cec978bbb4174dc0ab8acbd7 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Thu, 14 Mar 2024 06:25:59 +0900 Subject: [PATCH 7/7] Update version to 1.82.2 --- src-tauri/tauri.conf.json | 2 +- src/ts/storage/database.ts | 2 +- version.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index e16be3f5..f2235588 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -8,7 +8,7 @@ }, "package": { "productName": "RisuAI", - "version": "1.82.1" + "version": "1.82.2" }, "tauri": { "allowlist": { diff --git a/src/ts/storage/database.ts b/src/ts/storage/database.ts index 2b8d6663..bac89b2e 100644 --- a/src/ts/storage/database.ts +++ b/src/ts/storage/database.ts @@ -15,7 +15,7 @@ import type { OobaChatCompletionRequestParams } from '../model/ooba'; export const DataBase = writable({} as any as Database) export const loadedStore = writable(false) -export let appVer = "1.82.1" +export let appVer = "1.82.2" export let webAppSubVer = '' export function setDatabase(data:Database){ diff --git a/version.json b/version.json index 40b2f3df..60e91dd9 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version":"1.82.1"} \ No newline at end of file +{"version":"1.82.2"} \ No newline at end of file