From 19d6e3bc22d0dec617cdd70509c5fb2b25e26e52 Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Wed, 12 Feb 2025 13:52:07 -0800 Subject: [PATCH 1/9] feat: stop setting presets to metadata This separates metadata and presets so they can be used independently. This allows preset updates to be reflected since they are no longer overwritten by metadata. --- frontend/app/view/waveai/waveai.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/app/view/waveai/waveai.tsx b/frontend/app/view/waveai/waveai.tsx index d52a11c071..6f23500ad6 100644 --- a/frontend/app/view/waveai/waveai.tsx +++ b/frontend/app/view/waveai/waveai.tsx @@ -163,8 +163,13 @@ export class WaveAiModel implements ViewModel { this.aiOpts = atom((get) => { const meta = get(this.blockAtom).meta; let settings = get(atoms.settingsAtom); + let presetKey = get(this.presetKey); + + let presets = get(atoms.fullConfigAtom).presets; + let selectedPresets = presets?.[presetKey] ?? {}; settings = { ...settings, + ...selectedPresets, ...meta, }; const opts: WaveAIOptsType = { @@ -244,7 +249,6 @@ export class WaveAiModel implements ViewModel { onClick: () => fireAndForget(() => ObjectService.UpdateObjectMeta(WOS.makeORef("block", this.blockId), { - ...preset[1], "ai:preset": preset[0], }) ), From 22e90323e9ed99a7569fdc2a16c2d9c542d8640a Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Wed, 12 Feb 2025 13:53:24 -0800 Subject: [PATCH 2/9] fix: db migration to cleanup ai keys in meta Since metadata was previously overridden with preset data, this must be cleared out so the new change can work properly in existing blocks. --- db/migrations-wstore/000008_aimeta.down.sql | 1 + db/migrations-wstore/000008_aimeta.up.sql | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 db/migrations-wstore/000008_aimeta.down.sql create mode 100644 db/migrations-wstore/000008_aimeta.up.sql diff --git a/db/migrations-wstore/000008_aimeta.down.sql b/db/migrations-wstore/000008_aimeta.down.sql new file mode 100644 index 0000000000..b654758c26 --- /dev/null +++ b/db/migrations-wstore/000008_aimeta.down.sql @@ -0,0 +1 @@ +-- presets exist in config files, and should automatically prepopulate the meta in the older code versions \ No newline at end of file diff --git a/db/migrations-wstore/000008_aimeta.up.sql b/db/migrations-wstore/000008_aimeta.up.sql new file mode 100644 index 0000000000..bb5b151023 --- /dev/null +++ b/db/migrations-wstore/000008_aimeta.up.sql @@ -0,0 +1,17 @@ +--- removes all ai: keys except ai:preset +UPDATE db_block +SET data = json_remove( + db_block.data, + '$.meta.ai:*', + '$.meta.ai:apitype', + '$.meta.ai:baseurl', + '$.meta.ai:apitoken', + '$.meta.ai:name', + '$.meta.ai:model', + '$.meta.ai:orgid', + '$.meta.ai:apiversion', + '$.meta.ai:maxtokens', + '$.meta.timeoutms', + '$.meta.fontsize', + '$.meta.fixedfontsize' +); \ No newline at end of file From 1433a78f0a01bd35db1315625a57921c3a3179d8 Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Wed, 12 Feb 2025 15:36:27 -0800 Subject: [PATCH 3/9] fix: add mergeMeta frontend function This function includes proper handling of prefix:* meta items. --- frontend/app/view/waveai/waveai.tsx | 29 ++++++++--------- frontend/util/util.ts | 50 +++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/frontend/app/view/waveai/waveai.tsx b/frontend/app/view/waveai/waveai.tsx index 6f23500ad6..49f98a2b3c 100644 --- a/frontend/app/view/waveai/waveai.tsx +++ b/frontend/app/view/waveai/waveai.tsx @@ -11,7 +11,7 @@ import { DefaultRouter, TabRpcClient } from "@/app/store/wshrpcutil"; import { atoms, createBlock, fetchWaveFile, getApi, globalStore, useOverrideConfigAtom, WOS } from "@/store/global"; import { BlockService, ObjectService } from "@/store/services"; import { adaptFromReactOrNativeKeyEvent, checkKeyPressed } from "@/util/keyutil"; -import { fireAndForget, isBlank, makeIconClass } from "@/util/util"; +import { fireAndForget, isBlank, makeIconClass, mergeMeta } from "@/util/util"; import { atom, Atom, PrimitiveAtom, useAtomValue, WritableAtom } from "jotai"; import { splitAtom } from "jotai/utils"; import type { OverlayScrollbars } from "overlayscrollbars"; @@ -164,23 +164,22 @@ export class WaveAiModel implements ViewModel { const meta = get(this.blockAtom).meta; let settings = get(atoms.settingsAtom); let presetKey = get(this.presetKey); - let presets = get(atoms.fullConfigAtom).presets; + let selectedPresets = presets?.[presetKey] ?? {}; - settings = { - ...settings, - ...selectedPresets, - ...meta, - }; + let mergedPresets: MetaType = {}; + mergedPresets = mergeMeta(settings, selectedPresets); + mergedPresets = mergeMeta(mergedPresets, meta); + const opts: WaveAIOptsType = { - model: settings["ai:model"] ?? null, - apitype: settings["ai:apitype"] ?? null, - orgid: settings["ai:orgid"] ?? null, - apitoken: settings["ai:apitoken"] ?? null, - apiversion: settings["ai:apiversion"] ?? null, - maxtokens: settings["ai:maxtokens"] ?? null, - timeoutms: settings["ai:timeoutms"] ?? 60000, - baseurl: settings["ai:baseurl"] ?? null, + model: mergedPresets["ai:model"] ?? null, + apitype: mergedPresets["ai:apitype"] ?? null, + orgid: mergedPresets["ai:orgid"] ?? null, + apitoken: mergedPresets["ai:apitoken"] ?? null, + apiversion: mergedPresets["ai:apiversion"] ?? null, + maxtokens: mergedPresets["ai:maxtokens"] ?? null, + timeoutms: mergedPresets["ai:timeoutms"] ?? 60000, + baseurl: mergedPresets["ai:baseurl"] ?? null, }; return opts; }); diff --git a/frontend/util/util.ts b/frontend/util/util.ts index ae5df29e9c..307f81f521 100644 --- a/frontend/util/util.ts +++ b/frontend/util/util.ts @@ -329,6 +329,55 @@ function makeNativeLabel(platform: string, isDirectory: boolean, isParent: boole return `${fileAction} in ${managerName}`; } +function mergeMeta(meta: MetaType, metaUpdate: MetaType): MetaType { + const rtn: MetaType = {}; + + // Copy original meta + for (const [k, v] of Object.entries(meta)) { + rtn[k] = v; + } + + // Deal with "section:*" keys + for (const k of Object.keys(metaUpdate)) { + if (!k.endsWith(":*")) { + continue; + } + + if (!metaUpdate[k]) { + continue; + } + + const prefix = k.slice(0, -2); // Remove ':*' suffix + if (prefix === "") { + continue; + } + + // Delete "[prefix]" and all keys that start with "[prefix]:" + const prefixColon = prefix + ":"; + for (const k2 of Object.keys(rtn)) { + if (k2 === prefix || k2.startsWith(prefixColon)) { + delete rtn[k2]; + } + } + } + + // Deal with regular keys + for (const [k, v] of Object.entries(metaUpdate)) { + if (k.endsWith(":*")) { + continue; + } + + if (v === null || v === undefined) { + delete rtn[k]; + continue; + } + + rtn[k] = v; + } + + return rtn; +} + export { atomWithDebounce, atomWithThrottle, @@ -349,6 +398,7 @@ export { makeExternLink, makeIconClass, makeNativeLabel, + mergeMeta, sleep, stringToBase64, useAtomValueSafe, From 63f508ff04a4d955d16b2e151f57fa62268037c9 Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Wed, 12 Feb 2025 16:00:26 -0800 Subject: [PATCH 4/9] fix: add a prefix filter in mergeMeta --- frontend/app/view/waveai/waveai.tsx | 6 ++--- frontend/util/util.ts | 42 ++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/frontend/app/view/waveai/waveai.tsx b/frontend/app/view/waveai/waveai.tsx index 49f98a2b3c..657b9a150f 100644 --- a/frontend/app/view/waveai/waveai.tsx +++ b/frontend/app/view/waveai/waveai.tsx @@ -165,11 +165,11 @@ export class WaveAiModel implements ViewModel { let settings = get(atoms.settingsAtom); let presetKey = get(this.presetKey); let presets = get(atoms.fullConfigAtom).presets; - let selectedPresets = presets?.[presetKey] ?? {}; + let mergedPresets: MetaType = {}; - mergedPresets = mergeMeta(settings, selectedPresets); - mergedPresets = mergeMeta(mergedPresets, meta); + mergedPresets = mergeMeta(settings, selectedPresets, "ai"); + mergedPresets = mergeMeta(mergedPresets, meta, "ai"); const opts: WaveAIOptsType = { model: mergedPresets["ai:model"] ?? null, diff --git a/frontend/util/util.ts b/frontend/util/util.ts index 307f81f521..04b581521a 100644 --- a/frontend/util/util.ts +++ b/frontend/util/util.ts @@ -329,15 +329,28 @@ function makeNativeLabel(platform: string, isDirectory: boolean, isParent: boole return `${fileAction} in ${managerName}`; } -function mergeMeta(meta: MetaType, metaUpdate: MetaType): MetaType { +function mergeMeta(meta: MetaType, metaUpdate: MetaType, prefix?: string): MetaType { const rtn: MetaType = {}; - // Copy original meta + // Helper function to check if a key matches the prefix criteria + const shouldIncludeKey = (key: string): boolean => { + if (prefix === undefined) { + return true; + } + if (prefix === "") { + return !key.includes(":"); + } + return key.startsWith(prefix + ":"); + }; + + // Copy original meta (only keys matching prefix criteria) for (const [k, v] of Object.entries(meta)) { - rtn[k] = v; + if (shouldIncludeKey(k)) { + rtn[k] = v; + } } - // Deal with "section:*" keys + // Deal with "section:*" keys (only if they match prefix criteria) for (const k of Object.keys(metaUpdate)) { if (!k.endsWith(":*")) { continue; @@ -347,22 +360,31 @@ function mergeMeta(meta: MetaType, metaUpdate: MetaType): MetaType { continue; } - const prefix = k.slice(0, -2); // Remove ':*' suffix - if (prefix === "") { + const sectionPrefix = k.slice(0, -2); // Remove ':*' suffix + if (sectionPrefix === "") { + continue; + } + + // Only process if this section matches our prefix criteria + if (!shouldIncludeKey(sectionPrefix)) { continue; } - // Delete "[prefix]" and all keys that start with "[prefix]:" - const prefixColon = prefix + ":"; + // Delete "[sectionPrefix]" and all keys that start with "[sectionPrefix]:" + const prefixColon = sectionPrefix + ":"; for (const k2 of Object.keys(rtn)) { - if (k2 === prefix || k2.startsWith(prefixColon)) { + if (k2 === sectionPrefix || k2.startsWith(prefixColon)) { delete rtn[k2]; } } } - // Deal with regular keys + // Deal with regular keys (only if they match prefix criteria) for (const [k, v] of Object.entries(metaUpdate)) { + if (!shouldIncludeKey(k)) { + continue; + } + if (k.endsWith(":*")) { continue; } From f688db76c0f5f9797e4dd7e8a95642316b401e9d Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Wed, 12 Feb 2025 16:14:29 -0800 Subject: [PATCH 5/9] fix: names of json keys and limit to waveai view Fixes ai:timeoutms, ai:fontsize, and ai:fixedfontsize. Also only performs the migration on blocks where the view is waveai. --- db/migrations-wstore/000008_aimeta.up.sql | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/db/migrations-wstore/000008_aimeta.up.sql b/db/migrations-wstore/000008_aimeta.up.sql index bb5b151023..203902ef99 100644 --- a/db/migrations-wstore/000008_aimeta.up.sql +++ b/db/migrations-wstore/000008_aimeta.up.sql @@ -11,7 +11,8 @@ SET data = json_remove( '$.meta.ai:orgid', '$.meta.ai:apiversion', '$.meta.ai:maxtokens', - '$.meta.timeoutms', - '$.meta.fontsize', - '$.meta.fixedfontsize' -); \ No newline at end of file + '$.meta.ai:timeoutms', + '$.meta.ai:fontsize', + '$.meta.ai:fixedfontsize' +) +WHERE json_extract(data, '$.meta.view') = 'waveai'; \ No newline at end of file From f921acb2ee44873a48f9b66055f557dd3d6f0c33 Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Wed, 12 Feb 2025 17:56:42 -0800 Subject: [PATCH 6/9] fix:use json_value instead of json_extract --- db/migrations-wstore/000008_aimeta.up.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrations-wstore/000008_aimeta.up.sql b/db/migrations-wstore/000008_aimeta.up.sql index 203902ef99..92bb9b0497 100644 --- a/db/migrations-wstore/000008_aimeta.up.sql +++ b/db/migrations-wstore/000008_aimeta.up.sql @@ -15,4 +15,4 @@ SET data = json_remove( '$.meta.ai:fontsize', '$.meta.ai:fixedfontsize' ) -WHERE json_extract(data, '$.meta.view') = 'waveai'; \ No newline at end of file +WHERE json_value(data, '$.meta.view') = 'waveai'; \ No newline at end of file From 7a21f4ff1cec3240763540009d7cd20d4bbf20cb Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Thu, 13 Feb 2025 11:00:12 -0800 Subject: [PATCH 7/9] fix: ai:fontsize and ai:fixedfontsize in presets This change allows the presets to apply these changes in addition to global and meta settings. --- frontend/app/view/waveai/waveai.tsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/frontend/app/view/waveai/waveai.tsx b/frontend/app/view/waveai/waveai.tsx index 657b9a150f..398329a84d 100644 --- a/frontend/app/view/waveai/waveai.tsx +++ b/frontend/app/view/waveai/waveai.tsx @@ -8,7 +8,7 @@ import { RpcResponseHelper, WshClient } from "@/app/store/wshclient"; import { RpcApi } from "@/app/store/wshclientapi"; import { makeFeBlockRouteId } from "@/app/store/wshrouter"; import { DefaultRouter, TabRpcClient } from "@/app/store/wshrpcutil"; -import { atoms, createBlock, fetchWaveFile, getApi, globalStore, useOverrideConfigAtom, WOS } from "@/store/global"; +import { atoms, createBlock, fetchWaveFile, getApi, globalStore, WOS } from "@/store/global"; import { BlockService, ObjectService } from "@/store/services"; import { adaptFromReactOrNativeKeyEvent, checkKeyPressed } from "@/util/keyutil"; import { fireAndForget, isBlank, makeIconClass, mergeMeta } from "@/util/util"; @@ -67,6 +67,7 @@ export class WaveAiModel implements ViewModel { blockAtom: Atom; presetKey: Atom; presetMap: Atom<{ [k: string]: MetaType }>; + mergedPresets: Atom; aiOpts: Atom; viewIcon?: Atom; viewName?: Atom; @@ -160,7 +161,7 @@ export class WaveAiModel implements ViewModel { set(this.updateLastMessageAtom, "", false); }); - this.aiOpts = atom((get) => { + this.mergedPresets = atom((get) => { const meta = get(this.blockAtom).meta; let settings = get(atoms.settingsAtom); let presetKey = get(this.presetKey); @@ -171,6 +172,12 @@ export class WaveAiModel implements ViewModel { mergedPresets = mergeMeta(settings, selectedPresets, "ai"); mergedPresets = mergeMeta(mergedPresets, meta, "ai"); + return mergedPresets; + }); + + this.aiOpts = atom((get) => { + const mergedPresets = get(this.mergedPresets); + const opts: WaveAIOptsType = { model: mergedPresets["ai:model"] ?? null, apitype: mergedPresets["ai:apitype"] ?? null, @@ -440,8 +447,11 @@ export class WaveAiModel implements ViewModel { const ChatItem = ({ chatItemAtom, model }: ChatItemProps) => { const chatItem = useAtomValue(chatItemAtom); const { user, text } = chatItem; - const fontSize = useOverrideConfigAtom(model.blockId, "ai:fontsize"); - const fixedFontSize = useOverrideConfigAtom(model.blockId, "ai:fixedfontsize"); + const fontSize = useAtomValue(model.mergedPresets)?.["ai:fontsize"]; + console.log("font size is:", fontSize); + //const fontSize = useOverrideConfigAtom(model.blockId, "ai:fontsize"); + const fixedFontSize = useAtomValue(model.mergedPresets)?.["ai:fixedfontsize"]; + //const fixedFontSize = useOverrideConfigAtom(model.blockId, "ai:fixedfontsize"); const renderContent = useMemo(() => { if (user == "error") { return ( From ef5f165044e7402a6926f4dadc54cbdff824d18c Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Thu, 13 Feb 2025 11:46:39 -0800 Subject: [PATCH 8/9] fix: switch back to json_extract --- db/migrations-wstore/000008_aimeta.up.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrations-wstore/000008_aimeta.up.sql b/db/migrations-wstore/000008_aimeta.up.sql index 92bb9b0497..203902ef99 100644 --- a/db/migrations-wstore/000008_aimeta.up.sql +++ b/db/migrations-wstore/000008_aimeta.up.sql @@ -15,4 +15,4 @@ SET data = json_remove( '$.meta.ai:fontsize', '$.meta.ai:fixedfontsize' ) -WHERE json_value(data, '$.meta.view') = 'waveai'; \ No newline at end of file +WHERE json_extract(data, '$.meta.view') = 'waveai'; \ No newline at end of file From f5c9cb16a1d504140babedf92a1688b60e4213a9 Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Thu, 13 Feb 2025 13:12:31 -0800 Subject: [PATCH 9/9] refactor: remove debugging comments and dead code --- frontend/app/view/waveai/waveai.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/app/view/waveai/waveai.tsx b/frontend/app/view/waveai/waveai.tsx index 398329a84d..3ed852a780 100644 --- a/frontend/app/view/waveai/waveai.tsx +++ b/frontend/app/view/waveai/waveai.tsx @@ -448,10 +448,7 @@ const ChatItem = ({ chatItemAtom, model }: ChatItemProps) => { const chatItem = useAtomValue(chatItemAtom); const { user, text } = chatItem; const fontSize = useAtomValue(model.mergedPresets)?.["ai:fontsize"]; - console.log("font size is:", fontSize); - //const fontSize = useOverrideConfigAtom(model.blockId, "ai:fontsize"); const fixedFontSize = useAtomValue(model.mergedPresets)?.["ai:fixedfontsize"]; - //const fixedFontSize = useOverrideConfigAtom(model.blockId, "ai:fixedfontsize"); const renderContent = useMemo(() => { if (user == "error") { return (