-1

I am trying to use transformers.js to build a simple chatbot to answer questions related to my resume. The error is as below:

TypeError: text.split is not a function
    at closure._encode_text (tokenizers.js:2840:1)
    at closure._encode_plus (tokenizers.js:2894:1)
    at closure._call (tokenizers.js:2727:1)
    at closure [as tokenizer] (core.js:62:1)
    at closure._call (pipelines.js:492:1)
    at closure (core.js:62:1)

The error occurs as soon as the model gets loaded and we set state using:

setAnswerer(qa);

This line is executed, however the error doesn't explicitly tell the line the error is coming from.

import React, { useState, useEffect } from "react";
import { pipeline, env } from "@xenova/transformers";

// Sample resume content - replace with your actual resume
const RESUME_CONTEXT = `
John doe - Senior Software Engineer
Experience:
- 5+ years React development
- Built 10+ production applications
- Led team at XYZ Corp (2020-2023)
Education: BS Computer Science - MIT (2019)
Skills: JavaScript, Python, WebGL, LLM integration
`;

export default function ChatInterface() {
  console.log("ChatInterface component loaded");
  const [input, setInput] = useState("");
  const [answer, setAnswer] = useState("");
  const [loading, setLoading] = useState(false);
  const [answerer, setAnswerer] = useState(null);

  useEffect(() => {
    // Initialize the pipeline
    const init = async () => {
      env.allowLocalModels = false; // Disable local models for security
      const qa = await pipeline(
        "question-answering",
        "Xenova/distilbert-base-uncased-distilled-squad",
        {
          device: "webgpu",
          dtype: "q4f16",
          progress_callback: (progress) => {
            console.log(`Loading model: ${Math.round(progress.progress)}%`);
          },
        }
      );
      console.log("Model loaded successfully");
      setAnswerer(qa);
    };
    init();
  }, []);

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!input.trim() || !answerer) return;

    setLoading(true);
    try {
      const output = await answerer(input, RESUME_CONTEXT);
      setAnswer(output.answer);
    } catch (error) {
      console.error("Error:", error);
      setAnswer("Sorry, I had trouble answering that.");
    }
    setLoading(false);
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Ask a question..."
          disabled={loading}
        />
        <button type="submit" disabled={loading}>
          {loading ? "Thinking..." : "Ask"}
        </button>
      </form>
      {answer && (
        <div>
          <strong>Answer:</strong> {answer}
        </div>
      )}
    </div>
  );
}

Looking at the error, I went through the source code in node_modules, put some debuggers there, and qa is getting called with null or undefined arguments even though I have not called it anywhere. What I found out was if I comment out the line:

setAnswerer(qa);

then the error stops occurring. I do not understand why this is happening. I have not made any calls to answerer/qa outside the handleSubmit but still it seems that somehow qa is getting called. I have tried putting a debugger breakpoint inside handleSubmit, but it doesn't stop inside the function.

Basically setting the state is somehow calling the qa function even though that should not happen.

1
  • I'm not sure why this was closed as it is reproducible and my answer explains how this occurs... Commented Jul 4 at 16:00

1 Answer 1

1

useState's set functions (like setAnswerer) have special behavior for function arguments:

If you pass a function as nextState, it will be treated as an updater function.

Based on your usage, qa is a function, so setAnswerer(qa) results in React calling qa as an updater function.

As you're not using qa as a value for rendering, useRef would be more appropriate than useState for it.

Re-writing your code with useRef, you could do:

import React, { useState, useEffect } from "react";
import { pipeline, env } from "@xenova/transformers";

// Sample resume content - replace with your actual resume
const RESUME_CONTEXT = `
John doe - Senior Software Engineer
Experience:
- 5+ years React development
- Built 10+ production applications
- Led team at XYZ Corp (2020-2023)
Education: BS Computer Science - MIT (2019)
Skills: JavaScript, Python, WebGL, LLM integration
`;

export default function ChatInterface() {
  console.log("ChatInterface component loaded");
  const [input, setInput] = useState("");
  const [answer, setAnswer] = useState("");
  const [loading, setLoading] = useState(false);
  const answererRef = useRef(null);

  useEffect(() => {
    // Initialize the pipeline
    const init = async () => {
      env.allowLocalModels = false; // Disable local models for security
      const qa = await pipeline(
        "question-answering",
        "Xenova/distilbert-base-uncased-distilled-squad",
        {
          device: "webgpu",
          dtype: "q4f16",
          progress_callback: (progress) => {
            console.log(`Loading model: ${Math.round(progress.progress)}%`);
          },
        }
      );
      console.log("Model loaded successfully");
      answererRef.current = qa;
    };
    init();
  }, []);

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!input.trim() || !answererRef.current) return;

    setLoading(true);
    try {
      const output = await answererRef.current(input, RESUME_CONTEXT);
      setAnswer(output.answer);
    } catch (error) {
      console.error("Error:", error);
      setAnswer("Sorry, I had trouble answering that.");
    }
    setLoading(false);
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Ask a question..."
          disabled={loading}
        />
        <button type="submit" disabled={loading}>
          {loading ? "Thinking..." : "Ask"}
        </button>
      </form>
      {answer && (
        <div>
          <strong>Answer:</strong> {answer}
        </div>
      )}
    </div>
  );
}

The specific changes are the usage of answererRef in place of answerer and setAnswerer.

Sign up to request clarification or add additional context in comments.

2 Comments

Or, i guess, you could use setAnswerer(currentState => qa) to pass updater function returning qa
Yea that could work too. More succinctly that would be setAnswerer(() => qa)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.