1- import { useCallback , useEffect , useRef , useState } from 'react'
1+ import { useCallback , useEffect , useRef } from 'react'
22import { isEqual } from 'lodash'
33import { createLogger } from '@/lib/logs/console-logger'
44import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
@@ -160,49 +160,28 @@ function storeApiKeyValue(
160160
161161interface UseSubBlockValueOptions {
162162 debounceMs ?: number
163- streamingThresholdMs ?: number
164- onStreamingStart ?: ( ) => void
163+ isStreaming ?: boolean // Explicit streaming state
165164 onStreamingEnd ?: ( ) => void
166165}
167166
168167/**
169168 * Custom hook to get and set values for a sub-block in a workflow.
170169 * Handles complex object values properly by using deep equality comparison.
171- * Includes automatic debouncing and streaming detection for large text operations .
170+ * Includes automatic debouncing and explicit streaming mode for AI generation .
172171 *
173172 * @param blockId The ID of the block containing the sub-block
174173 * @param subBlockId The ID of the sub-block
175174 * @param triggerWorkflowUpdate Whether to trigger a workflow update when the value changes
176175 * @param options Configuration for debouncing and streaming behavior
177- * @returns A tuple containing the current value, setter function, and optionally streaming state
176+ * @returns A tuple containing the current value and setter function
178177 */
179- export function useSubBlockValue < T = any > (
180- blockId : string ,
181- subBlockId : string ,
182- triggerWorkflowUpdate ?: boolean
183- ) : readonly [ T | null , ( value : T ) => void ]
184-
185- export function useSubBlockValue < T = any > (
186- blockId : string ,
187- subBlockId : string ,
188- triggerWorkflowUpdate : boolean ,
189- options : UseSubBlockValueOptions
190- ) : readonly [ T | null , ( value : T ) => void , { isStreaming : boolean } ]
191-
192178export function useSubBlockValue < T = any > (
193179 blockId : string ,
194180 subBlockId : string ,
195181 triggerWorkflowUpdate = false ,
196182 options ?: UseSubBlockValueOptions
197- ) :
198- | readonly [ T | null , ( value : T ) => void ]
199- | readonly [ T | null , ( value : T ) => void , { isStreaming : boolean } ] {
200- const {
201- debounceMs = 100 ,
202- streamingThresholdMs = 50 ,
203- onStreamingStart,
204- onStreamingEnd,
205- } = options || { }
183+ ) : readonly [ T | null , ( value : T ) => void ] {
184+ const { debounceMs = 150 , isStreaming = false , onStreamingEnd } = options || { }
206185
207186 const { collaborativeSetSubblockValue } = useCollaborativeWorkflow ( )
208187
@@ -223,13 +202,11 @@ export function useSubBlockValue<T = any>(
223202 // Previous model reference for detecting model changes
224203 const prevModelRef = useRef < string | null > ( null )
225204
226- // Debouncing and streaming detection refs
205+ // Debouncing refs
227206 const debounceTimerRef = useRef < NodeJS . Timeout | null > ( null )
228- const lastUpdateTimeRef = useRef < number > ( 0 )
229- const updateCountRef = useRef < number > ( 0 )
230- const streamingTimeoutRef = useRef < NodeJS . Timeout | null > ( null )
231207 const lastEmittedValueRef = useRef < T | null > ( null )
232- const [ isStreaming , setIsStreaming ] = useState ( false )
208+ const streamingValueRef = useRef < T | null > ( null )
209+ const wasStreamingRef = useRef < boolean > ( false )
233210
234211 // Get value from subblock store - always call this hook unconditionally
235212 const storeValue = useSubBlockStore (
@@ -255,15 +232,12 @@ export function useSubBlockValue<T = any>(
255232 // Compute the modelValue based on block type
256233 const modelValue = isProviderBasedBlock ? ( modelSubBlockValue as string ) : null
257234
258- // Cleanup timers on unmount
235+ // Cleanup timer on unmount
259236 useEffect ( ( ) => {
260237 return ( ) => {
261238 if ( debounceTimerRef . current ) {
262239 clearTimeout ( debounceTimerRef . current )
263240 }
264- if ( streamingTimeoutRef . current ) {
265- clearTimeout ( streamingTimeoutRef . current )
266- }
267241 }
268242 } , [ ] )
269243
@@ -276,59 +250,17 @@ export function useSubBlockValue<T = any>(
276250 [ blockId , subBlockId , collaborativeSetSubblockValue ]
277251 )
278252
279- // Detect and handle streaming
280- const detectStreaming = useCallback ( ( ) => {
281- const now = Date . now ( )
282- const timeSinceLastUpdate = now - lastUpdateTimeRef . current
283-
284- // If updates are coming in rapidly, we're likely streaming
285- if ( timeSinceLastUpdate < streamingThresholdMs ) {
286- updateCountRef . current ++
287-
288- // Start streaming mode after 3 rapid updates
289- if ( updateCountRef . current >= 3 && ! isStreaming ) {
290- logger . debug ( 'Streaming detected' , {
291- blockId,
292- subBlockId,
293- updateCount : updateCountRef . current ,
294- } )
295- setIsStreaming ( true )
296- onStreamingStart ?.( )
297- }
298- } else {
299- // Reset counter if updates slow down
300- updateCountRef . current = 1
301- }
302-
303- lastUpdateTimeRef . current = now
304-
305- // Set up timeout to end streaming
306- if ( streamingTimeoutRef . current ) {
307- clearTimeout ( streamingTimeoutRef . current )
308- }
309-
310- if ( isStreaming ) {
311- streamingTimeoutRef . current = setTimeout ( ( ) => {
312- logger . debug ( 'Ending streaming mode' , { blockId, subBlockId } )
313- setIsStreaming ( false )
314- updateCountRef . current = 0
315- onStreamingEnd ?.( )
316-
317- // Emit the final value when streaming ends
318- if ( valueRef . current !== null && valueRef . current !== lastEmittedValueRef . current ) {
319- emitValue ( valueRef . current )
320- }
321- } , 300 ) // End streaming 300ms after last update
253+ // Handle streaming mode changes
254+ useEffect ( ( ) => {
255+ // If we just exited streaming mode, emit the final value
256+ if ( wasStreamingRef . current && ! isStreaming && streamingValueRef . current !== null ) {
257+ logger . debug ( 'Streaming ended, persisting final value' , { blockId, subBlockId } )
258+ emitValue ( streamingValueRef . current )
259+ streamingValueRef . current = null
260+ onStreamingEnd ?.( )
322261 }
323- } , [
324- blockId ,
325- subBlockId ,
326- isStreaming ,
327- streamingThresholdMs ,
328- onStreamingStart ,
329- onStreamingEnd ,
330- emitValue ,
331- ] )
262+ wasStreamingRef . current = isStreaming
263+ } , [ isStreaming , blockId , subBlockId , emitValue , onStreamingEnd ] )
332264
333265 // Hook to set a value in the subblock store
334266 const setValue = useCallback (
@@ -366,17 +298,17 @@ export function useSubBlockValue<T = any>(
366298 storeApiKeyValue ( blockId , blockType , modelValue , newValue , storeValue )
367299 }
368300
369- // Detect if we're in a streaming scenario
370- detectStreaming ( )
371-
372301 // Clear any existing debounce timer
373302 if ( debounceTimerRef . current ) {
374303 clearTimeout ( debounceTimerRef . current )
304+ debounceTimerRef . current = null
375305 }
376306
377- // If streaming, don't emit immediately - wait for streaming to end
378- if ( ! isStreaming ) {
379- // Detect large changes for automatic bulk operation mode
307+ // If streaming, just store the value without emitting
308+ if ( isStreaming ) {
309+ streamingValueRef . current = valueCopy
310+ } else {
311+ // Detect large changes for extended debounce
380312 const isLargeChange = detectLargeChange ( lastEmittedValueRef . current , valueCopy )
381313 const effectiveDebounceMs = isLargeChange ? debounceMs * 2 : debounceMs
382314
@@ -403,7 +335,6 @@ export function useSubBlockValue<T = any>(
403335 modelValue ,
404336 isStreaming ,
405337 debounceMs ,
406- detectStreaming ,
407338 emitValue ,
408339 ]
409340 )
@@ -478,13 +409,6 @@ export function useSubBlockValue<T = any>(
478409 } , [ storeValue , initialValue ] )
479410
480411 // Return appropriate tuple based on whether options were provided
481- if ( options ) {
482- return [
483- storeValue !== undefined ? storeValue : initialValue ,
484- setValue ,
485- { isStreaming } ,
486- ] as const
487- }
488412 return [ storeValue !== undefined ? storeValue : initialValue , setValue ] as const
489413}
490414
0 commit comments