From 5536d541f89094ade06f3203b5d65cb8e481b4f2 Mon Sep 17 00:00:00 2001 From: Dhravya Shah Date: Fri, 16 Jan 2026 12:07:46 -0800 Subject: [PATCH 1/6] trying to make gemini live work --- backend/requirements.txt | 2 +- backend/server.py | 143 ++++++++++++++++++++++++++------------- wrangler.jsonc | 1 - 3 files changed, 98 insertions(+), 48 deletions(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 07c46f6..e33b4fb 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -3,6 +3,6 @@ uvicorn[standard] websockets python-dotenv loguru -pipecat-ai[openai,silero] +pipecat-ai[google,silero] protobuf supermemory-pipecat>=0.1.0 diff --git a/backend/server.py b/backend/server.py index a74a2c0..3b41314 100644 --- a/backend/server.py +++ b/backend/server.py @@ -9,29 +9,51 @@ import os import sys import uuid -from dotenv import load_dotenv -from loguru import logger -from fastapi import FastAPI, WebSocket, Request, Query +from dotenv import load_dotenv +from fastapi import FastAPI, Query, Request, WebSocket from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse - +from google.genai import types +from google.genai.types import EndSensitivity, StartSensitivity +from loguru import logger +from pipecat.audio.interruptions.min_words_interruption_strategy import ( + MinWordsInterruptionStrategy, +) from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.frames.frames import LLMMessagesFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask -from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext -from pipecat.processors.frameworks.rtvi import RTVIConfig, RTVIObserver, RTVIProcessor +from pipecat.processors.aggregators.llm_response_universal import ( + LLMContextAggregatorPair, + LLMUserAggregatorParams, +) +from pipecat.processors.frameworks.rtvi import ( + RTVIConfig, + RTVIObserver, + RTVIObserverParams, + RTVIProcessor, +) from pipecat.serializers.protobuf import ProtobufFrameSerializer -from pipecat.services.openai.llm import OpenAILLMService -from pipecat.services.openai.tts import OpenAITTSService -from pipecat.services.openai.stt import OpenAISTTService +from pipecat.services.google.gemini_live.llm import ( + GeminiLiveContext, + GeminiLiveLLMService, + GeminiVADParams, + InputParams, +) +from pipecat.services.google.google import LLMContext from pipecat.transports.websocket.fastapi import ( FastAPIWebsocketParams, FastAPIWebsocketTransport, ) - +from pipecat.turns.user_start.external_user_turn_start_strategy import ( + ExternalUserTurnStartStrategy, +) +from pipecat.turns.user_stop.external_user_turn_stop_strategy import ( + ExternalUserTurnStopStrategy, +) +from pipecat.turns.user_turn_strategies import UserTurnStrategies from supermemory_pipecat import SupermemoryPipecatService load_dotenv(override=True) @@ -55,7 +77,7 @@ SYSTEM_PROMPT = """You are a helpful voice assistant with memory capabilities. You remember information from past conversations and use it to provide personalized responses. Keep your responses brief and conversational - one or two sentences at most. -Your output will be converted to audio so don't include special characters.""" +Your output will be converted to audio so don't include special characters. Ask for their name and greet them. Only speak when you need to. When giving an introduction, just say something along the lines of 'I am a memory assistant powered by supermemory'. What's your name? (don't ask the name if you already know it but you get the point)""" async def run_bot(websocket_client, user_id: str, session_id: str): @@ -76,18 +98,26 @@ async def run_bot(websocket_client, user_id: str, session_id: str): ), ) - stt = OpenAISTTService( - api_key=os.getenv("OPENAI_API_KEY"), - ) - - llm = OpenAILLMService( - api_key=os.getenv("OPENAI_API_KEY"), - model="gpt-4o-mini", - ) - - tts = OpenAITTSService( - api_key=os.getenv("OPENAI_API_KEY"), - voice="alloy", + gemini_api_key = os.getenv("GEMINI_API_KEY") + if not gemini_api_key: + raise ValueError("GEMINI_API_KEY is not set") + + llm = GeminiLiveLLMService( + api_key=gemini_api_key, + voice_id="Puck", + system_instruction=SYSTEM_PROMPT, + transcribe_user_audio=True, + transcribe_model_audio=True, + inference_on_context_initialization=True, + params=InputParams( + vad=GeminiVADParams( + disabled=False, + start_sensitivity=StartSensitivity.START_SENSITIVITY_HIGH, # Detect speech quickly + end_sensitivity=EndSensitivity.END_SENSITIVITY_HIGH, # Allow longer pauses + prefix_padding_ms=300, # Keep 300ms before speech + silence_duration_ms=500, # Allow longer pauses + ), + ), ) # Supermemory service with user-specific context @@ -99,32 +129,31 @@ async def run_bot(websocket_client, user_id: str, session_id: str): search_limit=10, search_threshold=0.1, mode="full", - add_memory="always", ), ) - messages = [ - { - "role": "system", - "content": SYSTEM_PROMPT, - }, - ] - context = OpenAILLMContext(messages) - context_aggregator = llm.create_context_aggregator(context) + context = LLMContext([{"role": "system", "content": SYSTEM_PROMPT}]) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair( + context, + user_params=LLMUserAggregatorParams( + user_turn_strategies=UserTurnStrategies( + start=[ExternalUserTurnStartStrategy()], + stop=[ExternalUserTurnStopStrategy()], + ), + ), + ) rtvi = RTVIProcessor(config=RTVIConfig(config=[])) pipeline = Pipeline( [ transport.input(), - stt, - rtvi, - context_aggregator.user(), memory, + rtvi, + user_aggregator, llm, - tts, transport.output(), - context_aggregator.assistant(), + assistant_aggregator, ] ) @@ -135,18 +164,38 @@ async def run_bot(websocket_client, user_id: str, session_id: str): enable_metrics=True, enable_usage_metrics=True, ), - observers=[RTVIObserver(rtvi)], + observers=[ + RTVIObserver( + rtvi, + params=RTVIObserverParams( + bot_tts_enabled=True, + bot_llm_enabled=True, + bot_output_enabled=True, + bot_speaking_enabled=True, + user_transcription_enabled=True, + user_speaking_enabled=True, + ), + ) + ], + enable_turn_tracking=True, ) @rtvi.event_handler("on_client_ready") async def on_client_ready(rtvi): logger.info(f"Client ready for user: {user_id}") await rtvi.set_bot_ready() - await task.queue_frames([ - LLMMessagesFrame([ - {"role": "system", "content": "Greet the user warmly and briefly introduce yourself as a memory-enabled assistant."} - ]) - ]) + await task.queue_frames( + [ + LLMMessagesFrame( + [ + { + "role": "system", + "content": "Greet the user warmly and ask them a question (like their name) don't say much please.", + } + ] + ) + ] + ) @transport.event_handler("on_client_connected") async def on_client_connected(transport, client): @@ -180,9 +229,11 @@ async def connect( ws_protocol = "wss" if os.getenv("USE_SSL", "false").lower() == "true" else "ws" # Pipecat client only expects wsUrl in response - embed userId/sessionId in URL - return JSONResponse({ - "wsUrl": f"{ws_protocol}://{ws_host}/ws?userId={user_id}&sessionId={session_id}", - }) + return JSONResponse( + { + "wsUrl": f"{ws_protocol}://{ws_host}/ws?userId={user_id}&sessionId={session_id}", + } + ) @app.websocket("/ws") diff --git a/wrangler.jsonc b/wrangler.jsonc index bfe2ad6..a4ae3ff 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -2,7 +2,6 @@ "$schema": "node_modules/wrangler/config-schema.json", "name": "pipecat-supermemory-demo", "main": "src/server.ts", - "account_id": "b3266c3b1d2134ff78a2078f6eb6f49a", "compatibility_date": "2025-12-17", "compatibility_flags": ["nodejs_compat"], "assets": { From 049689eec4677d40af7c4a9d5322a22ff0268cec Mon Sep 17 00:00:00 2001 From: Prasanna721 Date: Sat, 17 Jan 2026 19:27:32 -0800 Subject: [PATCH 2/6] feat: Migrate from OpenAI to Gemini Live for speech-to-speech - Switch backend from OpenAI (STT + LLM + TTS) to Gemini Live - Use LLMContextAggregatorPair instead of OpenAILLMContext - Configure Gemini VAD parameters for voice detection - Update frontend to handle TTS text streaming for bot transcription - Add TTS buffer refs to accumulate bot speech chunks into single message - Use onBotTtsText callback instead of onBotTranscript Co-Authored-By: Claude Opus 4.5 --- backend/server.py | 74 ++++++----------------------------------------- src/app.tsx | 34 ++++++++++++++++++++-- 2 files changed, 41 insertions(+), 67 deletions(-) diff --git a/backend/server.py b/backend/server.py index 3b41314..b82aa29 100644 --- a/backend/server.py +++ b/backend/server.py @@ -1,11 +1,3 @@ -""" -Pipecat + Supermemory Voice Bot Server - -This server uses Supermemory for memory storage. -Works with the official @pipecat-ai/client-js SDK. -Supports multiple users via query params. -""" - import os import sys import uuid @@ -14,12 +6,8 @@ from fastapi import FastAPI, Query, Request, WebSocket from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse -from google.genai import types from google.genai.types import EndSensitivity, StartSensitivity from loguru import logger -from pipecat.audio.interruptions.min_words_interruption_strategy import ( - MinWordsInterruptionStrategy, -) from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.frames.frames import LLMMessagesFrame from pipecat.pipeline.pipeline import Pipeline @@ -27,33 +15,23 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.llm_response_universal import ( LLMContextAggregatorPair, - LLMUserAggregatorParams, ) +from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.frameworks.rtvi import ( RTVIConfig, RTVIObserver, - RTVIObserverParams, RTVIProcessor, ) from pipecat.serializers.protobuf import ProtobufFrameSerializer from pipecat.services.google.gemini_live.llm import ( - GeminiLiveContext, GeminiLiveLLMService, GeminiVADParams, InputParams, ) -from pipecat.services.google.google import LLMContext from pipecat.transports.websocket.fastapi import ( FastAPIWebsocketParams, FastAPIWebsocketTransport, ) -from pipecat.turns.user_start.external_user_turn_start_strategy import ( - ExternalUserTurnStartStrategy, -) -from pipecat.turns.user_stop.external_user_turn_stop_strategy import ( - ExternalUserTurnStopStrategy, -) -from pipecat.turns.user_turn_strategies import UserTurnStrategies from supermemory_pipecat import SupermemoryPipecatService load_dotenv(override=True) @@ -63,7 +41,6 @@ app = FastAPI(title="Pipecat + Supermemory Voice Bot") -# Get allowed origins from env or default to all ALLOWED_ORIGINS = os.getenv("ALLOWED_ORIGINS", "*").split(",") app.add_middleware( @@ -81,8 +58,6 @@ async def run_bot(websocket_client, user_id: str, session_id: str): - """Run the voice bot pipeline with Supermemory for a specific user.""" - logger.info(f"Starting bot for user: {user_id}, session: {session_id}") transport = FastAPIWebsocketTransport( @@ -106,21 +81,18 @@ async def run_bot(websocket_client, user_id: str, session_id: str): api_key=gemini_api_key, voice_id="Puck", system_instruction=SYSTEM_PROMPT, - transcribe_user_audio=True, - transcribe_model_audio=True, inference_on_context_initialization=True, params=InputParams( vad=GeminiVADParams( disabled=False, - start_sensitivity=StartSensitivity.START_SENSITIVITY_HIGH, # Detect speech quickly - end_sensitivity=EndSensitivity.END_SENSITIVITY_HIGH, # Allow longer pauses - prefix_padding_ms=300, # Keep 300ms before speech - silence_duration_ms=500, # Allow longer pauses + start_sensitivity=StartSensitivity.START_SENSITIVITY_HIGH, + end_sensitivity=EndSensitivity.END_SENSITIVITY_HIGH, + prefix_padding_ms=300, + silence_duration_ms=500, ), ), ) - # Supermemory service with user-specific context memory = SupermemoryPipecatService( api_key=os.getenv("SUPERMEMORY_API_KEY"), user_id=user_id, @@ -133,24 +105,17 @@ async def run_bot(websocket_client, user_id: str, session_id: str): ) context = LLMContext([{"role": "system", "content": SYSTEM_PROMPT}]) + user_aggregator, assistant_aggregator = LLMContextAggregatorPair(context) - user_aggregator, assistant_aggregator = LLMContextAggregatorPair( - context, - user_params=LLMUserAggregatorParams( - user_turn_strategies=UserTurnStrategies( - start=[ExternalUserTurnStartStrategy()], - stop=[ExternalUserTurnStopStrategy()], - ), - ), - ) rtvi = RTVIProcessor(config=RTVIConfig(config=[])) + # Pipeline: RTVI before output transport for proper client communication pipeline = Pipeline( [ transport.input(), - memory, rtvi, user_aggregator, + memory, # Memory receives context frames with user messages llm, transport.output(), assistant_aggregator, @@ -164,20 +129,7 @@ async def run_bot(websocket_client, user_id: str, session_id: str): enable_metrics=True, enable_usage_metrics=True, ), - observers=[ - RTVIObserver( - rtvi, - params=RTVIObserverParams( - bot_tts_enabled=True, - bot_llm_enabled=True, - bot_output_enabled=True, - bot_speaking_enabled=True, - user_transcription_enabled=True, - user_speaking_enabled=True, - ), - ) - ], - enable_turn_tracking=True, + observers=[RTVIObserver(rtvi)], ) @rtvi.event_handler("on_client_ready") @@ -216,19 +168,12 @@ async def connect( userId: str = Query(None), sessionId: str = Query(None), ): - """ - Client calls this to get WebSocket connection info. - Pass userId and sessionId to maintain user context. - """ - # Generate IDs if not provided user_id = userId or f"user-{uuid.uuid4().hex[:12]}" session_id = sessionId or f"session-{uuid.uuid4().hex[:8]}" - # Get the host from request or use env ws_host = os.getenv("WS_HOST", request.headers.get("host", "localhost:8001")) ws_protocol = "wss" if os.getenv("USE_SSL", "false").lower() == "true" else "ws" - # Pipecat client only expects wsUrl in response - embed userId/sessionId in URL return JSONResponse( { "wsUrl": f"{ws_protocol}://{ws_host}/ws?userId={user_id}&sessionId={session_id}", @@ -244,7 +189,6 @@ async def websocket_endpoint( ): await websocket.accept() - # Generate IDs if not provided user_id = userId or f"user-{uuid.uuid4().hex[:12]}" session_id = sessionId or f"session-{uuid.uuid4().hex[:8]}" diff --git a/src/app.tsx b/src/app.tsx index 8fe4d3e..804b6d9 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -145,6 +145,8 @@ export default function VoiceApp() { const clientRef = useRef(null) const audioRef = useRef(null) const messagesEndRef = useRef(null) + const ttsBufferRef = useRef("") // Buffer for TTS text chunks + const lastBotMessageIndexRef = useRef(-1) // Track last bot message for updating // Inject memory-graph styles once useEffect(() => { @@ -346,13 +348,41 @@ export default function VoiceApp() { }, onUserTranscript: (data) => { if (data.final) { + // Reset TTS buffer for new response + ttsBufferRef.current = "" + lastBotMessageIndexRef.current = -1 + addTranscript("user", data.text) // Refresh memories after user speaks setTimeout(() => fetchMemories(), 3000) } }, - onBotTranscript: (data) => { - addTranscript("bot", data.text) + onBotTtsText: (data) => { + // Accumulate TTS chunks and update single bot message + const chunk = data.text || "" + ttsBufferRef.current += chunk + + setTranscripts(prev => { + const newTranscripts = [...prev] + + if (lastBotMessageIndexRef.current >= 0 && lastBotMessageIndexRef.current < newTranscripts.length) { + // Update existing bot message + newTranscripts[lastBotMessageIndexRef.current] = { + ...newTranscripts[lastBotMessageIndexRef.current], + text: ttsBufferRef.current + } + } else { + // Add new bot message + lastBotMessageIndexRef.current = newTranscripts.length + newTranscripts.push({ + role: "bot", + text: ttsBufferRef.current, + timestamp: new Date() + }) + } + + return newTranscripts + }) }, onError: (err) => { console.error("Client error:", err) From b7b376a8ad828fecf372683bbc8d7f20813812fb Mon Sep 17 00:00:00 2001 From: Prasanna721 Date: Sat, 17 Jan 2026 19:44:01 -0800 Subject: [PATCH 3/6] ci: Use bun instead of npm in CI workflow --- .github/workflows/sanity-check.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sanity-check.yml b/.github/workflows/sanity-check.yml index 7a19885..028e332 100644 --- a/.github/workflows/sanity-check.yml +++ b/.github/workflows/sanity-check.yml @@ -24,7 +24,7 @@ jobs: with: fetch-depth: 1 - - uses: actions/setup-node@v3 + - uses: oven-sh/setup-bun@v2 - - run: npm install - - run: npm run check + - run: bun install + - run: bun run check From 075bb46622b735a8429b84c8d8de6147b4109606 Mon Sep 17 00:00:00 2001 From: Prasanna721 Date: Sat, 17 Jan 2026 19:46:35 -0800 Subject: [PATCH 4/6] Remove outdated patch-package dependency - Delete @modelcontextprotocol/sdk@1.23.0 patch (transitive dep updated to 1.25.2) - Remove patch-package from devDependencies - Remove postinstall script since no patches remain Co-Authored-By: Claude Opus 4.5 --- bun.lock | 67 +------------------ package.json | 4 +- .../@modelcontextprotocol+sdk+1.23.0.patch | 13 ---- 3 files changed, 3 insertions(+), 81 deletions(-) delete mode 100644 patches/@modelcontextprotocol+sdk+1.23.0.patch diff --git a/bun.lock b/bun.lock index 0e94696..d9d5d05 100644 --- a/bun.lock +++ b/bun.lock @@ -50,7 +50,6 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.2", "drizzle-kit": "^0.31.8", - "patch-package": "^8.0.1", "prettier": "^3.7.4", "tailwindcss": "^4.1.18", "typescript": "^5.9.3", @@ -695,8 +694,6 @@ "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - "@yarnpkg/lockfile": ["@yarnpkg/lockfile@1.1.0", "", {}, "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ=="], - "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], @@ -717,7 +714,7 @@ "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], @@ -743,8 +740,6 @@ "bowser": ["bowser@2.13.1", "", {}, "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw=="], - "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], @@ -753,8 +748,6 @@ "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], - "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], @@ -765,8 +758,6 @@ "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], - "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], @@ -781,8 +772,6 @@ "chevrotain-allstar": ["chevrotain-allstar@0.3.1", "", { "dependencies": { "lodash-es": "^4.17.21" }, "peerDependencies": { "chevrotain": "^11.0.0" } }, "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw=="], - "ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], - "cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q=="], "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], @@ -923,8 +912,6 @@ "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], - "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], - "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], "delaunator": ["delaunator@5.0.1", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw=="], @@ -1025,12 +1012,8 @@ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], - "find-yarn-workspace-root": ["find-yarn-workspace-root@2.0.0", "", { "dependencies": { "micromatch": "^4.0.2" } }, "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ=="], - "form-data": ["form-data@4.0.5", "", { "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" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], "form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="], @@ -1043,8 +1026,6 @@ "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], - "fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="], - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -1073,10 +1054,6 @@ "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], - "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - - "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], - "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], @@ -1139,26 +1116,18 @@ "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], - "is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="], - "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], "is-plain-object": ["is-plain-object@2.0.4", "", { "dependencies": { "isobject": "^3.0.1" } }, "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og=="], "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], - "is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], - - "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "isobject": ["isobject@3.0.1", "", {}, "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="], @@ -1183,22 +1152,14 @@ "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], - "json-stable-stringify": ["json-stable-stringify@1.3.0", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "isarray": "^2.0.5", "jsonify": "^0.0.1", "object-keys": "^1.1.1" } }, "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg=="], - "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], - "jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="], - - "jsonify": ["jsonify@0.0.1", "", {}, "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg=="], - "katex": ["katex@0.16.27", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw=="], "khroma": ["khroma@2.1.0", "", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="], "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], - "klaw-sync": ["klaw-sync@6.0.0", "", { "dependencies": { "graceful-fs": "^4.1.11" } }, "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ=="], - "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], "kysely": ["kysely@0.28.9", "", {}, "sha512-3BeXMoiOhpOwu62CiVpO6lxfq4eS6KMYfQdMsN/2kUCRNuF2YiEr7u0HLHaQU+O4Xu8YXE3bHVkwaQ85i72EuA=="], @@ -1357,8 +1318,6 @@ "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], - "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], @@ -1399,8 +1358,6 @@ "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], - "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], - "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], @@ -1409,8 +1366,6 @@ "oniguruma-to-es": ["oniguruma-to-es@4.3.4", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA=="], - "open": ["open@7.4.2", "", { "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" } }, "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q=="], - "openai": ["openai@4.104.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" }, "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA=="], "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], @@ -1425,8 +1380,6 @@ "partysocket": ["partysocket@1.1.10", "", { "dependencies": { "event-target-polyfill": "^0.0.4" } }, "sha512-ACfn0P6lQuj8/AqB4L5ZDFcIEbpnIteNNObrlxqV1Ge80GTGhjuJ2sNKwNQlFzhGi4kI7fP/C1Eqh8TR78HjDQ=="], - "patch-package": ["patch-package@8.0.1", "", { "dependencies": { "@yarnpkg/lockfile": "^1.1.0", "chalk": "^4.1.2", "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", "fs-extra": "^10.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", "semver": "^7.5.3", "slash": "^2.0.0", "tmp": "^0.2.4", "yaml": "^2.2.2" }, "bin": { "patch-package": "index.js" } }, "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw=="], - "path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], @@ -1545,8 +1498,6 @@ "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], - "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], - "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], "shallow-clone": ["shallow-clone@3.0.1", "", { "dependencies": { "kind-of": "^6.0.2" } }, "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA=="], @@ -1571,8 +1522,6 @@ "simple-swizzle": ["simple-swizzle@0.2.4", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="], - "slash": ["slash@2.0.0", "", {}, "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="], - "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], @@ -1607,7 +1556,7 @@ "supermemory": ["supermemory@3.14.0", "", {}, "sha512-gy1C6B4wUHEIOjmvDqW6GRttEdr0TZFFZ2YVU5eTCXELPQ0zjxgwudmg2kLPI6dEIITUxw1Q6n1c+vm4ro0KSg=="], - "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], "svix": ["svix@1.76.1", "", { "dependencies": { "@stablelib/base64": "^1.0.0", "@types/node": "^22.7.5", "es6-promise": "^4.2.8", "fast-sha256": "^1.3.0", "url-parse": "^1.5.10", "uuid": "^10.0.0" } }, "sha512-CRuDWBTgYfDnBLRaZdKp9VuoPcNUq9An14c/k+4YJ15Qc5Grvf66vp0jvTltd4t7OIRj+8lM1DAgvSgvf7hdLw=="], @@ -1633,10 +1582,6 @@ "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - "tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="], - - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], - "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], @@ -1679,8 +1624,6 @@ "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], - "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], - "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], @@ -1805,8 +1748,6 @@ "@jridgewell/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@poppinss/dumper/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], - "@protobuf-ts/plugin/typescript": ["typescript@3.9.10", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q=="], "@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], @@ -1899,8 +1840,6 @@ "mermaid/uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], - "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "miniflare/workerd": ["workerd@1.20251217.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251217.0", "@cloudflare/workerd-darwin-arm64": "1.20251217.0", "@cloudflare/workerd-linux-64": "1.20251217.0", "@cloudflare/workerd-linux-arm64": "1.20251217.0", "@cloudflare/workerd-windows-64": "1.20251217.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-s3mHDSWwHTduyY8kpHOsl27ZJ4ziDBJlc18PfBvNMqNnhO7yBeemlxH7bo7yQyU1foJrIZ6IENHDDg0Z9N8zQA=="], "miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], @@ -1929,8 +1868,6 @@ "wrangler/miniflare": ["miniflare@4.20260103.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20260103.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "^3.25.76" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-iuSU0e+KMuFD7gxuPKoJXFi6cvDu/w/lQP4Wayq3v+YsmZ0dVMAJY9LMZ0TKMLicdAj2So9WcReAhJmJJ9Ppnw=="], - "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - "@cloudflare/vite-plugin/wrangler/workerd": ["workerd@1.20251217.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251217.0", "@cloudflare/workerd-darwin-arm64": "1.20251217.0", "@cloudflare/workerd-linux-64": "1.20251217.0", "@cloudflare/workerd-linux-arm64": "1.20251217.0", "@cloudflare/workerd-windows-64": "1.20251217.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-s3mHDSWwHTduyY8kpHOsl27ZJ4ziDBJlc18PfBvNMqNnhO7yBeemlxH7bo7yQyU1foJrIZ6IENHDDg0Z9N8zQA=="], "@cloudflare/vitest-pool-workers/wrangler/workerd": ["workerd@1.20251217.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251217.0", "@cloudflare/workerd-darwin-arm64": "1.20251217.0", "@cloudflare/workerd-linux-64": "1.20251217.0", "@cloudflare/workerd-linux-arm64": "1.20251217.0", "@cloudflare/workerd-windows-64": "1.20251217.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-s3mHDSWwHTduyY8kpHOsl27ZJ4ziDBJlc18PfBvNMqNnhO7yBeemlxH7bo7yQyU1foJrIZ6IENHDDg0Z9N8zQA=="], diff --git a/package.json b/package.json index 9236e94..c2d2816 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,7 @@ "test": "vitest", "types": "wrangler types env.d.ts --include-runtime false", "format": "prettier --write .", - "check": "prettier . --check && biome lint && tsc", - "postinstall": "patch-package" + "check": "prettier . --check && biome lint && tsc" }, "keywords": [ "cloudflare", @@ -37,7 +36,6 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.2", "drizzle-kit": "^0.31.8", - "patch-package": "^8.0.1", "prettier": "^3.7.4", "tailwindcss": "^4.1.18", "typescript": "^5.9.3", diff --git a/patches/@modelcontextprotocol+sdk+1.23.0.patch b/patches/@modelcontextprotocol+sdk+1.23.0.patch deleted file mode 100644 index 0ac54bd..0000000 --- a/patches/@modelcontextprotocol+sdk+1.23.0.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js b/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js -index 02762f0..7deca89 100644 ---- a/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js -+++ b/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js -@@ -1,7 +1,7 @@ - /** - * AJV-based JSON Schema validator provider - */ --import { Ajv } from 'ajv'; -+import Ajv from 'ajv'; // modifying this because vitest is struggling to pick up the import - import _addFormats from 'ajv-formats'; - function createDefaultAjvInstance() { - const ajv = new Ajv({ From bc40619cb00d5292366e0df4129c7e17bb750f95 Mon Sep 17 00:00:00 2001 From: Prasanna721 Date: Sat, 17 Jan 2026 20:03:17 -0800 Subject: [PATCH 5/6] Update CI to use biome and bun, remove patch-package --- README.md | 4 +- biome.json | 7 +- package.json | 2 +- src/app.tsx | 294 ++++++----- src/server.ts | 72 ++- src/voice.css | 1029 +++++++++++++++++++------------------ worker-configuration.d.ts | 42 +- 7 files changed, 761 insertions(+), 689 deletions(-) diff --git a/README.md b/README.md index 871d043..d14d373 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ - Screenshot 2026-01-15 at 5 49 27 PM - Voice assistant with persistent memory powered by [Supermemory](https://supermemory.ai) and [Pipecat](https://pipecat.ai). ## Quick Start @@ -18,7 +16,7 @@ SUPERMEMORY_API_KEY= ```bash bun run dev:backend -bun run dev +bun run dev ``` ## Pipecat Memory Integration diff --git a/biome.json b/biome.json index 68df3d5..fd3c9a1 100644 --- a/biome.json +++ b/biome.json @@ -53,7 +53,12 @@ "enabled": true, "rules": { "a11y": { - "useValidAnchor": "warn" + "useValidAnchor": "warn", + "noStaticElementInteractions": "warn", + "useMediaCaption": "warn" + }, + "suspicious": { + "noExplicitAny": "warn" }, "correctness": { "useYield": "warn", diff --git a/package.json b/package.json index c2d2816..d3e5558 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "test": "vitest", "types": "wrangler types env.d.ts --include-runtime false", "format": "prettier --write .", - "check": "prettier . --check && biome lint && tsc" + "check": "biome ci && tsc" }, "keywords": [ "cloudflare", diff --git a/src/app.tsx b/src/app.tsx index 804b6d9..e19fff9 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,11 +1,11 @@ -import { useCallback, useEffect, useRef, useState } from "react" import { PipecatClient, RTVIEvent } from "@pipecat-ai/client-js" import { WebSocketTransport } from "@pipecat-ai/websocket-transport" import { type DocumentWithMemories, - MemoryGraph, injectStyles, + MemoryGraph, } from "@supermemory/memory-graph" +import { useCallback, useEffect, useRef, useState } from "react" import "./voice.css" // Backend URL - configurable via env @@ -97,7 +97,6 @@ interface TranscriptEntry { timestamp: Date } - export default function VoiceApp() { // Generate userId on first load (persists in localStorage) const [userId] = useState(() => { @@ -117,7 +116,7 @@ export default function VoiceApp() { const [isConnected, setIsConnected] = useState(false) const [isConnecting, setIsConnecting] = useState(false) const [isBotReady, setIsBotReady] = useState(false) - const [isBotSpeaking, setIsBotSpeaking] = useState(false) + const [_isBotSpeaking, setIsBotSpeaking] = useState(false) const [error, setError] = useState(null) // Transcript @@ -129,7 +128,9 @@ export default function VoiceApp() { const [searchQuery, setSearchQuery] = useState("") // Graph documents - const [documentsData, setDocumentsData] = useState<{ documents: DocumentWithMemories[] } | null>(null) + const [documentsData, setDocumentsData] = useState<{ + documents: DocumentWithMemories[] + } | null>(null) const [isLoadingGraph, setIsLoadingGraph] = useState(false) // Profile data @@ -145,8 +146,8 @@ export default function VoiceApp() { const clientRef = useRef(null) const audioRef = useRef(null) const messagesEndRef = useRef(null) - const ttsBufferRef = useRef("") // Buffer for TTS text chunks - const lastBotMessageIndexRef = useRef(-1) // Track last bot message for updating + const ttsBufferRef = useRef("") // Buffer for TTS text chunks + const lastBotMessageIndexRef = useRef(-1) // Track last bot message for updating // Inject memory-graph styles once useEffect(() => { @@ -156,7 +157,7 @@ export default function VoiceApp() { // Auto-scroll messages useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }) - }, [transcripts]) + }, []) const addTranscript = useCallback((role: "user" | "bot", text: string) => { setTranscripts((prev) => [...prev, { role, text, timestamp: new Date() }]) @@ -186,7 +187,12 @@ export default function VoiceApp() { const data = await response.json() // Only update if we got valid data (no error field, and memories array exists) - if (!data.error && data.memories && Array.isArray(data.memories) && data.memories.length > 0) { + if ( + !data.error && + data.memories && + Array.isArray(data.memories) && + data.memories.length > 0 + ) { setMemories(data.memories) } } catch (err) { @@ -199,79 +205,82 @@ export default function VoiceApp() { ) // Fetch profile data from Supermemory API (preserves data on error) - const fetchProfile = useCallback( - async () => { - try { - const response = await fetch(`/api/profile?userId=${userId}`) - if (!response.ok) return // Don't clear data on error - const data = await response.json() - // Only update if we got valid profile data (no error field) - if (!data.error && data.profile) { - setProfileData(data) - } - } catch (err) { - console.error("Failed to fetch profile:", err) - // Don't clear data on error + const fetchProfile = useCallback(async () => { + try { + const response = await fetch(`/api/profile?userId=${userId}`) + if (!response.ok) return // Don't clear data on error + const data = await response.json() + // Only update if we got valid profile data (no error field) + if (!data.error && data.profile) { + setProfileData(data) } - }, - [userId], - ) + } catch (err) { + console.error("Failed to fetch profile:", err) + // Don't clear data on error + } + }, [userId]) // Fetch session documents (preserves data on error) - const fetchSessionDocs = useCallback( - async () => { - try { - const response = await fetch(`/api/documents?userId=${userId}&page=1&limit=10`) - if (!response.ok) return // Don't clear data on error - const data = await response.json() - // Only update if we got valid documents (no error field) - if (!data.error && data.documents && Array.isArray(data.documents) && data.documents.length > 0) { - const docs = data.documents.map((doc: any) => ({ - id: doc.id, - title: doc.title || "Untitled Session", - summary: doc.summary || "", - createdAt: doc.createdAt || new Date().toISOString(), - })) - setSessionDocs(docs) - } - } catch (err) { - console.error("Failed to fetch session docs:", err) - // Don't clear data on error + const fetchSessionDocs = useCallback(async () => { + try { + const response = await fetch( + `/api/documents?userId=${userId}&page=1&limit=10`, + ) + if (!response.ok) return // Don't clear data on error + const data = await response.json() + // Only update if we got valid documents (no error field) + if ( + !data.error && + data.documents && + Array.isArray(data.documents) && + data.documents.length > 0 + ) { + const docs = data.documents.map((doc: any) => ({ + id: doc.id, + title: doc.title || "Untitled Session", + summary: doc.summary || "", + createdAt: doc.createdAt || new Date().toISOString(), + })) + setSessionDocs(docs) } - }, - [userId], - ) + } catch (err) { + console.error("Failed to fetch session docs:", err) + // Don't clear data on error + } + }, [userId]) // Fetch documents for memory graph (preserves data on error) - const fetchDocuments = useCallback( - async () => { - // Only show loading spinner on first load when no data exists - const isFirstLoad = !documentsData - if (isFirstLoad) setIsLoadingGraph(true) + const fetchDocuments = useCallback(async () => { + // Only show loading spinner on first load when no data exists + const isFirstLoad = !documentsData + if (isFirstLoad) setIsLoadingGraph(true) - try { - const response = await fetch( - `/api/documents?userId=${userId}&page=1&limit=100`, - ) - if (!response.ok) { - // Don't clear data on error - if (isFirstLoad) setIsLoadingGraph(false) - return - } - const data = await response.json() - // Only update if we got valid data (no error field) - if (!data.error && data.documents && Array.isArray(data.documents) && data.documents.length > 0) { - setDocumentsData(data) - } - } catch (err) { - console.error("Failed to fetch documents:", err) + try { + const response = await fetch( + `/api/documents?userId=${userId}&page=1&limit=100`, + ) + if (!response.ok) { // Don't clear data on error + if (isFirstLoad) setIsLoadingGraph(false) + return } + const data = await response.json() + // Only update if we got valid data (no error field) + if ( + !data.error && + data.documents && + Array.isArray(data.documents) && + data.documents.length > 0 + ) { + setDocumentsData(data) + } + } catch (err) { + console.error("Failed to fetch documents:", err) + // Don't clear data on error + } - if (isFirstLoad) setIsLoadingGraph(false) - }, - [userId, documentsData], - ) + if (isFirstLoad) setIsLoadingGraph(false) + }, [userId, documentsData]) // Auto-refresh graph every 10 seconds when graph tab is active useEffect(() => { @@ -310,7 +319,14 @@ export default function VoiceApp() { clearInterval(profileInterval) clearInterval(sessionInterval) } - }, [hasEverStarted, activeTab, fetchProfile, fetchSessionDocs, fetchMemories, searchQuery]) + }, [ + hasEverStarted, + activeTab, + fetchProfile, + fetchSessionDocs, + fetchMemories, + searchQuery, + ]) // Start conversation const startConversation = useCallback(async () => { @@ -362,14 +378,17 @@ export default function VoiceApp() { const chunk = data.text || "" ttsBufferRef.current += chunk - setTranscripts(prev => { + setTranscripts((prev) => { const newTranscripts = [...prev] - if (lastBotMessageIndexRef.current >= 0 && lastBotMessageIndexRef.current < newTranscripts.length) { + if ( + lastBotMessageIndexRef.current >= 0 && + lastBotMessageIndexRef.current < newTranscripts.length + ) { // Update existing bot message newTranscripts[lastBotMessageIndexRef.current] = { ...newTranscripts[lastBotMessageIndexRef.current], - text: ttsBufferRef.current + text: ttsBufferRef.current, } } else { // Add new bot message @@ -377,7 +396,7 @@ export default function VoiceApp() { newTranscripts.push({ role: "bot", text: ttsBufferRef.current, - timestamp: new Date() + timestamp: new Date(), }) } @@ -405,7 +424,8 @@ export default function VoiceApp() { const connectUrl = `${BACKEND_URL}/connect?userId=${userId}&sessionId=${sessionId}` await client.startBotAndConnect({ endpoint: connectUrl }) } catch (err: unknown) { - const errorMessage = err instanceof Error ? err.message : "Failed to connect" + const errorMessage = + err instanceof Error ? err.message : "Failed to connect" console.error("Connection failed:", err) setError(errorMessage) setIsConnecting(false) @@ -442,7 +462,7 @@ export default function VoiceApp() { } }, []) - const formatTime = (date: Date) => { + const _formatTime = (date: Date) => { return date.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", @@ -468,14 +488,14 @@ export default function VoiceApp() { if (!hasEverStarted) { return (
-