From b27b99989c890959c01e051219f45e2a58f221e6 Mon Sep 17 00:00:00 2001 From: Philipp Rich Date: Tue, 30 Sep 2025 11:12:04 +0400 Subject: [PATCH 1/9] init package --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index aa84f26..afb1baf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-scribe-plugin", - "version": "2.0.9", + "version": "2.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "obsidian-scribe-plugin", - "version": "2.0.9", + "version": "2.1.1", "license": "MIT", "dependencies": { "@ffmpeg/ffmpeg": "^0.12.15", From ed05c396862e4196fa7889260c8414e69e45630f Mon Sep 17 00:00:00 2001 From: Philipp Rich Date: Sat, 15 Nov 2025 20:29:21 +0400 Subject: [PATCH 2/9] feat: refactored provider settings --- src/settings/ProviderSettingsTab.tsx | 154 ++++++++++++++++++-- src/settings/components/AiModelSettings.tsx | 41 +++--- src/settings/hooks/useSettingsForm.tsx | 6 + src/settings/settings.tsx | 16 +- 4 files changed, 181 insertions(+), 36 deletions(-) diff --git a/src/settings/ProviderSettingsTab.tsx b/src/settings/ProviderSettingsTab.tsx index 74a48f3..d2abe50 100644 --- a/src/settings/ProviderSettingsTab.tsx +++ b/src/settings/ProviderSettingsTab.tsx @@ -1,30 +1,158 @@ -import { SettingsInput } from './components/SettingsControl'; +import { LLM_MODELS } from 'src/util/openAiUtils'; +import { + SettingsInput, + SettingsSelect, + SettingsToggle, +} from './components/SettingsControl'; import { SettingsItemHeader } from './components/SettingsItem'; import useSettingsForm from './hooks/useSettingsForm'; +import { PROCESS_PLATFORM, TRANSCRIPT_PLATFORM } from './settings'; + +const transcriptSelectMapping = [ + { + displayName: 'OpenAI', + value: TRANSCRIPT_PLATFORM.openAi, + }, + { + displayName: 'AssemblyAI', + value: TRANSCRIPT_PLATFORM.assemblyAi, + }, + { + displayName: 'GoogleAI', + value: TRANSCRIPT_PLATFORM.google, + }, + { + displayName: 'Custom endpoint (OpenAI-compatible)', + value: TRANSCRIPT_PLATFORM.customOpenAi, + }, +]; +const processSelectMapping = [ + { + displayName: 'OpenAI', + value: TRANSCRIPT_PLATFORM.openAi, + }, + { + displayName: 'GoogleAI', + value: TRANSCRIPT_PLATFORM.google, + }, + { + displayName: 'Custom endpoint (OpenAI-compatible)', + value: TRANSCRIPT_PLATFORM.customOpenAi, + }, +]; + +// 'gpt-4.1' = 'gpt-4.1', +// 'gpt-4.1-mini' = 'gpt-4.1-mini', +// 'gpt-4o' = 'gpt-4o', +// 'gpt-4o-mini' = 'gpt-4o-mini', +// 'gpt-4-turbo' = 'gpt-4-turbo', + +const LlmModelMapping = Object.values(LLM_MODELS).map((model) => ({ + value: model, + displayName: model, +})); /** - * Tab, containing general settings + * Tab, containing AI provider settings */ function ProviderSettingsTab() { const { register, settings } = useSettingsForm(); return (
- - + - + + + + )} + {(settings.transcriptPlatform === TRANSCRIPT_PLATFORM.openAi || + settings.transcriptPlatform === TRANSCRIPT_PLATFORM.customOpenAi) && ( + + )} + {settings.transcriptPlatform === TRANSCRIPT_PLATFORM.customOpenAi && ( + <> + + + + )} + + + {settings.processPlatform === PROCESS_PLATFORM.openAi && ( + + )} + {settings.processPlatform === PROCESS_PLATFORM.customOpenAi && ( + <> + + + + )}
); } export default ProviderSettingsTab; + +// +// handleCustomTranscriptModelChange(e.target.value) +// } +// className="text-input" +// /> +// } +// /> diff --git a/src/settings/components/AiModelSettings.tsx b/src/settings/components/AiModelSettings.tsx index a7dedf4..30b8a99 100644 --- a/src/settings/components/AiModelSettings.tsx +++ b/src/settings/components/AiModelSettings.tsx @@ -1,18 +1,18 @@ import { useState } from 'react'; import type ScribePlugin from 'src'; -import { LLM_MODELS } from 'src/util/openAiUtils'; -import { TRANSCRIPT_PLATFORM } from '../settings'; +// import { LLM_MODELS } from 'src/util/openAiUtils'; +// import { TRANSCRIPT_PLATFORM } from '../settings'; import { SettingsItem } from './SettingsItem'; export const AiModelSettings: React.FC<{ plugin: ScribePlugin; saveSettings: () => void; }> = ({ plugin, saveSettings }) => { - const [transcriptPlatform, setTranscriptPlatform] = - useState(plugin.settings.transcriptPlatform); - const [llmModel, setLlmModel] = useState( - plugin.settings.llmModel, - ); + // const [transcriptPlatform, setTranscriptPlatform] = + // useState(plugin.settings.transcriptPlatform); + // const [llmModel, setLlmModel] = useState( + // plugin.settings.llmModel, + // ); const [isMultiSpeakerEnabled, setIsMultiSpeakerEnabled] = useState( plugin.settings.isMultiSpeakerEnabled, ); @@ -73,8 +73,8 @@ export const AiModelSettings: React.FC<{ return (
-

AI model options

- AI model options */} + {/* AssemblyAI } - /> + /> */} - {transcriptPlatform === TRANSCRIPT_PLATFORM.assemblyAi && ( + {/* {transcriptPlatform === TRANSCRIPT_PLATFORM.assemblyAi && ( } /> - )} + )} */} - } - /> + /> */}

Custom OpenAI Configuration

+ - } - /> + /> */} - } - /> + /> */} - } - /> + /> */} )}
diff --git a/src/settings/hooks/useSettingsForm.tsx b/src/settings/hooks/useSettingsForm.tsx index abe408c..512176b 100644 --- a/src/settings/hooks/useSettingsForm.tsx +++ b/src/settings/hooks/useSettingsForm.tsx @@ -10,7 +10,13 @@ interface UseSettingsFormProps { } interface RegisterOptions { + /** + * Callback to run on value in state before displaying it in input + */ displayValue?(value: ScribePluginSettings[K]): ScribePluginSettings[K]; + /** + * Callback to run on value before saving it to state + */ setValueAs?(value: ScribePluginSettings[K]): ScribePluginSettings[K]; } diff --git a/src/settings/settings.tsx b/src/settings/settings.tsx index d393767..e24dc99 100644 --- a/src/settings/settings.tsx +++ b/src/settings/settings.tsx @@ -21,6 +21,13 @@ import { SettingsFormProvider } from './provider/SettingsFormProvider'; export enum TRANSCRIPT_PLATFORM { assemblyAi = 'assemblyAi', openAi = 'openAi', + customOpenAi = 'customOpenAi', + google = 'google', +} +export enum PROCESS_PLATFORM { + openAi = 'openAi', + customOpenAi = 'customOpenAi', + google = 'google', } export enum OBSIDIAN_PATHS { @@ -50,10 +57,12 @@ export interface ScribePluginSettings { selectedAudioDeviceId: string; audioFileFormat: 'webm' | 'mp3'; // Custom OpenAI settings - useCustomOpenAiBaseUrl: boolean; + useCustomOpenAiBaseUrl: boolean; // TODO replace with processPlatform, transcriptPlatform customOpenAiBaseUrl: string; customTranscriptModel: string; customChatModel: string; + // Provider settings + processPlatform: PROCESS_PLATFORM; } export const DEFAULT_SETTINGS: ScribePluginSettings = { @@ -83,6 +92,7 @@ export const DEFAULT_SETTINGS: ScribePluginSettings = { customOpenAiBaseUrl: '', customTranscriptModel: 'whisper-1', customChatModel: 'gpt-4o', + processPlatform: PROCESS_PLATFORM.openAi, }; export async function handleSettingsTab(plugin: ScribePlugin) { @@ -165,10 +175,10 @@ const ScribeSettings: React.FC<{ plugin: ScribePlugin }> = ({ plugin }) => { return ( <> - + /> */} ); case SettingsTabsId.TEMPLATES: From ff794425f2ad505c6a3374f00a23215a2c195e30 Mon Sep 17 00:00:00 2001 From: Philipp Rich Date: Wed, 3 Dec 2025 17:14:17 +0400 Subject: [PATCH 3/9] refactor: updated langchain version --- package-lock.json | 1132 ++++++++------------------ package.json | 9 +- src/index.ts | 101 ++- src/settings/ProviderSettingsTab.tsx | 41 +- src/settings/settings.tsx | 12 +- src/util/audioDataToChunkedFiles.ts | 11 +- src/util/openAiUtils.ts | 16 +- tsconfig.json | 3 +- 8 files changed, 469 insertions(+), 856 deletions(-) diff --git a/package-lock.json b/package-lock.json index d8e0622..bae7fc5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,15 +12,14 @@ "@ffmpeg/ffmpeg": "^0.12.15", "@ffmpeg/util": "^0.12.2", "@fix-webm-duration/fix": "^1.0.1", - "@langchain/core": "^0.3.40", - "@langchain/openai": "^0.5.10", + "@langchain/openai": "^1.1.3", "assemblyai": "^4.9.0", - "langchain": "^0.3.19", + "langchain": "^1.1.2", "mini-debounce": "^1.0.8", - "openai": "^4.85.4", + "openai": "^6.0.0", "react": "^19.1.0", "react-dom": "^19.1.0", - "zod": "^3.24.2" + "zod": "^4.0.0" }, "devDependencies": { "@biomejs/biome": "1.9.4", @@ -206,9 +205,9 @@ "license": "MIT" }, "node_modules/@codemirror/state": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", - "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz", + "integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==", "dev": true, "license": "MIT", "peer": true, @@ -217,22 +216,23 @@ } }, "node_modules/@codemirror/view": { - "version": "6.36.7", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.7.tgz", - "integrity": "sha512-kCWGW/chWGPgZqfZ36Um9Iz0X2IVpmCjg1P/qY6B6a2ecXtWRRAigmpJ6YgUQ5lTWXMyyVdfmpzhLZmsZQMbtg==", + "version": "6.38.6", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz", + "integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", - "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "cpu": [ "ppc64" ], @@ -247,9 +247,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", - "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", "cpu": [ "arm" ], @@ -264,9 +264,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", - "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", "cpu": [ "arm64" ], @@ -281,9 +281,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", - "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ "x64" ], @@ -298,9 +298,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", - "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], @@ -315,9 +315,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", - "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], @@ -332,9 +332,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", - "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", "cpu": [ "arm64" ], @@ -349,9 +349,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", - "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ "x64" ], @@ -366,9 +366,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", - "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], @@ -383,9 +383,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", - "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], @@ -400,9 +400,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", - "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ "ia32" ], @@ -417,9 +417,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", - "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ "loong64" ], @@ -434,9 +434,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", - "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ "mips64el" ], @@ -451,9 +451,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", - "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ "ppc64" ], @@ -468,9 +468,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", - "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ "riscv64" ], @@ -485,9 +485,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", - "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ "s390x" ], @@ -502,9 +502,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", - "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -519,9 +519,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", - "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", "cpu": [ "arm64" ], @@ -536,9 +536,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", - "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], @@ -553,9 +553,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", - "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", "cpu": [ "arm64" ], @@ -570,9 +570,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", - "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", "cpu": [ "x64" ], @@ -586,10 +586,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", - "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", "cpu": [ "x64" ], @@ -604,9 +621,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", - "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", "cpu": [ "arm64" ], @@ -621,9 +638,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", - "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ "ia32" ], @@ -638,9 +655,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", - "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], @@ -708,51 +725,47 @@ "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz", "integrity": "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q==", "license": "Apache-2.0", - "optional": true, - "peer": true, "engines": { "node": ">=18.0.0" } }, "node_modules/@langchain/core": { - "version": "0.3.77", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.77.tgz", - "integrity": "sha512-aqXHea9xfpVn6VoCq9pjujwFqrh3vw3Fgm9KFUZJ1cF7Bx5HI62DvQPw8LlRB3NB4dhwBBA1ldAVkkkd1du8nA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.1.tgz", + "integrity": "sha512-vdUoj2CVbb+0Qszi8llP34vdUCfP7bfA9VoFr4Se1pFGu7VAPnk8lBnRat9IvqSxMfTvOHJSd7Rn6TUPjzKsnA==", "license": "MIT", + "peer": true, "dependencies": { "@cfworker/json-schema": "^4.0.2", "ansi-styles": "^5.0.0", "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", - "langsmith": "^0.3.67", + "langsmith": "^0.3.64", "mustache": "^4.2.0", "p-queue": "^6.6.2", - "p-retry": "4", + "p-retry": "^7.0.0", "uuid": "^10.0.0", - "zod": "^3.25.32", - "zod-to-json-schema": "^3.22.3" + "zod": "^3.25.76 || ^4" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/@langchain/google-genai": { - "version": "0.2.18", - "resolved": "https://registry.npmjs.org/@langchain/google-genai/-/google-genai-0.2.18.tgz", - "integrity": "sha512-m9EiN3VKC01A7/625YQ6Q1Lqq8zueewADX4W5Tcme4RImN75zkg2Z7FYbD1Fo6Zwolc4wBNO6LUtbg3no4rv1Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@langchain/google-genai/-/google-genai-2.0.1.tgz", + "integrity": "sha512-Aq87qOPcib7oQtKmgXYM7evaBc/VKJOZ9IH4jchqobYj13xMfIaJogtZekSZdL2HiZrtq14+cWXhqfLe+MAbzg==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "@google/generative-ai": "^0.24.0", "uuid": "^11.1.0" }, "engines": { - "node": ">=18" + "node": ">=20" }, "peerDependencies": { - "@langchain/core": ">=0.3.58 <0.4.0" + "@langchain/core": "1.1.1" } }, "node_modules/@langchain/google-genai/node_modules/uuid": { @@ -764,43 +777,117 @@ "https://github.com/sponsors/ctavan" ], "license": "MIT", - "optional": true, - "peer": true, "bin": { "uuid": "dist/esm/bin/uuid" } }, - "node_modules/@langchain/openai": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.5.10.tgz", - "integrity": "sha512-hBQIWjcVxGS7tgVvgBBmrZ5jSaJ8nu9g6V64/Tx6KGjkW7VdGmUvqCO+koiQCOZVL7PBJkHWAvDsbghPYXiZEA==", + "node_modules/@langchain/langgraph": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-1.0.2.tgz", + "integrity": "sha512-syxzzWTnmpCL+RhUEvalUeOXFoZy/KkzHa2Da2gKf18zsf9Dkbh3rfnRDrTyUGS1XSTejq07s4rg1qntdEDs2A==", "license": "MIT", "dependencies": { - "js-tiktoken": "^1.0.12", - "openai": "^4.96.0", - "zod": "^3.22.4", - "zod-to-json-schema": "^3.22.3" + "@langchain/langgraph-checkpoint": "^1.0.0", + "@langchain/langgraph-sdk": "~1.0.0", + "uuid": "^10.0.0" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@langchain/core": ">=0.3.48 <0.4.0" + "@langchain/core": "^1.0.1", + "zod": "^3.25.32 || ^4.1.0", + "zod-to-json-schema": "^3.x" + }, + "peerDependenciesMeta": { + "zod-to-json-schema": { + "optional": true + } } }, - "node_modules/@langchain/textsplitters": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@langchain/textsplitters/-/textsplitters-0.1.0.tgz", - "integrity": "sha512-djI4uw9rlkAb5iMhtLED+xJebDdAG935AdP4eRTB02R7OB/act55Bj9wsskhZsvuyQRpO4O1wQOp85s6T6GWmw==", + "node_modules/@langchain/langgraph-checkpoint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.0.0.tgz", + "integrity": "sha512-xrclBGvNCXDmi0Nz28t3vjpxSH6UYx6w5XAXSiiB1WEdc2xD2iY/a913I3x3a31XpInUW/GGfXXfePfaghV54A==", "license": "MIT", "dependencies": { - "js-tiktoken": "^1.0.12" + "uuid": "^10.0.0" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@langchain/core": ">=0.2.21 <0.4.0" + "@langchain/core": "^1.0.1" + } + }, + "node_modules/@langchain/langgraph-sdk": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-1.0.3.tgz", + "integrity": "sha512-6M4i0XsVO5Eb2vv/3OtIPHW3UqO4zYyXl6AOfS0Jf6d7JiWiSXqzLN8UoS0hpu1ItkcW1j575CRiP/6jn6XXFg==", + "license": "MIT", + "dependencies": { + "p-queue": "^6.6.2", + "p-retry": "4", + "uuid": "^9.0.0" + }, + "peerDependencies": { + "@langchain/core": "^1.0.1", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@langchain/core": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@langchain/langgraph-sdk/node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@langchain/langgraph-sdk/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/openai": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-1.1.3.tgz", + "integrity": "sha512-p+xR+4HRms5Ozjf5miC6U2AYRyNVSTdO7AMBkMYs1Tp6DWHBd+mQ72H8Ogd2dKrPuS5UDJ5dbpI1fS+OrTbgQQ==", + "license": "MIT", + "dependencies": { + "js-tiktoken": "^1.0.12", + "openai": "^6.9.0", + "zod": "^3.25.76 || ^4" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@langchain/core": "^1.0.0" } }, "node_modules/@marijn/find-cluster-break": { @@ -808,8 +895,7 @@ "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/codemirror": { "version": "5.60.8", @@ -822,55 +908,41 @@ } }, "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz", - "integrity": "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==", + "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", + "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, - "node_modules/@types/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/node/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, "node_modules/@types/react": { - "version": "19.1.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", - "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", + "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "19.1.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.3.tgz", - "integrity": "sha512-rJXC08OG0h3W6wDMFxQrZF00Kq6qQvw0djHRdzl3U5DnIERz0MRce3WVc7IS6JYBwtaP/DwYtRRjVlvivNveKg==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", "peerDependencies": { - "@types/react": "^19.0.0" + "@types/react": "^19.2.0" } }, "node_modules/@types/retry": { @@ -895,30 +967,6 @@ "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", "license": "MIT" }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "license": "MIT", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, "node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", @@ -931,16 +979,10 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, "node_modules/assemblyai": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/assemblyai/-/assemblyai-4.12.2.tgz", - "integrity": "sha512-T0QPhKfKoIV1hiQgUvH8fmxYeDsNZI8gSvJHCntJnzQF/WwbtMrebc4z6rFy29QP1tp28iXlmABGRZjHwW0LEQ==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/assemblyai/-/assemblyai-4.19.0.tgz", + "integrity": "sha512-3yMTZoipmae16Xj3htLKtG/m7PMOt/enIn16u3leX9iF+GiTpzuj60aUqA3zq19lkoDauVlpWVy6PIYssWN1Nw==", "license": "MIT", "dependencies": { "ws": "^8.18.0" @@ -949,12 +991,6 @@ "node": ">=18" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -988,19 +1024,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -1062,31 +1085,26 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/console-table-printer": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.12.1.tgz", - "integrity": "sha512-wKGOQRRvdnd89pCeH96e2Fn4wkbenSP6LMHfjfyNLMbGuHEFbMqQNuxXqd0oXG9caIOQ1FTvc5Uijp9/4jujnQ==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.15.0.tgz", + "integrity": "sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==", "license": "MIT", "dependencies": { - "simple-wcswidth": "^1.0.1" + "simple-wcswidth": "^1.1.2" } }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "dev": true, + "license": "MIT" + }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "dev": true, "license": "MIT" }, @@ -1099,19 +1117,10 @@ "node": ">=0.10.0" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/dotenv": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", - "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -1121,69 +1130,10 @@ "url": "https://dotenvx.com" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/esbuild": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", - "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1194,40 +1144,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.3", - "@esbuild/android-arm": "0.25.3", - "@esbuild/android-arm64": "0.25.3", - "@esbuild/android-x64": "0.25.3", - "@esbuild/darwin-arm64": "0.25.3", - "@esbuild/darwin-x64": "0.25.3", - "@esbuild/freebsd-arm64": "0.25.3", - "@esbuild/freebsd-x64": "0.25.3", - "@esbuild/linux-arm": "0.25.3", - "@esbuild/linux-arm64": "0.25.3", - "@esbuild/linux-ia32": "0.25.3", - "@esbuild/linux-loong64": "0.25.3", - "@esbuild/linux-mips64el": "0.25.3", - "@esbuild/linux-ppc64": "0.25.3", - "@esbuild/linux-riscv64": "0.25.3", - "@esbuild/linux-s390x": "0.25.3", - "@esbuild/linux-x64": "0.25.3", - "@esbuild/netbsd-arm64": "0.25.3", - "@esbuild/netbsd-x64": "0.25.3", - "@esbuild/openbsd-arm64": "0.25.3", - "@esbuild/openbsd-x64": "0.25.3", - "@esbuild/sunos-x64": "0.25.3", - "@esbuild/win32-arm64": "0.25.3", - "@esbuild/win32-ia32": "0.25.3", - "@esbuild/win32-x64": "0.25.3" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" } }, "node_modules/eventemitter3": { @@ -1236,99 +1178,6 @@ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "license": "MIT" }, - "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", - "license": "MIT" - }, - "node_modules/formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "license": "MIT", - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1338,191 +1187,56 @@ "node": ">=8" } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "node_modules/is-network-error": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.0.tgz", + "integrity": "sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==", "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, "engines": { - "node": ">= 0.4" + "node": ">=16" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/js-tiktoken": { - "version": "1.0.20", - "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.20.tgz", - "integrity": "sha512-Xlaqhhs8VfCd6Sh7a1cFkZHQbYTLCwVJJWiHVxBYzLPxW0XsoxBy1hitmjkdIjD3Aon5BXLHFwU5O8WUx6HH+A==", + "version": "1.0.21", + "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz", + "integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==", "license": "MIT", "dependencies": { "base64-js": "^1.5.1" } }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/langchain": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.3.24.tgz", - "integrity": "sha512-BTjiYkUCpWFAmufK8J5zMqc5aUs4eEnAXPWtPe2+R4ZPP+U7bXJSBHAcrB40rQ3VeTdRgMvgDjekOOgCMWut6Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/langchain/-/langchain-1.1.2.tgz", + "integrity": "sha512-5ChHmMDEA6EO4DdW33Bf3sbZ2zYzXHmD+BE9VuxznCnzBkoBkPY5Ly2Au2oyaQr7PyEFRSZHkHZhhEqQc4slVQ==", "license": "MIT", "dependencies": { - "@langchain/openai": ">=0.1.0 <0.6.0", - "@langchain/textsplitters": ">=0.0.0 <0.2.0", - "js-tiktoken": "^1.0.12", - "js-yaml": "^4.1.0", - "jsonpointer": "^5.0.1", - "langsmith": "^0.3.16", - "openapi-types": "^12.1.3", - "p-retry": "4", + "@langchain/langgraph": "^1.0.0", + "@langchain/langgraph-checkpoint": "^1.0.0", + "langsmith": "~0.3.74", "uuid": "^10.0.0", - "yaml": "^2.2.1", - "zod": "^3.22.4", - "zod-to-json-schema": "^3.22.3" + "zod": "^3.25.76 || ^4" }, "engines": { - "node": ">=18" + "node": ">=20" }, "peerDependencies": { - "@langchain/anthropic": "*", - "@langchain/aws": "*", - "@langchain/cerebras": "*", - "@langchain/cohere": "*", - "@langchain/core": ">=0.2.21 <0.4.0", - "@langchain/deepseek": "*", - "@langchain/google-genai": "*", - "@langchain/google-vertexai": "*", - "@langchain/google-vertexai-web": "*", - "@langchain/groq": "*", - "@langchain/mistralai": "*", - "@langchain/ollama": "*", - "@langchain/xai": "*", - "axios": "*", - "cheerio": "*", - "handlebars": "^4.7.8", - "peggy": "^3.0.2", - "typeorm": "*" - }, - "peerDependenciesMeta": { - "@langchain/anthropic": { - "optional": true - }, - "@langchain/aws": { - "optional": true - }, - "@langchain/cerebras": { - "optional": true - }, - "@langchain/cohere": { - "optional": true - }, - "@langchain/deepseek": { - "optional": true - }, - "@langchain/google-genai": { - "optional": true - }, - "@langchain/google-vertexai": { - "optional": true - }, - "@langchain/google-vertexai-web": { - "optional": true - }, - "@langchain/groq": { - "optional": true - }, - "@langchain/mistralai": { - "optional": true - }, - "@langchain/ollama": { - "optional": true - }, - "@langchain/xai": { - "optional": true - }, - "axios": { - "optional": true - }, - "cheerio": { - "optional": true - }, - "handlebars": { - "optional": true - }, - "peggy": { - "optional": true - }, - "typeorm": { - "optional": true - } + "@langchain/core": "1.1.1" } }, "node_modules/langsmith": { - "version": "0.3.71", - "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.3.71.tgz", - "integrity": "sha512-xl00JZso7J3OaurUQ+seT2qRJ34OGZXYAvCYj3vNC3TB+JOcdcYZ1uLvENqOloKB8VCiADh1eZ0FG3Cj/cy2FQ==", + "version": "0.3.82", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.3.82.tgz", + "integrity": "sha512-RTcxtRm0zp2lV+pMesMW7EZSsIlqN7OmR2F6sZ/sOFQwmcLVl+VErMPV4VkX4Sycs4/EIAFT5hpr36EqiHoikQ==", "license": "MIT", "dependencies": { "@types/uuid": "^10.0.0", "chalk": "^4.1.2", "console-table-printer": "^2.12.1", "p-queue": "^6.6.2", - "p-retry": "4", "semver": "^7.6.3", "uuid": "^10.0.0" }, @@ -1547,36 +1261,6 @@ } } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/mini-debounce": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/mini-debounce/-/mini-debounce-1.0.8.tgz", @@ -1593,12 +1277,6 @@ "node": "*" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/mustache": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", @@ -1608,50 +1286,10 @@ "mustache": "bin/mustache" } }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/obsidian": { - "version": "1.8.7", - "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.8.7.tgz", - "integrity": "sha512-h4bWwNFAGRXlMlMAzdEiIM2ppTGlrh7uGOJS6w4gClrsjc+ei/3YAtU2VdFUlCiPuTHpY4aBpFJJW75S1Tl/JA==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.10.3.tgz", + "integrity": "sha512-VP+ZSxNMG7y6Z+sU9WqLvJAskCfkFrTz2kFHWmmzis+C+4+ELjk/sazwcTHrHXNZlgCeo8YOlM6SOrAFCynNew==", "dev": true, "license": "MIT", "dependencies": { @@ -1659,30 +1297,21 @@ "moment": "2.29.4" }, "peerDependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" + "@codemirror/state": "6.5.0", + "@codemirror/view": "6.38.6" } }, "node_modules/openai": { - "version": "4.97.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.97.0.tgz", - "integrity": "sha512-LRoiy0zvEf819ZUEJhgfV8PfsE8G5WpQi4AwA1uCV8SKvvtXQkoWUFkepD6plqyJQRghy2+AEPQ07FrJFKHZ9Q==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.9.1.tgz", + "integrity": "sha512-vQ5Rlt0ZgB3/BNmTa7bIijYFhz3YBceAA3Z4JuoMSBftBF9YqFHIEhZakSs+O/Ad7EaoEimZvHxD5ylRjN11Lg==", "license": "Apache-2.0", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - }, "bin": { "openai": "bin/cli" }, "peerDependencies": { "ws": "^8.18.0", - "zod": "^3.23.8" + "zod": "^3.25 || ^4.0" }, "peerDependenciesMeta": { "ws": { @@ -1693,21 +1322,6 @@ } } }, - "node_modules/openai/node_modules/@types/node": { - "version": "18.19.87", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.87.tgz", - "integrity": "sha512-OIAAu6ypnVZHmsHCeJ+7CCSub38QNBS9uceMQeg7K5Ur0Jr+wG9wEOEvvMbhp09pxD5czIUy/jND7s7Tb6Nw7A==", - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/openapi-types": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", - "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", - "license": "MIT" - }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -1734,16 +1348,18 @@ } }, "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-7.1.0.tgz", + "integrity": "sha512-xL4PiFRQa/f9L9ZvR4/gUCRNus4N8YX80ku8kv9Jqz+ZokkiZLM0bcvX0gm1F3PDi9SPRsww1BDsTWgE6Y1GLQ==", "license": "MIT", "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" + "is-network-error": "^1.1.0" }, "engines": { - "node": ">=8" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-timeout": { @@ -1759,24 +1375,26 @@ } }, "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", + "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", + "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", + "peer": true, "dependencies": { - "scheduler": "^0.26.0" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.1.0" + "react": "^19.2.0" } }, "node_modules/retry": { @@ -1789,15 +1407,15 @@ } }, "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1807,18 +1425,17 @@ } }, "node_modules/simple-wcswidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz", - "integrity": "sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz", + "integrity": "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==", "license": "MIT" }, "node_modules/style-mod": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", - "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/supports-color": { "version": "7.2.0", @@ -1832,12 +1449,6 @@ "node": ">=8" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -1845,9 +1456,9 @@ "license": "0BSD" }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1859,9 +1470,10 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, "license": "MIT" }, "node_modules/uuid": { @@ -1882,38 +1494,12 @@ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } + "license": "MIT" }, "node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -1931,35 +1517,15 @@ } } }, - "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", + "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } - }, - "node_modules/zod-to-json-schema": { - "version": "3.24.5", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", - "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.24.1" - } } } } diff --git a/package.json b/package.json index 4b01401..fa0a7e1 100644 --- a/package.json +++ b/package.json @@ -28,14 +28,13 @@ "@ffmpeg/ffmpeg": "^0.12.15", "@ffmpeg/util": "^0.12.2", "@fix-webm-duration/fix": "^1.0.1", - "@langchain/core": "^0.3.40", - "@langchain/openai": "^0.5.10", + "@langchain/openai": "^1.1.3", "assemblyai": "^4.9.0", - "langchain": "^0.3.19", + "langchain": "^1.1.2", "mini-debounce": "^1.0.8", - "openai": "^4.85.4", + "openai": "^6.0.0", "react": "^19.1.0", "react-dom": "^19.1.0", - "zod": "^3.24.2" + "zod": "^4.0.0" } } diff --git a/src/index.ts b/src/index.ts index e02c4df..6c5467f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,19 @@ import { Notice, Plugin, type TFile, moment, normalizePath } from 'obsidian'; import type OpenAI from 'openai'; +import { AudioRecord } from './audioRecord/audioRecord'; +import { handleCommands } from './commands/commands'; +import { ScribeControlsModal } from './modal/scribeControlsModal'; +import { handleRibbon } from './ribbon/ribbon'; +import type { ScribeTemplate } from './settings/components/NoteTemplateSettings'; import { DEFAULT_SETTINGS, - handleSettingsTab, - TRANSCRIPT_PLATFORM, + PROCESS_PLATFORM, type ScribePluginSettings, + TRANSCRIPT_PLATFORM, + handleSettingsTab, } from './settings/settings'; -import { handleRibbon } from './ribbon/ribbon'; -import { handleCommands } from './commands/commands'; -import { getDefaultPathSettings } from './util/pathUtils'; -import { AudioRecord } from './audioRecord/audioRecord'; +import { transcribeAudioWithAssemblyAi } from './util/assemblyAiUtil'; +import type { LanguageOptions } from './util/consts'; import { appendTextToNote, createNewNote, @@ -17,22 +21,20 @@ import { saveAudioRecording, setupFileFrontmatter, } from './util/fileUtils'; +import { formatFilenamePrefix } from './util/filenameUtils'; +// import { summarizeTranscriptGemini } from './util/geminiAiUtils'; +import { + type SupportedMimeType, + mimeTypeToFileExtension, +} from './util/mimeType'; import { - chunkAndTranscribeWithOpenAi, type LLM_MODELS, + chunkAndTranscribeWithOpenAi, llmFixMermaidChart, summarizeTranscript, } from './util/openAiUtils'; -import { ScribeControlsModal } from './modal/scribeControlsModal'; -import { - mimeTypeToFileExtension, - type SupportedMimeType, -} from './util/mimeType'; +import { getDefaultPathSettings } from './util/pathUtils'; import { convertToSafeJsonKey, extractMermaidChart } from './util/textUtil'; -import { transcribeAudioWithAssemblyAi } from './util/assemblyAiUtil'; -import { formatFilenamePrefix } from './util/filenameUtils'; -import type { LanguageOptions } from './util/consts'; -import type { ScribeTemplate } from './settings/components/NoteTemplateSettings'; export interface ScribeState { isOpen: boolean; @@ -229,13 +231,13 @@ export default class ScribePlugin extends Plugin { let fixedMermaidChart: string | undefined; if (brokenMermaidChart) { - const customBaseUrl = this.settings.useCustomOpenAiBaseUrl - ? this.settings.customOpenAiBaseUrl + const customBaseUrl = this.settings.useCustomOpenAiBaseUrl + ? this.settings.customOpenAiBaseUrl : undefined; - const customChatModel = this.settings.useCustomOpenAiBaseUrl - ? this.settings.customChatModel + const customChatModel = this.settings.useCustomOpenAiBaseUrl + ? this.settings.customChatModel : undefined; - + fixedMermaidChart = ( await llmFixMermaidChart( this.settings.openAiApiKey, @@ -402,12 +404,10 @@ export default class ScribePlugin extends Plugin { ) { try { if (this.settings.isDisableLlmTranscription) { - new Notice( - `Scribe: 🎧 Transcription is disabled in settings`, - ); - return ""; + new Notice('Scribe: 🎧 Transcription is disabled in settings'); + return ''; } - + new Notice( `Scribe: 🎧 Beginning transcription w/ ${this.settings.transcriptPlatform}`, ); @@ -422,11 +422,11 @@ export default class ScribePlugin extends Plugin { this.settings.openAiApiKey, audioBuffer, scribeOptions, - this.settings.useCustomOpenAiBaseUrl - ? this.settings.customOpenAiBaseUrl + this.settings.useCustomOpenAiBaseUrl + ? this.settings.customOpenAiBaseUrl : undefined, - this.settings.useCustomOpenAiBaseUrl - ? this.settings.customTranscriptModel + this.settings.useCustomOpenAiBaseUrl + ? this.settings.customTranscriptModel : undefined, ); @@ -453,21 +453,38 @@ export default class ScribePlugin extends Plugin { ) { new Notice('Scribe: 🧠 Sending to LLM to summarize'); - const customBaseUrl = this.settings.useCustomOpenAiBaseUrl - ? this.settings.customOpenAiBaseUrl + const customBaseUrl = this.settings.useCustomOpenAiBaseUrl + ? this.settings.customOpenAiBaseUrl : undefined; - const customChatModel = this.settings.useCustomOpenAiBaseUrl - ? this.settings.customChatModel + const customChatModel = this.settings.useCustomOpenAiBaseUrl + ? this.settings.customChatModel : undefined; - const llmSummary = await summarizeTranscript( - this.settings.openAiApiKey, - transcript, - scribeOptions, - this.settings.llmModel, - customBaseUrl, - customChatModel, - ); + let llmSummary: Record & { + fileTitle: string; + }; + + switch (this.settings.processPlatform) { + case PROCESS_PLATFORM.openAi: + case PROCESS_PLATFORM.customOpenAi: + llmSummary = await summarizeTranscript( + this.settings.openAiApiKey, + transcript, + scribeOptions, + this.settings.llmModel, + customBaseUrl, + customChatModel, + ); + break; + // case PROCESS_PLATFORM.google: + // llmSummary = await summarizeTranscriptGemini( + // this.settings.googleAiApiKey, + // transcript, + // scribeOptions, + // this.settings.googleModel, + // ); + // break; + } new Notice('Scribe: 🧠 LLM summation complete'); diff --git a/src/settings/ProviderSettingsTab.tsx b/src/settings/ProviderSettingsTab.tsx index d2abe50..5151abf 100644 --- a/src/settings/ProviderSettingsTab.tsx +++ b/src/settings/ProviderSettingsTab.tsx @@ -1,3 +1,4 @@ +// import { LLM_MODELS as GOOGLE_MODELS } from 'src/util/geminiAiUtils'; import { LLM_MODELS } from 'src/util/openAiUtils'; import { SettingsInput, @@ -17,10 +18,10 @@ const transcriptSelectMapping = [ displayName: 'AssemblyAI', value: TRANSCRIPT_PLATFORM.assemblyAi, }, - { - displayName: 'GoogleAI', - value: TRANSCRIPT_PLATFORM.google, - }, + // { + // displayName: 'GoogleAI', + // value: TRANSCRIPT_PLATFORM.google, + // }, { displayName: 'Custom endpoint (OpenAI-compatible)', value: TRANSCRIPT_PLATFORM.customOpenAi, @@ -31,10 +32,10 @@ const processSelectMapping = [ displayName: 'OpenAI', value: TRANSCRIPT_PLATFORM.openAi, }, - { - displayName: 'GoogleAI', - value: TRANSCRIPT_PLATFORM.google, - }, + // { + // displayName: 'GoogleAI', + // value: TRANSCRIPT_PLATFORM.google, + // }, { displayName: 'Custom endpoint (OpenAI-compatible)', value: TRANSCRIPT_PLATFORM.customOpenAi, @@ -47,10 +48,14 @@ const processSelectMapping = [ // 'gpt-4o-mini' = 'gpt-4o-mini', // 'gpt-4-turbo' = 'gpt-4-turbo', -const LlmModelMapping = Object.values(LLM_MODELS).map((model) => ({ +const OpenAiModelMapping = Object.values(LLM_MODELS).map((model) => ({ value: model, displayName: model, })); +// const GoogleModelMapping = Object.values(GOOGLE_MODELS).map((model) => ({ +// value: model, +// displayName: model, +// })); /** * Tab, containing AI provider settings @@ -117,9 +122,25 @@ function ProviderSettingsTab() { )} + {/* {settings.processPlatform === PROCESS_PLATFORM.google && ( + <> + + { + // + } + + )} */} {settings.processPlatform === PROCESS_PLATFORM.customOpenAi && ( <> { +): Promise { const audioContext = new window.AudioContext(); const sourceBuffer = await audioContext.decodeAudioData(audioData); const monoBuffer = audioBufferToMono(audioContext, sourceBuffer); @@ -23,7 +20,7 @@ export default async function audioDataToChunkedFiles( const chunkSamples = Math.floor(maxSize / 4); // 32-bit float = 4 bytes const nChunks = Math.ceil(monoBuffer.length / chunkSamples); - const files: FileLike[] = []; + const files: File[] = []; for (let i = 0; i < nChunks; i++) { const startSample = i * chunkSamples; @@ -42,7 +39,9 @@ export default async function audioDataToChunkedFiles( // Convert the chunk to a WAV ArrayBuffer const wavArrayBuffer = audioBufferToWav(chunkBuffer); - const file = await toFile(wavArrayBuffer, fileName(i, 'wav')); + const file = new File([wavArrayBuffer], fileName(i, 'wav'), { + type: 'audio/wav', + }); files.push(file); } diff --git a/src/util/openAiUtils.ts b/src/util/openAiUtils.ts index cd3abc5..78c381e 100644 --- a/src/util/openAiUtils.ts +++ b/src/util/openAiUtils.ts @@ -1,15 +1,14 @@ +import { ChatOpenAI } from '@langchain/openai'; /** * This was heavily inspired by * https://github.com/drewmcdonald/obsidian-magic-mic * Thank you for traversing this in such a clean way */ import OpenAI from 'openai'; -import audioDataToChunkedFiles from './audioDataToChunkedFiles'; -import type { FileLike } from 'openai/uploads'; -import { ChatOpenAI } from '@langchain/openai'; import { z } from 'zod'; -import { SystemMessage } from '@langchain/core/messages'; +import audioDataToChunkedFiles from './audioDataToChunkedFiles'; +import { SystemMessage } from 'langchain'; import { Notice } from 'obsidian'; import type { ScribeOptions } from 'src'; import { LanguageOptions } from './consts'; @@ -57,7 +56,7 @@ export async function chunkAndTranscribeWithOpenAi( */ interface TranscriptionOptions { - audioFiles: FileLike[]; + audioFiles: File[]; onChunkStart?: (i: number, totalChunks: number) => void; audioFileLanguage?: LanguageOptions; customModel?: string; @@ -65,7 +64,12 @@ interface TranscriptionOptions { async function transcribeAudio( client: OpenAI, - { audioFiles, onChunkStart, audioFileLanguage, customModel }: TranscriptionOptions, + { + audioFiles, + onChunkStart, + audioFileLanguage, + customModel, + }: TranscriptionOptions, ): Promise { let transcript = ''; for (const [i, file] of audioFiles.entries()) { diff --git a/tsconfig.json b/tsconfig.json index 938e0d8..ef2b6b9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,8 @@ "importHelpers": true, "isolatedModules": true, "strictNullChecks": true, - "lib": ["DOM", "ES5", "ES6", "ES7"] + "lib": ["DOM", "ES5", "ES6", "ES7"], + "skipLibCheck": true // to prevent tsc throwing errors from node_modules }, "include": ["src/*.ts", "src/*.tsx", "src/**/*.ts", "src/**/*.tsx"] } From 672c086ef386e21545de992bf87e5beefd5e217f Mon Sep 17 00:00:00 2001 From: Philipp Rich Date: Wed, 3 Dec 2025 19:12:32 +0400 Subject: [PATCH 4/9] feat: Implemented gemini processing of transcribed text --- src/index.ts | 18 +++++++++--------- src/settings/ProviderSettingsTab.tsx | 22 ++++++++++++---------- src/settings/settings.tsx | 6 +++--- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/index.ts b/src/index.ts index 6c5467f..2b2916d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,7 +22,7 @@ import { setupFileFrontmatter, } from './util/fileUtils'; import { formatFilenamePrefix } from './util/filenameUtils'; -// import { summarizeTranscriptGemini } from './util/geminiAiUtils'; +import { summarizeTranscriptGemini } from './util/geminiAiUtils'; import { type SupportedMimeType, mimeTypeToFileExtension, @@ -476,14 +476,14 @@ export default class ScribePlugin extends Plugin { customChatModel, ); break; - // case PROCESS_PLATFORM.google: - // llmSummary = await summarizeTranscriptGemini( - // this.settings.googleAiApiKey, - // transcript, - // scribeOptions, - // this.settings.googleModel, - // ); - // break; + case PROCESS_PLATFORM.google: + llmSummary = await summarizeTranscriptGemini( + this.settings.googleAiApiKey, + transcript, + scribeOptions, + this.settings.googleModel, + ); + break; } new Notice('Scribe: 🧠 LLM summation complete'); diff --git a/src/settings/ProviderSettingsTab.tsx b/src/settings/ProviderSettingsTab.tsx index 5151abf..c16c026 100644 --- a/src/settings/ProviderSettingsTab.tsx +++ b/src/settings/ProviderSettingsTab.tsx @@ -1,4 +1,4 @@ -// import { LLM_MODELS as GOOGLE_MODELS } from 'src/util/geminiAiUtils'; +import { LLM_MODELS as GOOGLE_MODELS } from 'src/util/geminiAiUtils'; import { LLM_MODELS } from 'src/util/openAiUtils'; import { SettingsInput, @@ -52,6 +52,10 @@ const OpenAiModelMapping = Object.values(LLM_MODELS).map((model) => ({ value: model, displayName: model, })); +const GoogleModelMapping = Object.values(GOOGLE_MODELS).map((model) => ({ + value: model, + displayName: model, +})); // const GoogleModelMapping = Object.values(GOOGLE_MODELS).map((model) => ({ // value: model, // displayName: model, @@ -125,22 +129,20 @@ function ProviderSettingsTab() { valuesMapping={OpenAiModelMapping} /> )} - {/* {settings.processPlatform === PROCESS_PLATFORM.google && ( + {settings.processPlatform === PROCESS_PLATFORM.google && ( <> - { - // - } + - )} */} + )} {settings.processPlatform === PROCESS_PLATFORM.customOpenAi && ( <> Date: Wed, 3 Dec 2025 19:14:45 +0400 Subject: [PATCH 5/9] feat: Implemented gemini processing of transcribed text --- package.json | 3 +- src/index.ts | 12 +- src/settings/ProviderSettingsTab.tsx | 181 -------------- .../ProviderSettingsSections.tsx | 146 ++++++++++++ .../ProviderSettingsTab.tsx | 92 ++++++++ .../ai-provider-settings-tab/index.ts | 3 + src/settings/components/AiModelSettings.tsx | 223 ------------------ src/settings/hooks/useSettingsForm.tsx | 2 +- src/settings/settings.tsx | 5 +- src/util/geminiAiUtils.ts | 149 ++++++++++++ 10 files changed, 403 insertions(+), 413 deletions(-) delete mode 100644 src/settings/ProviderSettingsTab.tsx create mode 100644 src/settings/ai-provider-settings-tab/ProviderSettingsSections.tsx create mode 100644 src/settings/ai-provider-settings-tab/ProviderSettingsTab.tsx create mode 100644 src/settings/ai-provider-settings-tab/index.ts delete mode 100644 src/settings/components/AiModelSettings.tsx create mode 100644 src/util/geminiAiUtils.ts diff --git a/package.json b/package.json index fa0a7e1..14822c8 100644 --- a/package.json +++ b/package.json @@ -36,5 +36,6 @@ "react": "^19.1.0", "react-dom": "^19.1.0", "zod": "^4.0.0" - } + }, + "packageManager": "pnpm@10.6.1+sha512.40ee09af407fa9fbb5fbfb8e1cb40fbb74c0af0c3e10e9224d7b53c7658528615b2c92450e74cfad91e3a2dcafe3ce4050d80bda71d757756d2ce2b66213e9a3" } diff --git a/src/index.ts b/src/index.ts index 2b2916d..2de6658 100644 --- a/src/index.ts +++ b/src/index.ts @@ -92,11 +92,15 @@ export default class ScribePlugin extends Plugin { const defaultPathSettings = await getDefaultPathSettings(this); - if (!this.settings.openAiApiKey) { - console.error( - 'OpenAI API key is needed in Scribes settings - https://platform.openai.com/settings', + if ( + !this.settings.openAiApiKey && + !this.settings.googleAiApiKey && + !this.settings.assemblyAiApiKey + ) { + console.error('API key is needed in Scribes settings'); + new Notice( + '⚠️ Scribe: Provide API key for AI provider in plugin settings', ); - new Notice('⚠️ Scribe: OpenAI API key is missing for Scribe'); } if (!this.settings.recordingDirectory) { diff --git a/src/settings/ProviderSettingsTab.tsx b/src/settings/ProviderSettingsTab.tsx deleted file mode 100644 index c16c026..0000000 --- a/src/settings/ProviderSettingsTab.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import { LLM_MODELS as GOOGLE_MODELS } from 'src/util/geminiAiUtils'; -import { LLM_MODELS } from 'src/util/openAiUtils'; -import { - SettingsInput, - SettingsSelect, - SettingsToggle, -} from './components/SettingsControl'; -import { SettingsItemHeader } from './components/SettingsItem'; -import useSettingsForm from './hooks/useSettingsForm'; -import { PROCESS_PLATFORM, TRANSCRIPT_PLATFORM } from './settings'; - -const transcriptSelectMapping = [ - { - displayName: 'OpenAI', - value: TRANSCRIPT_PLATFORM.openAi, - }, - { - displayName: 'AssemblyAI', - value: TRANSCRIPT_PLATFORM.assemblyAi, - }, - // { - // displayName: 'GoogleAI', - // value: TRANSCRIPT_PLATFORM.google, - // }, - { - displayName: 'Custom endpoint (OpenAI-compatible)', - value: TRANSCRIPT_PLATFORM.customOpenAi, - }, -]; -const processSelectMapping = [ - { - displayName: 'OpenAI', - value: TRANSCRIPT_PLATFORM.openAi, - }, - // { - // displayName: 'GoogleAI', - // value: TRANSCRIPT_PLATFORM.google, - // }, - { - displayName: 'Custom endpoint (OpenAI-compatible)', - value: TRANSCRIPT_PLATFORM.customOpenAi, - }, -]; - -// 'gpt-4.1' = 'gpt-4.1', -// 'gpt-4.1-mini' = 'gpt-4.1-mini', -// 'gpt-4o' = 'gpt-4o', -// 'gpt-4o-mini' = 'gpt-4o-mini', -// 'gpt-4-turbo' = 'gpt-4-turbo', - -const OpenAiModelMapping = Object.values(LLM_MODELS).map((model) => ({ - value: model, - displayName: model, -})); -const GoogleModelMapping = Object.values(GOOGLE_MODELS).map((model) => ({ - value: model, - displayName: model, -})); -// const GoogleModelMapping = Object.values(GOOGLE_MODELS).map((model) => ({ -// value: model, -// displayName: model, -// })); - -/** - * Tab, containing AI provider settings - */ -function ProviderSettingsTab() { - const { register, settings } = useSettingsForm(); - - return ( -
- - - {settings.transcriptPlatform === TRANSCRIPT_PLATFORM.assemblyAi && ( - <> - - - - )} - {(settings.transcriptPlatform === TRANSCRIPT_PLATFORM.openAi || - settings.transcriptPlatform === TRANSCRIPT_PLATFORM.customOpenAi) && ( - - )} - {settings.transcriptPlatform === TRANSCRIPT_PLATFORM.customOpenAi && ( - <> - - - - )} - - - {settings.processPlatform === PROCESS_PLATFORM.openAi && ( - - )} - {settings.processPlatform === PROCESS_PLATFORM.google && ( - <> - - - - )} - {settings.processPlatform === PROCESS_PLATFORM.customOpenAi && ( - <> - - - - )} -
- ); -} - -export default ProviderSettingsTab; - -// -// handleCustomTranscriptModelChange(e.target.value) -// } -// className="text-input" -// /> -// } -// /> diff --git a/src/settings/ai-provider-settings-tab/ProviderSettingsSections.tsx b/src/settings/ai-provider-settings-tab/ProviderSettingsSections.tsx new file mode 100644 index 0000000..1e6c4d4 --- /dev/null +++ b/src/settings/ai-provider-settings-tab/ProviderSettingsSections.tsx @@ -0,0 +1,146 @@ +import { LLM_MODELS as GOOGLE_MODELS } from 'src/util/geminiAiUtils'; +import { LLM_MODELS } from 'src/util/openAiUtils'; +import { + SettingsInput, + SettingsSelect, + SettingsToggle, +} from '../components/SettingsControl'; +import type { RegisterOptions } from '../hooks/useSettingsForm'; +import type { ScribePluginSettings } from '../settings'; + +const GoogleModelMapping = Object.values(GOOGLE_MODELS).map((model) => ({ + value: model, + displayName: model, +})); + +const OpenAiModelMapping = Object.values(LLM_MODELS).map((model) => ({ + value: model, + displayName: model, +})); + +type RegisterFn = ( + id: K, + options?: RegisterOptions, +) => { + onChange: (value: ScribePluginSettings[K]) => void; + value: ScribePluginSettings[K]; + id: K; +}; + +type SettingsSectionProps = { + register: RegisterFn; +}; + +export function CustomOpenAiProcessingSettings({ + register, +}: SettingsSectionProps) { + return ( + <> + + + + ); +} + +export function GeminiProcessingSettings({ register }: SettingsSectionProps) { + return ( + <> + + + + ); +} + +export function OpenAiProcessingSettings({ register }: SettingsSectionProps) { + return ( + <> + + + + ); +} + +export function AssemblyTranscriptSettings({ register }: SettingsSectionProps) { + return ( + <> + + + + ); +} + +export function CustomOpenAiTranscriptSettings({ + register, +}: SettingsSectionProps) { + // TODO Implement customOpenAi Transcription API Input + return ( + <> + + + + + ); +} +export function OpenAiTranscriptSettings({ register }: SettingsSectionProps) { + // TODO Implement OpenAi Transcription API Input + // TODO Implement transcription model selection + return ( + <> + + + ); +} diff --git a/src/settings/ai-provider-settings-tab/ProviderSettingsTab.tsx b/src/settings/ai-provider-settings-tab/ProviderSettingsTab.tsx new file mode 100644 index 0000000..e0a6d3f --- /dev/null +++ b/src/settings/ai-provider-settings-tab/ProviderSettingsTab.tsx @@ -0,0 +1,92 @@ +import { SettingsSelect } from '../components/SettingsControl'; +import { SettingsItemHeader } from '../components/SettingsItem'; +import useSettingsForm, {} from '../hooks/useSettingsForm'; +import { PROCESS_PLATFORM, TRANSCRIPT_PLATFORM } from '../settings'; +import { + AssemblyTranscriptSettings, + CustomOpenAiProcessingSettings, + CustomOpenAiTranscriptSettings, + GeminiProcessingSettings, + OpenAiProcessingSettings, + OpenAiTranscriptSettings, +} from './ProviderSettingsSections'; + +const transcriptSelectMapping = [ + { + displayName: 'OpenAI', + value: TRANSCRIPT_PLATFORM.openAi, + }, + { + displayName: 'AssemblyAI', + value: TRANSCRIPT_PLATFORM.assemblyAi, + }, + // { + // displayName: 'GoogleAI', + // value: TRANSCRIPT_PLATFORM.google, + // }, + { + displayName: 'Custom endpoint (OpenAI-compatible)', + value: TRANSCRIPT_PLATFORM.customOpenAi, + }, +]; +const processSelectMapping = [ + { + displayName: 'OpenAI', + value: PROCESS_PLATFORM.openAi, + }, + { + displayName: 'GoogleAI', + value: PROCESS_PLATFORM.google, + }, + { + displayName: 'Custom endpoint (OpenAI-compatible)', + value: PROCESS_PLATFORM.customOpenAi, + }, +]; + +/** + * Tab, containing AI provider settings + */ +function ProviderSettingsTab() { + const { register, settings } = useSettingsForm(); + + return ( +
+ + + {settings.transcriptPlatform === TRANSCRIPT_PLATFORM.assemblyAi && ( + + )} + {settings.transcriptPlatform === TRANSCRIPT_PLATFORM.openAi && ( + + )} + {settings.transcriptPlatform === TRANSCRIPT_PLATFORM.customOpenAi && ( + + )} + + + + {settings.processPlatform === PROCESS_PLATFORM.openAi && ( + + )} + {settings.processPlatform === PROCESS_PLATFORM.google && ( + + )} + {settings.processPlatform === PROCESS_PLATFORM.customOpenAi && ( + + )} +
+ ); +} + +export default ProviderSettingsTab; diff --git a/src/settings/ai-provider-settings-tab/index.ts b/src/settings/ai-provider-settings-tab/index.ts new file mode 100644 index 0000000..f3572b3 --- /dev/null +++ b/src/settings/ai-provider-settings-tab/index.ts @@ -0,0 +1,3 @@ +import ProviderSettingsTab from './ProviderSettingsTab'; + +export { ProviderSettingsTab }; diff --git a/src/settings/components/AiModelSettings.tsx b/src/settings/components/AiModelSettings.tsx deleted file mode 100644 index 30b8a99..0000000 --- a/src/settings/components/AiModelSettings.tsx +++ /dev/null @@ -1,223 +0,0 @@ -import { useState } from 'react'; -import type ScribePlugin from 'src'; -// import { LLM_MODELS } from 'src/util/openAiUtils'; -// import { TRANSCRIPT_PLATFORM } from '../settings'; -import { SettingsItem } from './SettingsItem'; - -export const AiModelSettings: React.FC<{ - plugin: ScribePlugin; - saveSettings: () => void; -}> = ({ plugin, saveSettings }) => { - // const [transcriptPlatform, setTranscriptPlatform] = - // useState(plugin.settings.transcriptPlatform); - // const [llmModel, setLlmModel] = useState( - // plugin.settings.llmModel, - // ); - const [isMultiSpeakerEnabled, setIsMultiSpeakerEnabled] = useState( - plugin.settings.isMultiSpeakerEnabled, - ); - const [isDisableLlmTranscription, setIsDisableLlmTranscription] = useState( - plugin.settings.isDisableLlmTranscription, - ); - const [useCustomOpenAiBaseUrl, setUseCustomOpenAiBaseUrl] = useState( - plugin.settings.useCustomOpenAiBaseUrl, - ); - const [customOpenAiBaseUrl, setCustomOpenAiBaseUrl] = useState( - plugin.settings.customOpenAiBaseUrl, - ); - const [customTranscriptModel, setCustomTranscriptModel] = useState( - plugin.settings.customTranscriptModel, - ); - const [customChatModel, setCustomChatModel] = useState( - plugin.settings.customChatModel, - ); - - const handleToggleMultiSpeaker = () => { - const value = !isMultiSpeakerEnabled; - setIsMultiSpeakerEnabled(value); - plugin.settings.isMultiSpeakerEnabled = value; - saveSettings(); - }; - - const handleToggleDisableLlmTranscription = () => { - const value = !isDisableLlmTranscription; - setIsDisableLlmTranscription(value); - plugin.settings.isDisableLlmTranscription = value; - saveSettings(); - }; - - const handleToggleCustomOpenAiBaseUrl = () => { - const value = !useCustomOpenAiBaseUrl; - setUseCustomOpenAiBaseUrl(value); - plugin.settings.useCustomOpenAiBaseUrl = value; - saveSettings(); - }; - - const handleCustomOpenAiBaseUrlChange = (value: string) => { - setCustomOpenAiBaseUrl(value); - plugin.settings.customOpenAiBaseUrl = value; - saveSettings(); - }; - - const handleCustomTranscriptModelChange = (value: string) => { - setCustomTranscriptModel(value); - plugin.settings.customTranscriptModel = value; - saveSettings(); - }; - - const handleCustomChatModelChange = (value: string) => { - setCustomChatModel(value); - plugin.settings.customChatModel = value; - saveSettings(); - }; - - return ( -
- {/*

AI model options

*/} - {/* { - const value = e.target.value as TRANSCRIPT_PLATFORM; - setTranscriptPlatform(value); - plugin.settings.transcriptPlatform = value; - saveSettings(); - }} - > - - - - } - /> */} - - {/* {transcriptPlatform === TRANSCRIPT_PLATFORM.assemblyAi && ( - { - handleToggleMultiSpeaker(); - }} - onKeyDown={(e) => { - if (e.key === 'Enter') { - handleToggleMultiSpeaker(); - } - }} - > - -
- } - /> - )} */} - - {/* { - const value = e.target.value as LLM_MODELS; - setLlmModel(value); - plugin.settings.llmModel = value; - saveSettings(); - }} - > - {Object.keys(LLM_MODELS).map((model) => ( - - ))} - - } - /> */} - -

Custom OpenAI Configuration

- - { - handleToggleCustomOpenAiBaseUrl(); - }} - onKeyDown={(e) => { - if (e.key === 'Enter') { - handleToggleCustomOpenAiBaseUrl(); - } - }} - > - - - } - /> - - {useCustomOpenAiBaseUrl && ( - <> - {/* - handleCustomOpenAiBaseUrlChange(e.target.value) - } - className="text-input" - /> - } - /> */} - - {/* - handleCustomTranscriptModelChange(e.target.value) - } - className="text-input" - /> - } - /> */} - - {/* handleCustomChatModelChange(e.target.value)} - className="text-input" - /> - } - /> */} - - )} - - ); -}; diff --git a/src/settings/hooks/useSettingsForm.tsx b/src/settings/hooks/useSettingsForm.tsx index 512176b..8c8bc00 100644 --- a/src/settings/hooks/useSettingsForm.tsx +++ b/src/settings/hooks/useSettingsForm.tsx @@ -9,7 +9,7 @@ interface UseSettingsFormProps { plugin: ScribePlugin; } -interface RegisterOptions { +export interface RegisterOptions { /** * Callback to run on value in state before displaying it in input */ diff --git a/src/settings/settings.tsx b/src/settings/settings.tsx index c977849..1110ca2 100644 --- a/src/settings/settings.tsx +++ b/src/settings/settings.tsx @@ -10,8 +10,7 @@ import { LLM_MODELS } from 'src/util/openAiUtils'; import { useState } from 'react'; import { LanguageOptions, type OutputLanguageOptions } from 'src/util/consts'; import GeneralSettingsTab from './GeneralSettingsTab'; -import ProviderSettingsTab from './ProviderSettingsTab'; -import { AiModelSettings } from './components/AiModelSettings'; +import { ProviderSettingsTab } from './ai-provider-settings-tab'; import { DEFAULT_TEMPLATE, NoteTemplateSettings, @@ -28,7 +27,7 @@ export enum TRANSCRIPT_PLATFORM { export enum PROCESS_PLATFORM { openAi = 'openAi', customOpenAi = 'customOpenAi', - // google = 'google', + google = 'google', } export enum OBSIDIAN_PATHS { diff --git a/src/util/geminiAiUtils.ts b/src/util/geminiAiUtils.ts new file mode 100644 index 0000000..18cb164 --- /dev/null +++ b/src/util/geminiAiUtils.ts @@ -0,0 +1,149 @@ +import { ChatGoogleGenerativeAI } from '@langchain/google-genai'; +import { type BaseMessage, HumanMessage, SystemMessage } from 'langchain'; +import type { ScribeOptions } from 'src'; +import { z } from 'zod'; +import type { LanguageOptions } from './consts'; +import { convertToSafeJsonKey } from './textUtil'; + +export enum LLM_MODELS { + 'gemini-flash-latest' = 'gemini-flash-latest', + 'gemini-flash-light-latest' = 'gemini-flash-light-latest', + 'gemini-2.5-flash' = 'gemini-2.5-flash', + 'gemini-2.5-flash-lite' = 'gemini-2.5-flash-lite', + 'gemini-2.5-pro' = 'gemini-2.5-pro', + 'gemini-2.0-flash' = 'gemini-2.0-flash', + 'gemini-2.0-flash-lite' = 'gemini-2.0-flash-lite', +} + +// interface TranscriptionOptions { +// audioFiles: FileLike[]; // FIX +// onChunkStart?: (i: number, totalChunks: number) => void; +// audioFileLanguage?: LanguageOptions; +// model; +// } + +// async function transcribeAudioGemini({ +// audioFiles, +// onChunkStart, +// audioFileLanguage, +// model, +// }: TranscriptionOptions): Promise { +// // Write function which uses langchain + google AI to transcribe audio. +// return transcript; +// } + +export async function summarizeTranscriptGemini( + openAiKey: string, + transcript: string, + { scribeOutputLanguage, activeNoteTemplate }: ScribeOptions, + llmModel: LLM_MODELS = LLM_MODELS['gemini-2.0-flash-lite'], +) { + const systemPrompt = ` + You are "Scribe" an expert note-making AI for Obsidian you specialize in the Linking Your Thinking (LYK) strategy. + The following is the transcription generated from a recording of someone talking aloud or multiple people in a conversation. + There may be a lot of random things said given fluidity of conversation or thought process and the microphone's ability to pick up all audio. + + The transcription may address you by calling you "Scribe" or saying "Hey Scribe" and asking you a question, they also may just allude to you by asking "you" to do something. + Give them the answers to this question + + Give me notes in Markdown language on what was said, they should be + - Easy to understand + - Succinct + - Clean + - Logical + - Insightful + + It will be nested under a h2 # tag, feel free to nest headers underneath it + Rules: + - Do not include escaped new line characters + - Do not mention "the speaker" anywhere in your response. + - The notes should be written as if I were writing them. + + ${scribeOutputLanguage ? `Respond in ${scribeOutputLanguage} language` : ''} + `; + + const transcriptMessage = ` + The following is the transcribed audio: + + ${transcript.trim()} + + `; + + const modelToUse = llmModel; + + const model = new ChatGoogleGenerativeAI({ + model: modelToUse, + apiKey: openAiKey, + temperature: 0.5, + }); + + const messages: BaseMessage[] = [ + new SystemMessage(systemPrompt.trim()), + new HumanMessage(transcriptMessage), + ]; + + const schema: Record> = { + fileTitle: z + .string() + .describe( + 'A suggested title for the Obsidian Note. Ensure that it is in the proper format for a file on mac, windows and linux, do not include any special characters', + ), + }; + + activeNoteTemplate.sections.forEach((section) => { + const { sectionHeader, sectionInstructions, isSectionOptional } = section; + schema[convertToSafeJsonKey(sectionHeader)] = isSectionOptional + ? z.string().optional().describe(sectionInstructions) + : z.string().describe(sectionInstructions); + }); + + const structuredOutput = z.object(schema); + const structuredLlm = model.withStructuredOutput(structuredOutput); + + const result = (await structuredLlm.invoke(messages)) as Record< + string, + string + > & { fileTitle: string }; + + return result; +} + +// export async function llmFixMermaidChart( +// openAiKey: string, +// brokenMermaidChart: string, +// llmModel: LLM_MODELS = LLM_MODELS['gpt-4o'], +// customBaseUrl?: string, +// customChatModel?: string, +// ) { +// const systemPrompt = ` +// You are an expert in mermaid mindmaps and Obsidian (the note taking app) +// Below is a that isn't rendering correctly in Obsidian +// There may be some new line characters, or tab characters, or special characters. +// Strip them out and only return a fully valid unicode Mermaid mindmap that will render properly in Obsidian +// Remove any special characters in the nodes text that isn't valid. + +// +// ${brokenMermaidChart} +// + +// Thank you +// `; +// const modelToUse = customChatModel || llmModel; +// const model = new ChatOpenAI({ +// model: modelToUse, +// apiKey: openAiKey, +// temperature: 0.3, +// ...(customBaseUrl && { configuration: { baseURL: customBaseUrl } }), +// }); +// const messages = [new SystemMessage(systemPrompt)]; +// const structuredOutput = z.object({ +// mermaidChart: z +// .string() +// .describe('A fully valid unicode mermaid mindmap diagram'), +// }); + +// const structuredLlm = model.withStructuredOutput(structuredOutput); +// const { mermaidChart } = await structuredLlm.invoke(messages); + +// return { mermaidChart }; +// } From 5e4174895c8652d11ae7362427b3495f14da4df0 Mon Sep 17 00:00:00 2001 From: Philipp Rich Date: Fri, 5 Dec 2025 21:17:07 +0400 Subject: [PATCH 6/9] fix: changed inputs in ai provider settings tab --- .../ai-provider-settings-tab/ProviderSettingsSections.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/settings/ai-provider-settings-tab/ProviderSettingsSections.tsx b/src/settings/ai-provider-settings-tab/ProviderSettingsSections.tsx index 1e6c4d4..654cdfe 100644 --- a/src/settings/ai-provider-settings-tab/ProviderSettingsSections.tsx +++ b/src/settings/ai-provider-settings-tab/ProviderSettingsSections.tsx @@ -36,13 +36,19 @@ export function CustomOpenAiProcessingSettings({ }: SettingsSectionProps) { return ( <> + Date: Sun, 3 May 2026 11:25:06 +0400 Subject: [PATCH 7/9] feat(ai): implement obsidianFetch to resolve CORS issues and refine Gemini prompts - Add `obsidianFetch` utility to wrap Obsidian's `requestUrl`, bypassing CORS restrictions for OpenAI-compatible providers. - Integrate `obsidianFetch` --- .gitignore | 4 + src/index.ts | 258 ++++++++++++++++++++++++-------------- src/settings/settings.tsx | 22 ++-- src/util/geminiAiUtils.ts | 116 ++++++++--------- src/util/obsidianFetch.ts | 90 +++++++++++++ src/util/openAiUtils.ts | 12 +- 6 files changed, 339 insertions(+), 163 deletions(-) create mode 100644 src/util/obsidianFetch.ts diff --git a/.gitignore b/.gitignore index cf31756..04a701c 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,7 @@ data.json # Exclude macOS Finder (System Explorer) View States .DS_Store + +# AI tooling +.codemie +.claude diff --git a/src/index.ts b/src/index.ts index 60d44e7..9ba152d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,7 +23,10 @@ import { saveAudioRecording, updateFrontMatter, } from './util/fileUtils'; -import { summarizeTranscriptGemini } from './util/geminiAiUtils'; +import { + llmFixMermaidChartGemini, + summarizeTranscriptGemini, +} from './util/geminiAiUtils'; import { mimeTypeToFileExtension, type SupportedMimeType, @@ -69,7 +72,7 @@ export interface ScribeOptions { export default class ScribePlugin extends Plugin { settings: ScribePluginSettings = DEFAULT_SETTINGS; state: ScribeState = DEFAULT_STATE; - controlModal: ScribeControlsModal; + controlModal!: ScribeControlsModal; private recordingNotice: Notice | null = null; private recordingNoticeIntervalId: number | null = null; public recordingNoticeStartTime: number | null = null; @@ -137,7 +140,7 @@ export default class ScribePlugin extends Plugin { await newRecording.startRecording(this.settings.selectedAudioDeviceId); this.recordingNoticeStartTime = newRecording.startTime; new Notice('Scribe: 🎙️ Recording started'); - } catch (error) { + } catch (error: unknown) { this.state.audioRecord = null; new Notice('Scribe: ⚠️ Unable to start recording'); throw error; @@ -224,8 +227,10 @@ export default class ScribePlugin extends Plugin { await this.app.vault.delete(recordingFile); new Notice(`Scribe: ✅🗑️ Audio file deleted ${fileName}`); } - } catch (error) { - new Notice(`Scribe: Something went wrong ${error.toString()}`); + } catch (error: unknown) { + new Notice( + `Scribe: Something went wrong ${error instanceof Error ? error.message : String(error)}`, + ); console.error('Scribe: Something went wrong', error); } finally { await this.cleanup(); @@ -281,8 +286,10 @@ export default class ScribePlugin extends Plugin { audioRecordingBuffer: audioFileBuffer, scribeOptions: scribeOptions, }); - } catch (error) { - new Notice(`Scribe: Something went wrong ${error.toString()}`); + } catch (error: unknown) { + new Notice( + `Scribe: Something went wrong ${error instanceof Error ? error.message : String(error)}`, + ); console.error('Scribe: Something went wrong', error); } finally { await this.cleanup(); @@ -298,44 +305,73 @@ export default class ScribePlugin extends Plugin { return data; }); - let fixedMermaidChart: string | undefined; - if (brokenMermaidChart) { - const customBaseUrl = this.settings.useCustomOpenAiBaseUrl - ? this.settings.customOpenAiBaseUrl - : undefined; - const customChatModel = this.settings.useCustomOpenAiBaseUrl - ? this.settings.customChatModel - : undefined; - - fixedMermaidChart = ( - await llmFixMermaidChart( - this.settings.openAiApiKey, - brokenMermaidChart, - this.settings.llmModel, - customBaseUrl, - customChatModel, - ) - ).mermaidChart; - } + if (!brokenMermaidChart) return; - if (brokenMermaidChart && fixedMermaidChart) { + const fixedMermaidChart = + await this.handleFixMermaidChart(brokenMermaidChart); + + if (fixedMermaidChart) { await this.app.vault.process(file, (data) => { brokenMermaidChart = extractMermaidChart(data); - return data.replace( brokenMermaidChart as string, - `${fixedMermaidChart} -`, + `${fixedMermaidChart}\n`, ); }); } - } catch (error) { - new Notice(`Scribe: Something went wrong ${error.toString()}`); + } catch (error: unknown) { + new Notice( + `Scribe: Something went wrong ${error instanceof Error ? error.message : String(error)}`, + ); } finally { await this.cleanup(); } } + private async handleFixMermaidChart( + brokenMermaidChart: string, + ): Promise { + const processingPlatform = this.settings.processPlatform; + + if (!(processingPlatform in PROCESS_PLATFORM)) { + const errorText = `Chosen AI provider not supported: ${processingPlatform}`; + new Notice(errorText); + throw Error(errorText); + } + + let mermaidChart: string; + + switch (processingPlatform) { + case PROCESS_PLATFORM.openAi: + ({ mermaidChart } = await llmFixMermaidChart( + this.settings.openAiApiKey, + brokenMermaidChart, + this.settings.llmModel, + )); + break; + + case PROCESS_PLATFORM.customOpenAi: + ({ mermaidChart } = await llmFixMermaidChart( + this.settings.openAiApiKey, + brokenMermaidChart, + this.settings.llmModel, + this.settings.customOpenAiBaseUrl, + this.settings.customChatModel, + )); + break; + + case PROCESS_PLATFORM.google: + ({ mermaidChart } = await llmFixMermaidChartGemini( + this.settings.googleAiApiKey, + brokenMermaidChart, + this.settings.googleModel, + )); + break; + } + + return mermaidChart; + } + async handleStopAndSaveRecording(baseFileName: string) { const audioRecord = this.state.audioRecord as AudioRecord; @@ -468,44 +504,62 @@ export default class ScribePlugin extends Plugin { audioBuffer: ArrayBuffer, scribeOptions: ScribeOptions, ) { + const transcriptPlatform = this.settings.transcriptPlatform; + const isTranscribeDisabled = this.settings.isDisableLlmTranscription; + if (isTranscribeDisabled) { + new Notice('Scribe: 🎧 Transcription is disabled in settings'); + return ''; + } + + if (!(transcriptPlatform in TRANSCRIPT_PLATFORM)) { + const errorText = `Choosen AI provider not supported: ${transcriptPlatform}`; + new Notice(errorText); + throw Error(errorText); + } + try { - if (this.settings.isDisableLlmTranscription) { - new Notice('Scribe: 🎧 Transcription is disabled in settings'); - return ''; - } + new Notice(`Scribe: 🎧 Beginning transcription w/ ${transcriptPlatform}`); - new Notice( - `Scribe: 🎧 Beginning transcription w/ ${this.settings.transcriptPlatform}`, - ); - const transcript = - this.settings.transcriptPlatform === TRANSCRIPT_PLATFORM.assemblyAi - ? await transcribeAudioWithAssemblyAi( - this.settings.assemblyAiApiKey, - audioBuffer, - scribeOptions, - ) - : await chunkAndTranscribeWithOpenAi( - this.settings.openAiApiKey, - audioBuffer, - scribeOptions, - this.settings.useCustomOpenAiBaseUrl - ? this.settings.customOpenAiBaseUrl - : undefined, - this.settings.useCustomOpenAiBaseUrl - ? this.settings.customTranscriptModel - : undefined, - ); + let transcript: string; + + switch (transcriptPlatform) { + case TRANSCRIPT_PLATFORM.assemblyAi: + transcript = await transcribeAudioWithAssemblyAi( + this.settings.assemblyAiApiKey, + audioBuffer, + scribeOptions, + ); + break; + + case TRANSCRIPT_PLATFORM.customOpenAi: + transcript = await chunkAndTranscribeWithOpenAi( + this.settings.openAiApiKey, + audioBuffer, + scribeOptions, + this.settings.customOpenAiBaseUrl, + this.settings.customTranscriptModel, + ); + break; + + case TRANSCRIPT_PLATFORM.openAi: + transcript = await chunkAndTranscribeWithOpenAi( + this.settings.openAiApiKey, + audioBuffer, + scribeOptions, + ); + break; + } new Notice( - `Scribe: 🎧 Completed transcription w/ ${this.settings.transcriptPlatform}`, + `Scribe: 🎧 Completed transcription w/ ${transcriptPlatform}`, ); return transcript; - } catch (error) { + } catch (error: unknown) { new Notice( `Scribe: 🎧 🛑 Something went wrong trying to Transcribe w/ ${ - this.settings.transcriptPlatform + transcriptPlatform } - ${error.toString()}`, + ${error instanceof Error ? error.message : String(error)}`, ); console.error; @@ -517,44 +571,66 @@ export default class ScribePlugin extends Plugin { transcript: string, scribeOptions: ScribeOptions, ) { - new Notice('Scribe: 🧠 Sending to LLM to summarize'); + const processingPlatform = this.settings.processPlatform; - const customBaseUrl = this.settings.useCustomOpenAiBaseUrl - ? this.settings.customOpenAiBaseUrl - : undefined; - const customChatModel = this.settings.useCustomOpenAiBaseUrl - ? this.settings.customChatModel - : undefined; + if (!(processingPlatform in PROCESS_PLATFORM)) { + const errorText = `Chosen AI provider not supported: ${this.settings.transcriptPlatform}`; + new Notice(errorText); + throw Error(errorText); + } let llmSummary: Record & { fileTitle: string; }; - switch (this.settings.processPlatform) { - case PROCESS_PLATFORM.openAi: - case PROCESS_PLATFORM.customOpenAi: - llmSummary = await summarizeTranscript( - this.settings.openAiApiKey, - transcript, - scribeOptions, - this.settings.llmModel, - customBaseUrl, - customChatModel, - ); - break; - case PROCESS_PLATFORM.google: - llmSummary = await summarizeTranscriptGemini( - this.settings.googleAiApiKey, - transcript, - scribeOptions, - this.settings.googleModel, - ); - break; - } + try { + new Notice(`Scribe: 🧠 Sending to ${processingPlatform} to summarize`); + + switch (processingPlatform) { + case PROCESS_PLATFORM.openAi: + llmSummary = await summarizeTranscript( + this.settings.openAiApiKey, + transcript, + scribeOptions, + this.settings.llmModel, + ); + break; + + case PROCESS_PLATFORM.customOpenAi: + llmSummary = await summarizeTranscript( + this.settings.openAiApiKey, + transcript, + scribeOptions, + this.settings.llmModel, + this.settings.customOpenAiBaseUrl, + this.settings.customChatModel, + ); + break; + + case PROCESS_PLATFORM.google: + llmSummary = await summarizeTranscriptGemini( + this.settings.googleAiApiKey, + transcript, + scribeOptions, + this.settings.googleModel, + ); + break; + } + + new Notice('Scribe: 🧠 LLM summation complete'); - new Notice('Scribe: 🧠 LLM summation complete'); + return llmSummary; + } catch (error: unknown) { + new Notice( + `Scribe: 🎧 🛑 Something went wrong trying to Transcribe w/ ${ + processingPlatform + } + ${error instanceof Error ? error.message : String(error)}`, + ); - return llmSummary; + console.error; + throw error; + } } cleanup() { diff --git a/src/settings/settings.tsx b/src/settings/settings.tsx index 1110ca2..42cde16 100644 --- a/src/settings/settings.tsx +++ b/src/settings/settings.tsx @@ -1,28 +1,25 @@ import { type App, PluginSettingTab, Setting } from 'obsidian'; -import { type Root, createRoot } from 'react-dom/client'; -import { useDebounce } from 'src/util/useDebounce'; +import { useState } from 'react'; +import { createRoot, type Root } from 'react-dom/client'; import type ScribePlugin from 'src'; - +import { LanguageOptions, type OutputLanguageOptions } from 'src/util/consts'; import { LLM_MODELS as GOOGLE_MODELS } from 'src/util/geminiAiUtils'; import { LLM_MODELS } from 'src/util/openAiUtils'; - -import { useState } from 'react'; -import { LanguageOptions, type OutputLanguageOptions } from 'src/util/consts'; -import GeneralSettingsTab from './GeneralSettingsTab'; +import { useDebounce } from 'src/util/useDebounce'; import { ProviderSettingsTab } from './ai-provider-settings-tab'; import { DEFAULT_TEMPLATE, NoteTemplateSettings, type ScribeTemplate, } from './components/NoteTemplateSettings'; +import GeneralSettingsTab from './GeneralSettingsTab'; import { SettingsFormProvider } from './provider/SettingsFormProvider'; export enum TRANSCRIPT_PLATFORM { assemblyAi = 'assemblyAi', openAi = 'openAi', customOpenAi = 'customOpenAi', - // google = 'google', } export enum PROCESS_PLATFORM { openAi = 'openAi', @@ -57,13 +54,13 @@ export interface ScribePluginSettings { selectedAudioDeviceId: string; audioFileFormat: 'webm' | 'mp3'; // Custom OpenAI settings - useCustomOpenAiBaseUrl: boolean; // TODO replace with processPlatform, transcriptPlatform + // useCustomOpenAiBaseUrl: boolean; // replaced with processPlatform and transcriptPlatform customOpenAiBaseUrl: string; customTranscriptModel: string; customChatModel: string; - // Process platform settings + // --- Process platform settings --- processPlatform: PROCESS_PLATFORM; - // Gemini settings + // --- Gemini settings --- googleModel: GOOGLE_MODELS; googleAiApiKey: string; } @@ -91,7 +88,6 @@ export const DEFAULT_SETTINGS: ScribePluginSettings = { selectedAudioDeviceId: '', audioFileFormat: 'webm', // Custom OpenAI settings - useCustomOpenAiBaseUrl: false, customOpenAiBaseUrl: '', customTranscriptModel: 'whisper-1', customChatModel: 'gpt-4o', @@ -106,7 +102,7 @@ export async function handleSettingsTab(plugin: ScribePlugin) { export class ScribeSettingsTab extends PluginSettingTab { plugin: ScribePlugin; - reactRoot: Root | null; + reactRoot: Root | null = null; constructor(app: App, plugin: ScribePlugin) { super(app, plugin); diff --git a/src/util/geminiAiUtils.ts b/src/util/geminiAiUtils.ts index 18cb164..65ea43b 100644 --- a/src/util/geminiAiUtils.ts +++ b/src/util/geminiAiUtils.ts @@ -39,9 +39,9 @@ export async function summarizeTranscriptGemini( llmModel: LLM_MODELS = LLM_MODELS['gemini-2.0-flash-lite'], ) { const systemPrompt = ` - You are "Scribe" an expert note-making AI for Obsidian you specialize in the Linking Your Thinking (LYK) strategy. - The following is the transcription generated from a recording of someone talking aloud or multiple people in a conversation. - There may be a lot of random things said given fluidity of conversation or thought process and the microphone's ability to pick up all audio. + You are "Scribe" an expert note-making AI for Obsidian you specialize in the Linking Your Thinking (LYK) strategy. + The following is the transcription generated from a recording of someone talking aloud or multiple people in a conversation. + There may be a lot of random things said given fluidity of conversation or thought process and the microphone's ability to pick up all audio. The transcription may address you by calling you "Scribe" or saying "Hey Scribe" and asking you a question, they also may just allude to you by asking "you" to do something. Give them the answers to this question @@ -52,36 +52,38 @@ export async function summarizeTranscriptGemini( - Clean - Logical - Insightful - + It will be nested under a h2 # tag, feel free to nest headers underneath it Rules: - - Do not include escaped new line characters - - Do not mention "the speaker" anywhere in your response. + - Use actual line breaks in your Markdown output (e.g. newlines between bullet points, code block lines, etc.). Do NOT write literal \\n characters. + - Do not mention "the speaker" anywhere in your response. - The notes should be written as if I were writing them. - - ${scribeOutputLanguage ? `Respond in ${scribeOutputLanguage} language` : ''} + - CRITICAL: Each JSON field value must contain ONLY the section content. Do NOT open the value with a heading that repeats the section name (e.g. never start a value with "## Summary" or "## Insights"). Jump straight into the content. + ${scribeOutputLanguage ? `- Please respond in ${scribeOutputLanguage} language.` : ''} `; - const transcriptMessage = ` + const humanMessage = ` The following is the transcribed audio: - ${transcript.trim()} + ${transcript} `; - const modelToUse = llmModel; - const model = new ChatGoogleGenerativeAI({ - model: modelToUse, + model: llmModel, apiKey: openAiKey, temperature: 0.5, }); const messages: BaseMessage[] = [ - new SystemMessage(systemPrompt.trim()), - new HumanMessage(transcriptMessage), + new SystemMessage(systemPrompt), + new HumanMessage(humanMessage), ]; + // Strip any leading markdown heading line Gemini injects before the content. + const stripLeadingHeading = (s: string) => + s.replace(/^#{1,6}\s+[^\n]*\n?/, '').trimStart(); + const schema: Record> = { fileTitle: z .string() @@ -93,8 +95,12 @@ export async function summarizeTranscriptGemini( activeNoteTemplate.sections.forEach((section) => { const { sectionHeader, sectionInstructions, isSectionOptional } = section; schema[convertToSafeJsonKey(sectionHeader)] = isSectionOptional - ? z.string().optional().describe(sectionInstructions) - : z.string().describe(sectionInstructions); + ? z + .string() + .nullable() + .transform((v) => (v ? stripLeadingHeading(v) : v)) + .describe(sectionInstructions) + : z.string().transform(stripLeadingHeading).describe(sectionInstructions); }); const structuredOutput = z.object(schema); @@ -105,45 +111,41 @@ export async function summarizeTranscriptGemini( string > & { fileTitle: string }; - return result; + return await result; } -// export async function llmFixMermaidChart( -// openAiKey: string, -// brokenMermaidChart: string, -// llmModel: LLM_MODELS = LLM_MODELS['gpt-4o'], -// customBaseUrl?: string, -// customChatModel?: string, -// ) { -// const systemPrompt = ` -// You are an expert in mermaid mindmaps and Obsidian (the note taking app) -// Below is a that isn't rendering correctly in Obsidian -// There may be some new line characters, or tab characters, or special characters. -// Strip them out and only return a fully valid unicode Mermaid mindmap that will render properly in Obsidian -// Remove any special characters in the nodes text that isn't valid. - -// -// ${brokenMermaidChart} -// - -// Thank you -// `; -// const modelToUse = customChatModel || llmModel; -// const model = new ChatOpenAI({ -// model: modelToUse, -// apiKey: openAiKey, -// temperature: 0.3, -// ...(customBaseUrl && { configuration: { baseURL: customBaseUrl } }), -// }); -// const messages = [new SystemMessage(systemPrompt)]; -// const structuredOutput = z.object({ -// mermaidChart: z -// .string() -// .describe('A fully valid unicode mermaid mindmap diagram'), -// }); - -// const structuredLlm = model.withStructuredOutput(structuredOutput); -// const { mermaidChart } = await structuredLlm.invoke(messages); - -// return { mermaidChart }; -// } +export async function llmFixMermaidChartGemini( + googleAiKey: string, + brokenMermaidChart: string, + llmModel: LLM_MODELS = LLM_MODELS['gemini-2.0-flash-lite'], +) { + const systemPrompt = ` +You are an expert in mermaid charts and Obsidian (the note taking app) +Below is a that isn't rendering correctly in Obsidian +There may be some new line characters, or tab characters, or special characters. +Strip them out and only return a fully valid unicode Mermaid chart that will render properly in Obsidian +Remove any special characters in the nodes text that isn't valid. + + +${brokenMermaidChart} + + +Thank you + `; + + const model = new ChatGoogleGenerativeAI({ + model: llmModel, + apiKey: googleAiKey, + temperature: 0.3, + }); + + const messages: BaseMessage[] = [new SystemMessage(systemPrompt)]; + const structuredOutput = z.object({ + mermaidChart: z.string().describe('A fully valid unicode mermaid chart'), + }); + + const structuredLlm = model.withStructuredOutput(structuredOutput); + const { mermaidChart } = await structuredLlm.invoke(messages); + + return { mermaidChart }; +} diff --git a/src/util/obsidianFetch.ts b/src/util/obsidianFetch.ts new file mode 100644 index 0000000..a1ceb5c --- /dev/null +++ b/src/util/obsidianFetch.ts @@ -0,0 +1,90 @@ +import { + type RequestUrlParam, + type RequestUrlResponse, + requestUrl, +} from 'obsidian'; +import type { Fetch } from 'openai/internal/builtin-types'; + +/** + * A custom 'fetch' implementation that wraps Obsidian's 'requestUrl()' to + * avoid CORS issues with OpenAI-compatible providers (e.g. Fireworks, Gemini). + * Works with both the OpenAI SDK and LangChain's ChatOpenAI. + * @example + * const client = new OpenAI({ fetch: obsidianFetch }) + * const model = new ChatOpenAI({ configuration: { fetch: obsidianFetch } }) + */ +export const obsidianFetch: Fetch = async (requestInfo, init) => { + // Always normalize to a Request object so the browser handles FormData + // serialization, including generating the multipart/form-data boundary in + // the content-type header. Without this, passing FormData through init.body + // and calling .toString() on it produces "[object FormData]". + const req = + requestInfo instanceof Request + ? requestInfo + : new Request(requestInfo, init ?? undefined); + + const { url } = req; + + // The OpenAI SDK occasionally calls fetch('data:,...') internally to read + // file content. Delegate to native fetch — Obsidian's requestUrl only + // handles http/https. + if (url.startsWith('data:')) { + console.debug( + '[obsidianFetch] data: URL — delegating to native fetch', + url.slice(0, 40), + ); + return fetch(requestInfo, init); + } + + if (!url.startsWith('http://') && !url.startsWith('https://')) { + const msg = + `Invalid URL protocol — only http/https are supported. Got: "${url}". ` + + `If you're using a custom base URL, make sure it includes "https://".`; + console.error('[obsidianFetch]', msg); + throw new Error(msg); + } + + const headersObj: Record = {}; + req.headers.forEach((value, key) => { + headersObj[key] = value; + }); + + const bodyBuffer = req.body ? await req.arrayBuffer() : undefined; + + const obsidianParams: RequestUrlParam = { + url, + method: req.method, + headers: headersObj, + body: bodyBuffer, + throw: false, // Don't throw on non-2xx — let the OpenAI SDK handle error responses + }; + + const bodySize = bodyBuffer ? `${bodyBuffer.byteLength} bytes` : 'none'; + const contentType = headersObj['content-type'] ?? '(none)'; + console.debug( + '[obsidianFetch] →', + req.method, + url, + '| content-type:', + contentType, + '| body:', + bodySize, + ); + + const obsidianResponse = await requestUrl(obsidianParams); + console.debug('[obsidianFetch] ←', obsidianResponse.status, url); + if (obsidianResponse.status >= 400) { + console.debug('[obsidianFetch] error body:', obsidianResponse.text); + } + return obsidianResponseToResponse(obsidianResponse); +}; + +function obsidianResponseToResponse( + obsidianResponse: RequestUrlResponse, +): Response { + return new Response(obsidianResponse.text, { + status: obsidianResponse.status, + statusText: '', + headers: new Headers(obsidianResponse.headers), + }); +} diff --git a/src/util/openAiUtils.ts b/src/util/openAiUtils.ts index d4e6bdb..2730850 100644 --- a/src/util/openAiUtils.ts +++ b/src/util/openAiUtils.ts @@ -13,6 +13,7 @@ import type { ScribeOptions } from 'src'; import { z } from 'zod'; import audioDataToChunkedFiles from './audioDataToChunkedFiles'; import { LanguageOptions } from './consts'; +import { obsidianFetch } from './obsidianFetch'; import { convertToSafeJsonKey } from './textUtil'; export enum LLM_MODELS { @@ -35,6 +36,7 @@ export async function chunkAndTranscribeWithOpenAi( const openAiClient = new OpenAI({ apiKey: openAiKey, dangerouslyAllowBrowser: true, + fetch: obsidianFetch, ...(customBaseUrl && { baseURL: customBaseUrl }), }); const audioFiles = await audioDataToChunkedFiles(audioBuffer, MAX_CHUNK_SIZE); @@ -136,7 +138,10 @@ export async function summarizeTranscript( model: modelToUse, apiKey: openAiKey, temperature: 0.5, - ...(customBaseUrl && { configuration: { baseURL: customBaseUrl } }), + configuration: { + ...(customBaseUrl && { baseURL: customBaseUrl }), + fetch: obsidianFetch, + }, }); const messages = [new SystemMessage(systemPrompt)]; @@ -196,7 +201,10 @@ Thank you model: modelToUse, apiKey: openAiKey, temperature: 0.3, - ...(customBaseUrl && { configuration: { baseURL: customBaseUrl } }), + configuration: { + ...(customBaseUrl && { baseURL: customBaseUrl }), + fetch: obsidianFetch, + }, }); const messages = [new SystemMessage(systemPrompt)]; const structuredOutput = z.object({ From 1ff6b1512e15075e50482c464edee7c59b9b7763 Mon Sep 17 00:00:00 2001 From: Philipp Rich Date: Sun, 3 May 2026 12:10:21 +0400 Subject: [PATCH 8/9] refactor(ai): improve gemini prompt reliability and sanitize request schemas - Update `llmFixMermaidChartGemini` to use explicit HumanMessage and enforce real newline characters in the output. - Add debug logging to Gemini summarization and mermaid chart fixing functions. - Modify `obsidianFetch` to strip `$schema` and `title` from JSON schemas to ensure compatibility with providers like Groq. - Remove unused --- src/util/geminiAiUtils.ts | 33 ++++++++++++++++++++++----------- src/util/obsidianFetch.ts | 22 +++++++++++++++++++++- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/util/geminiAiUtils.ts b/src/util/geminiAiUtils.ts index 65ea43b..9f2a4a3 100644 --- a/src/util/geminiAiUtils.ts +++ b/src/util/geminiAiUtils.ts @@ -2,7 +2,6 @@ import { ChatGoogleGenerativeAI } from '@langchain/google-genai'; import { type BaseMessage, HumanMessage, SystemMessage } from 'langchain'; import type { ScribeOptions } from 'src'; import { z } from 'zod'; -import type { LanguageOptions } from './consts'; import { convertToSafeJsonKey } from './textUtil'; export enum LLM_MODELS { @@ -106,10 +105,12 @@ export async function summarizeTranscriptGemini( const structuredOutput = z.object(schema); const structuredLlm = model.withStructuredOutput(structuredOutput); + console.debug('[gemini] → summarizeTranscript', llmModel, `transcript: ${transcript.length} chars`); const result = (await structuredLlm.invoke(messages)) as Record< string, string > & { fileTitle: string }; + console.debug('[gemini] ← summarizeTranscript', `fileTitle: "${result.fileTitle}"`); return await result; } @@ -119,19 +120,18 @@ export async function llmFixMermaidChartGemini( brokenMermaidChart: string, llmModel: LLM_MODELS = LLM_MODELS['gemini-2.0-flash-lite'], ) { - const systemPrompt = ` -You are an expert in mermaid charts and Obsidian (the note taking app) -Below is a that isn't rendering correctly in Obsidian + const systemPrompt = `You are an expert in mermaid charts and Obsidian (the note taking app). +You will be given a broken mermaid chart that isn't rendering correctly in Obsidian. There may be some new line characters, or tab characters, or special characters. -Strip them out and only return a fully valid unicode Mermaid chart that will render properly in Obsidian +Strip them out and only return a fully valid unicode Mermaid chart that will render properly in Obsidian. Remove any special characters in the nodes text that isn't valid. +CRITICAL: Use actual newline characters between each line of the chart. Do NOT write literal \\n characters.`; + + const humanMessage = `Please fix the following broken mermaid chart: ${brokenMermaidChart} - - -Thank you - `; +`; const model = new ChatGoogleGenerativeAI({ model: llmModel, @@ -139,13 +139,24 @@ Thank you temperature: 0.3, }); - const messages: BaseMessage[] = [new SystemMessage(systemPrompt)]; + const messages: BaseMessage[] = [ + new SystemMessage(systemPrompt), + new HumanMessage(humanMessage), + ]; const structuredOutput = z.object({ - mermaidChart: z.string().describe('A fully valid unicode mermaid chart'), + mermaidChart: z + .string() + .transform((s) => s.replace(/\\n/g, '\n')) + .describe( + 'A fully valid unicode mermaid chart with real newline characters between each line', + ), }); const structuredLlm = model.withStructuredOutput(structuredOutput); + + console.debug('[gemini] → fixMermaidChart', llmModel, `chart: ${brokenMermaidChart.length} chars`); const { mermaidChart } = await structuredLlm.invoke(messages); + console.debug('[gemini] ← fixMermaidChart', `result: ${mermaidChart.length} chars`); return { mermaidChart }; } diff --git a/src/util/obsidianFetch.ts b/src/util/obsidianFetch.ts index a1ceb5c..44ab34f 100644 --- a/src/util/obsidianFetch.ts +++ b/src/util/obsidianFetch.ts @@ -49,7 +49,27 @@ export const obsidianFetch: Fetch = async (requestInfo, init) => { headersObj[key] = value; }); - const bodyBuffer = req.body ? await req.arrayBuffer() : undefined; + let bodyBuffer = req.body ? await req.arrayBuffer() : undefined; + + // Groq (and some other providers) reject JSON schemas that include + // meta-fields like `$schema`, `title`, or `additionalProperties`. + // Strip them from response_format.json_schema.schema before sending. + if ( + bodyBuffer && + (headersObj['content-type'] ?? '').includes('application/json') + ) { + try { + const bodyJson = JSON.parse(new TextDecoder().decode(bodyBuffer)); + const schema = bodyJson?.response_format?.json_schema?.schema; + if (schema && typeof schema === 'object') { + delete schema.$schema; + delete schema.title; + bodyBuffer = new TextEncoder().encode(JSON.stringify(bodyJson)).buffer as ArrayBuffer; + } + } catch { + // Not valid JSON or no schema to strip — leave body unchanged + } + } const obsidianParams: RequestUrlParam = { url, From 47fcd2f408fbc8161ad87473140c675f90b452ff Mon Sep 17 00:00:00 2001 From: Philipp Rich Date: Sun, 3 May 2026 12:25:23 +0400 Subject: [PATCH 9/9] chore(settings): implement settings migration logic Introduce `migrateSettings` to handle updates to the plugin configuration structure, ensuring backward compatibility when loading saved user data. --- src/index.ts | 5 +++-- src/settings/migration.ts | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 src/settings/migration.ts diff --git a/src/index.ts b/src/index.ts index 9ba152d..66c122c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ import { handleCommands } from './commands/commands'; import { ScribeControlsModal } from './modal/scribeControlsModal'; import { handleRibbon } from './ribbon/ribbon'; import type { ScribeTemplate } from './settings/components/NoteTemplateSettings'; +import { migrateSettings } from './settings/migration'; import { DEFAULT_SETTINGS, handleSettingsTab, @@ -96,8 +97,8 @@ export default class ScribePlugin extends Plugin { onunload() {} async loadSettings() { - const savedUserData: ScribePluginSettings = await this.loadData(); - this.settings = { ...DEFAULT_SETTINGS, ...savedUserData }; + const savedUserData = await this.loadData(); + this.settings = { ...DEFAULT_SETTINGS, ...migrateSettings(savedUserData ?? {}) }; const defaultPathSettings = await getDefaultPathSettings(this); diff --git a/src/settings/migration.ts b/src/settings/migration.ts new file mode 100644 index 0000000..3bff587 --- /dev/null +++ b/src/settings/migration.ts @@ -0,0 +1,30 @@ +import { PROCESS_PLATFORM, TRANSCRIPT_PLATFORM, type ScribePluginSettings } from './settings'; + +interface LegacySettings extends Partial { + useCustomOpenAiBaseUrl?: boolean; +} + +/** + * Migrates settings saved by older plugin versions to the current shape. + * Called once during loadSettings before merging with DEFAULT_SETTINGS. + */ +export function migrateSettings(saved: LegacySettings): Partial { + const migrated = { ...saved } as LegacySettings & Partial; + + // v1 → v2: useCustomOpenAiBaseUrl replaced by processPlatform/transcriptPlatform + if (migrated.useCustomOpenAiBaseUrl === true) { + if (!migrated.processPlatform) { + migrated.processPlatform = PROCESS_PLATFORM.customOpenAi; + } + if ( + !migrated.transcriptPlatform || + migrated.transcriptPlatform === TRANSCRIPT_PLATFORM.openAi + ) { + migrated.transcriptPlatform = TRANSCRIPT_PLATFORM.customOpenAi; + } + } + + delete (migrated as LegacySettings).useCustomOpenAiBaseUrl; + + return migrated; +}