https://www.boundaryml.com logo
When using hooks on nextjs with stream enabled, ab...
# help
q
Nor do I receive finalData. It does work all the time with
stream: false
. The other 50% of the time it works normally. Here is how I use it:
Copy code
tsx
export function Component() {

  const getMyFunction = useGetMyFunction()

  console.log(getMyFunction)

  useEffect(() => {
    getMyFunction.mutate("input")
  }, [])
}
It re-renders a few times and fills my schema with null values, then nothing. Not even an error. The interesting part is that the server function logs the full results parsed from the LLM. I have logged
useBamlAction
and it gets stuck at this line
const { value, done } = await reader.read()
Any idea?
a
in dev mode useEffects sometimes will run twice. Can you try doing the mutate inside a button click temporarily and see if you get the same issue?
q
Indeed, it seems to work with a button but not even this seems to do the trick:
Copy code
tsx
export function Component() {
  const [tick, setTick] = useState(0)
  const getMyFunction = useGetMyFunction()

  console.log(getMyFunction)

  useEffect(() => {
    if (tick === 0) {
      setTick((prev) => prev + 1)
    }
    if (tick === 1) {
      useGetMyFunction.mutate("input")
    }
  }, [tick])
}
a
the thing is that react will run the setTick potentially out of order
my guess is that mutate is being called twice
q
I'm going to take a look at what can be done in the hook. Its should be able to work that way
a
CC @greg (BAML) -- i think you just made a tiny nextjs app -- did you have this problem?
yeah worst case you can literally copy the hook / server actions in baml_client and make your own version
q
Thanks aronv. Tried to log mutate and the underlying
GetMyFunction
. The logs only appear a single time.
I seem to have constant success like this:
Copy code
tsx
  useEffect(() => {
    if (tick === 0) {
      setTimeout(() => {
        setTick((prev) => prev + 1)
      }, 1000)
    }
    if (tick === 1) {
      useGetMyFunction.mutate("input")
    }
  }, [tick, useGetMyFunction.mutate])
`
Also if I use
useGetMyFunction.mutate
as a dependency, I have an infinite re-render loop. The reason is because
useBamlAction
does this:
Copy code
tsx
function useBamlAction<FunctionName extends FunctionNames>(
  action: ServerAction,
  props: HookInput<FunctionName, { stream?: boolean }> = {},
) {
  ... 
  const mutate = useCallback(() => {
    ...
  }, [action, onFinalData, onError, props])
}
Therefore props object reference is recreated every time. I removed the dependency for tinkering right now
a
oh interesting. Yeah if there's any improvements we can make to the generated code we're glad to revisit this
g
Hi @Qwertas! I like the hypothesis that ~50% of your responses are getting dropped because of dev mode running the
useEffect
hook twice. I wonder if we can just verify that by adding a
console.log
right under the call to
mutate
in your original example? If we see that, then we can go down the path of hook-debugging (which would probably involve either turning StrictMode off or implementing a cleanup function, IIUC the docs I'm looking at).
In my own experiments with the auto-generated react hooks, I didn't see such a 50% drop issue. But my case was much simpler - running
mutate
in response to form submission. I never stress-tested it with multiple calls in quick succession.
q
Hello @greg (BAML) thanks for helping. When using in a button onClick, I did not had any issues, with streaming off too. I don't have React strict mode enabled except if it's on by default. Also I did log the mutate function and ran once (one log). I'm going to try that again as soon as I'm back home. To be sure the useEffect ran once, I did this:
Copy code
export function Component() {
  const [tick, setTick] = useState(0)
  const getMyFunction = useGetMyFunction()

  console.log(getMyFunction)

  useEffect(() => {
    if (tick === 0) {
      setTick((prev) => prev + 1)
    }
    if (tick === 1) {
      useGetMyFunction.mutate("input")
    }
  }, [tick])
}
Which should only have the condition
if (tick === 1) {
met once right? Same issue and only one log. Also my useEffect run as soon as the app starts maybe could be an issue. Could you try with a useEffect at the start of you app and see if you get the same issue?
g
Yah, I will try to reproduce the issue, good idea.
q
By the way I have described my whole experiment here -> https://github.com/BoundaryML/baml/issues/2413
g
Ah fantastic!