> ## Documentation Index
> Fetch the complete documentation index at: https://docs.voiceblox.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Component Nodes

> LLM, TTS, STT, Avatar, and Persona nodes — providers, parameters, and configuration.

export const GREEN = "hsl(142, 71%, 45%)";
export const MODELS = ["gpt-5.2", "gpt-4.1", "gpt-4.1-mini", "gpt-4o", "gpt-4o-mini", "o4-mini", "o3"];

export const LlmNodePreview = () => {
  const [model, setModel] = useState("gpt-4.1-mini");
  const [temperature, setTemperature] = useState(0.7);
  const [maxTokens, setMaxTokens] = useState("");
  const [showAdvanced, setShowAdvanced] = useState(false);
  return <div style={{
    display: "flex",
    justifyContent: "center",
    padding: "28px 16px",
    background: "rgba(128,128,128,0.06)",
    borderRadius: "12px",
    margin: "16px 0"
  }}>
      <div style={{
    position: "relative",
    width: "240px",
    borderRadius: "12px",
    border: "1px solid hsla(142,71%,45%,0.3)",
    background: "hsla(142,71%,45%,0.1)",
    boxShadow: "0 4px 24px -4px rgba(0,0,0,0.08),0 1px 2px rgba(0,0,0,0.04)",
    fontFamily: "-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif"
  }}>
        <div style={{
    position: "absolute",
    right: "-6px",
    top: "18px",
    width: "12px",
    height: "12px",
    borderRadius: "50%",
    background: "hsl(142,71%,45%)",
    border: "2px solid white",
    zIndex: 10,
    boxShadow: "0 1px 3px rgba(0,0,0,0.2)"
  }} />
        <div style={{
    display: "flex",
    alignItems: "center",
    gap: "8px",
    padding: "8px 12px",
    borderBottom: "1px solid rgba(128,128,128,0.12)"
  }}>
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="hsl(142,71%,45%)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z" />
            <path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z" />
            <path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4" />
            <path d="M17.599 6.5a3 3 0 0 0 .399-1.375" />
            <path d="M6.003 5.125A3 3 0 0 0 6.401 6.5" />
            <path d="M3.477 10.896a4 4 0 0 1 .585-.396" />
            <path d="M19.938 10.5a4 4 0 0 1 .585.396" />
            <path d="M6 18a4 4 0 0 1-1.967-.516" />
            <path d="M19.967 17.484A4 4 0 0 1 18 18" />
          </svg>
          <span style={{
    fontSize: "14px",
    fontWeight: 600,
    flex: 1
  }}>OpenAI</span>
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    opacity: 0.3
  }}>
            <path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.21 1.21 0 0 0 1.72 0L21.64 5.36a1.21 1.21 0 0 0 0-1.72Z" />
            <path d="m14 7 3 3" /><path d="M5 6v4" /><path d="M19 14v4" /><path d="M10 2v2" /><path d="M7 8H3" /><path d="M21 16h-4" /><path d="M11 3H9" />
          </svg>
        </div>
        <div style={{
    padding: "10px 12px",
    display: "flex",
    flexDirection: "column",
    gap: "8px"
  }}>
          <p style={{
    fontSize: "10px",
    color: "#888",
    margin: 0,
    lineHeight: 1.4
  }}>Uses OpenAI GPT via LiveKit inference</p>
          <div>
            <label style={{
    fontSize: "10px",
    color: "#888",
    display: "block",
    marginBottom: "3px"
  }}>Model</label>
            <div style={{
    position: "relative"
  }}>
              <select value={model} onChange={e => setModel(e.target.value)} style={{
    width: "100%",
    padding: "4px 24px 4px 8px",
    border: "1px solid rgba(128,128,128,0.2)",
    borderRadius: "6px",
    fontSize: "12px",
    background: "rgba(128,128,128,0.06)",
    cursor: "pointer",
    outline: "none",
    appearance: "none",
    WebkitAppearance: "none"
  }}>
                {MODELS.map(m => <option key={m} value={m}>{m}</option>)}
              </select>
              <svg style={{
    position: "absolute",
    right: "7px",
    top: "50%",
    transform: "translateY(-50%)",
    pointerEvents: "none"
  }} width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#aaa" strokeWidth="2"><polyline points="6 9 12 15 18 9" /></svg>
            </div>
          </div>
          <div>
            <label style={{
    fontSize: "10px",
    color: "#888",
    display: "block",
    marginBottom: "3px"
  }}>Temperature: {temperature.toFixed(1)}</label>
            <input type="range" min={0} max={2} step={0.1} value={temperature} onChange={e => setTemperature(Number(e.target.value))} style={{
    width: "100%",
    accentColor: "hsl(142,71%,45%)",
    cursor: "pointer"
  }} />
            <div style={{
    display: "flex",
    justifyContent: "space-between",
    marginTop: "2px"
  }}>
              <span style={{
    fontSize: "9px",
    color: "#aaa"
  }}>Precise</span>
              <span style={{
    fontSize: "9px",
    color: "#aaa"
  }}>Creative</span>
            </div>
          </div>
          <button onClick={() => setShowAdvanced(v => !v)} style={{
    display: "flex",
    alignItems: "center",
    gap: "4px",
    width: "100%",
    padding: "4px 0",
    fontSize: "10px",
    color: "#aaa",
    background: "none",
    border: "none",
    cursor: "pointer",
    textAlign: "left"
  }}>
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    transform: showAdvanced ? "rotate(180deg)" : "rotate(0deg)",
    transition: "transform 0.15s"
  }}>
              <polyline points="6 9 12 15 18 9" />
            </svg>
            Advanced
          </button>
          {showAdvanced && <div>
              <label style={{
    fontSize: "10px",
    color: "#888",
    display: "block",
    marginBottom: "3px"
  }}>Max Tokens</label>
              <input type="number" placeholder="e.g. 1024" value={maxTokens} onChange={e => setMaxTokens(e.target.value)} style={{
    width: "100%",
    padding: "4px 8px",
    border: "1px solid rgba(128,128,128,0.2)",
    borderRadius: "6px",
    fontSize: "12px",
    background: "rgba(128,128,128,0.06)",
    outline: "none",
    boxSizing: "border-box"
  }} />
            </div>}
        </div>
      </div>
    </div>;
};

export const TtsNodePreview = () => {
  const AMBER = "hsl(38,92%,50%)";
  const [voiceId, setVoiceId] = useState("");
  const [model, setModel] = useState("eleven_flash_v2_5");
  const [stability, setStability] = useState(0.5);
  const [simBoost, setSimBoost] = useState(0.75);
  const [showAdv, setShowAdv] = useState(false);
  const TTS_MODELS = ["eleven_flash_v2_5", "eleven_turbo_v2_5", "eleven_multilingual_v2"];
  const sel = {
    width: "100%",
    padding: "4px 24px 4px 8px",
    border: "1px solid rgba(128,128,128,0.2)",
    borderRadius: "6px",
    fontSize: "12px",
    background: "rgba(128,128,128,0.06)",
    cursor: "pointer",
    outline: "none",
    appearance: "none",
    WebkitAppearance: "none"
  };
  return <div style={{
    display: "flex",
    justifyContent: "center",
    padding: "28px 16px",
    background: "rgba(128,128,128,0.06)",
    borderRadius: "12px",
    margin: "16px 0"
  }}>
      <div style={{
    position: "relative",
    width: "240px",
    borderRadius: "12px",
    border: "1px solid hsla(38,92%,50%,0.3)",
    background: "hsla(38,92%,50%,0.1)",
    boxShadow: "0 4px 24px -4px rgba(0,0,0,0.08),0 1px 2px rgba(0,0,0,0.04)",
    fontFamily: "-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif"
  }}>
        <div style={{
    position: "absolute",
    right: "-6px",
    top: "18px",
    width: "12px",
    height: "12px",
    borderRadius: "50%",
    background: AMBER,
    border: "2px solid white",
    zIndex: 10,
    boxShadow: "0 1px 3px rgba(0,0,0,0.2)"
  }} />
        <div style={{
    display: "flex",
    alignItems: "center",
    gap: "8px",
    padding: "8px 12px",
    borderBottom: "1px solid rgba(128,128,128,0.12)"
  }}>
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={AMBER} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M2 10v3" /><path d="M6 6v11" /><path d="M10 3v18" /><path d="M14 8v7" /><path d="M18 5v13" /><path d="M22 10v3" />
          </svg>
          <span style={{
    fontSize: "14px",
    fontWeight: 600,
    flex: 1
  }}>ElevenLabs</span>
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    opacity: 0.3
  }}>
            <path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.21 1.21 0 0 0 1.72 0L21.64 5.36a1.21 1.21 0 0 0 0-1.72Z" />
            <path d="m14 7 3 3" /><path d="M5 6v4" /><path d="M19 14v4" /><path d="M10 2v2" /><path d="M7 8H3" /><path d="M21 16h-4" /><path d="M11 3H9" />
          </svg>
        </div>
        <div style={{
    padding: "10px 12px",
    display: "flex",
    flexDirection: "column",
    gap: "8px"
  }}>
          <p style={{
    fontSize: "10px",
    color: "#888",
    margin: 0,
    lineHeight: 1.4
  }}>Converts text into natural-sounding speech via LiveKit inference</p>
          <div>
            <label style={{
    fontSize: "10px",
    color: "#888",
    display: "block",
    marginBottom: "3px"
  }}>Voice ID</label>
            <input type="text" placeholder="e.g. Rachel, Adam..." value={voiceId} onChange={e => setVoiceId(e.target.value)} style={{
    width: "100%",
    padding: "4px 8px",
    border: "1px solid rgba(128,128,128,0.2)",
    borderRadius: "6px",
    fontSize: "12px",
    background: "rgba(128,128,128,0.06)",
    outline: "none",
    boxSizing: "border-box"
  }} />
          </div>
          <div>
            <label style={{
    fontSize: "10px",
    color: "#888",
    display: "block",
    marginBottom: "3px"
  }}>Model</label>
            <div style={{
    position: "relative"
  }}>
              <select value={model} onChange={e => setModel(e.target.value)} style={sel}>
                {TTS_MODELS.map(m => <option key={m} value={m}>{m}</option>)}
              </select>
              <svg style={{
    position: "absolute",
    right: "7px",
    top: "50%",
    transform: "translateY(-50%)",
    pointerEvents: "none"
  }} width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#aaa" strokeWidth="2"><polyline points="6 9 12 15 18 9" /></svg>
            </div>
          </div>
          <button onClick={() => setShowAdv(v => !v)} style={{
    display: "flex",
    alignItems: "center",
    gap: "4px",
    width: "100%",
    padding: "4px 0",
    fontSize: "10px",
    color: "#aaa",
    background: "none",
    border: "none",
    cursor: "pointer",
    textAlign: "left"
  }}>
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    transform: showAdv ? "rotate(180deg)" : "rotate(0deg)",
    transition: "transform 0.15s"
  }}><polyline points="6 9 12 15 18 9" /></svg>
            Advanced
          </button>
          {showAdv && <>
            <div>
              <label style={{
    fontSize: "10px",
    color: "#888",
    display: "block",
    marginBottom: "3px"
  }}>Stability: {stability.toFixed(2)}</label>
              <input type="range" min={0} max={1} step={0.05} value={stability} onChange={e => setStability(Number(e.target.value))} style={{
    width: "100%",
    accentColor: AMBER,
    cursor: "pointer"
  }} />
            </div>
            <div>
              <label style={{
    fontSize: "10px",
    color: "#888",
    display: "block",
    marginBottom: "3px"
  }}>Similarity Boost: {simBoost.toFixed(2)}</label>
              <input type="range" min={0} max={1} step={0.05} value={simBoost} onChange={e => setSimBoost(Number(e.target.value))} style={{
    width: "100%",
    accentColor: AMBER,
    cursor: "pointer"
  }} />
            </div>
          </>}
        </div>
      </div>
    </div>;
};

export const SttNodePreview = () => {
  const SKY = "hsl(199,89%,48%)";
  const [model, setModel] = useState("nova-3");
  const [keyterms, setKeyterms] = useState("");
  const [diarization, setDiarization] = useState(false);
  const [showAdv, setShowAdv] = useState(false);
  const STT_MODELS = ["nova-3", "nova-2", "whisper-large"];
  const sel = {
    width: "100%",
    padding: "4px 24px 4px 8px",
    border: "1px solid rgba(128,128,128,0.2)",
    borderRadius: "6px",
    fontSize: "12px",
    background: "rgba(128,128,128,0.06)",
    cursor: "pointer",
    outline: "none",
    appearance: "none",
    WebkitAppearance: "none"
  };
  return <div style={{
    display: "flex",
    justifyContent: "center",
    padding: "28px 16px",
    background: "rgba(128,128,128,0.06)",
    borderRadius: "12px",
    margin: "16px 0"
  }}>
      <div style={{
    position: "relative",
    width: "240px",
    borderRadius: "12px",
    border: "1px solid hsla(199,89%,48%,0.3)",
    background: "hsla(199,89%,48%,0.1)",
    boxShadow: "0 4px 24px -4px rgba(0,0,0,0.08),0 1px 2px rgba(0,0,0,0.04)",
    fontFamily: "-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif"
  }}>
        <div style={{
    position: "absolute",
    right: "-6px",
    top: "18px",
    width: "12px",
    height: "12px",
    borderRadius: "50%",
    background: SKY,
    border: "2px solid white",
    zIndex: 10,
    boxShadow: "0 1px 3px rgba(0,0,0,0.2)"
  }} />
        <div style={{
    display: "flex",
    alignItems: "center",
    gap: "8px",
    padding: "8px 12px",
    borderBottom: "1px solid rgba(128,128,128,0.12)"
  }}>
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={SKY} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" />
            <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
            <line x1="12" x2="12" y1="19" y2="22" />
          </svg>
          <span style={{
    fontSize: "14px",
    fontWeight: 600,
    flex: 1
  }}>Deepgram</span>
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    opacity: 0.3
  }}>
            <path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.21 1.21 0 0 0 1.72 0L21.64 5.36a1.21 1.21 0 0 0 0-1.72Z" />
            <path d="m14 7 3 3" /><path d="M5 6v4" /><path d="M19 14v4" /><path d="M10 2v2" /><path d="M7 8H3" /><path d="M21 16h-4" /><path d="M11 3H9" />
          </svg>
        </div>
        <div style={{
    padding: "10px 12px",
    display: "flex",
    flexDirection: "column",
    gap: "8px"
  }}>
          <p style={{
    fontSize: "10px",
    color: "#888",
    margin: 0,
    lineHeight: 1.4
  }}>Converts voice to text via LiveKit inference</p>
          <div>
            <label style={{
    fontSize: "10px",
    color: "#888",
    display: "block",
    marginBottom: "3px"
  }}>Model</label>
            <div style={{
    position: "relative"
  }}>
              <select value={model} onChange={e => setModel(e.target.value)} style={sel}>
                {STT_MODELS.map(m => <option key={m} value={m}>{m}</option>)}
              </select>
              <svg style={{
    position: "absolute",
    right: "7px",
    top: "50%",
    transform: "translateY(-50%)",
    pointerEvents: "none"
  }} width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#aaa" strokeWidth="2"><polyline points="6 9 12 15 18 9" /></svg>
            </div>
          </div>
          <button onClick={() => setShowAdv(v => !v)} style={{
    display: "flex",
    alignItems: "center",
    gap: "4px",
    width: "100%",
    padding: "4px 0",
    fontSize: "10px",
    color: "#aaa",
    background: "none",
    border: "none",
    cursor: "pointer",
    textAlign: "left"
  }}>
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    transform: showAdv ? "rotate(180deg)" : "rotate(0deg)",
    transition: "transform 0.15s"
  }}><polyline points="6 9 12 15 18 9" /></svg>
            Advanced
          </button>
          {showAdv && <>
            <div>
              <label style={{
    fontSize: "10px",
    color: "#888",
    display: "block",
    marginBottom: "3px"
  }}>Key Terms</label>
              <input type="text" placeholder="term1, term2, ..." value={keyterms} onChange={e => setKeyterms(e.target.value)} style={{
    width: "100%",
    padding: "4px 8px",
    border: "1px solid rgba(128,128,128,0.2)",
    borderRadius: "6px",
    fontSize: "12px",
    background: "rgba(128,128,128,0.06)",
    outline: "none",
    boxSizing: "border-box"
  }} />
            </div>
            <label style={{
    display: "flex",
    alignItems: "center",
    gap: "8px",
    padding: "6px 8px",
    borderRadius: "6px",
    border: `1px solid ${diarization ? "rgba(130,111,252,0.4)" : "rgba(128,128,128,0.2)"}`,
    background: diarization ? "rgba(130,111,252,0.1)" : "rgba(128,128,128,0.05)",
    cursor: "pointer"
  }}>
              <input type="checkbox" checked={diarization} onChange={e => setDiarization(e.target.checked)} style={{
    accentColor: "hsl(262,83%,58%)"
  }} />
              <span style={{
    fontSize: "12px",
    fontWeight: 500,
    color: diarization ? "hsl(262,83%,58%)" : "inherit"
  }}>Enable diarization</span>
            </label>
          </>}
        </div>
      </div>
    </div>;
};

export const AvatarNodePreview = () => {
  const PINK = "hsl(326,78%,60%)";
  const [avatarMode, setAvatarMode] = useState("imageUrl");
  const [fieldVal, setFieldVal] = useState("");
  return <div style={{
    display: "flex",
    justifyContent: "center",
    padding: "28px 16px",
    background: "rgba(128,128,128,0.06)",
    borderRadius: "12px",
    margin: "16px 0"
  }}>
      <div style={{
    position: "relative",
    width: "240px",
    borderRadius: "12px",
    border: "1px solid hsla(326,78%,60%,0.3)",
    background: "hsla(326,78%,60%,0.1)",
    boxShadow: "0 4px 24px -4px rgba(0,0,0,0.08),0 1px 2px rgba(0,0,0,0.04)",
    fontFamily: "-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif"
  }}>
        <div style={{
    position: "absolute",
    right: "-6px",
    top: "18px",
    width: "12px",
    height: "12px",
    borderRadius: "50%",
    background: PINK,
    border: "2px solid white",
    zIndex: 10,
    boxShadow: "0 1px 3px rgba(0,0,0,0.2)"
  }} />
        <div style={{
    display: "flex",
    alignItems: "center",
    gap: "8px",
    padding: "8px 12px",
    borderBottom: "1px solid rgba(128,128,128,0.12)"
  }}>
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={PINK} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="m22 8-6 4 6 4V8Z" /><rect width="14" height="12" x="2" y="6" rx="2" ry="2" />
          </svg>
          <span style={{
    fontSize: "14px",
    fontWeight: 600,
    flex: 1
  }}>LemonSlice</span>
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    opacity: 0.3
  }}>
            <path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.21 1.21 0 0 0 1.72 0L21.64 5.36a1.21 1.21 0 0 0 0-1.72Z" />
            <path d="m14 7 3 3" /><path d="M5 6v4" /><path d="M19 14v4" /><path d="M10 2v2" /><path d="M7 8H3" /><path d="M21 16h-4" /><path d="M11 3H9" />
          </svg>
        </div>
        <div style={{
    padding: "10px 12px",
    display: "flex",
    flexDirection: "column",
    gap: "8px"
  }}>
          <p style={{
    fontSize: "10px",
    color: "#888",
    margin: 0,
    lineHeight: 1.4
  }}>Lifelike avatar streaming via LemonSlice</p>
          <div>
            <label style={{
    fontSize: "10px",
    color: "#888",
    display: "block",
    marginBottom: "3px"
  }}>Avatar Mode</label>
            <div style={{
    display: "flex",
    border: "1px solid rgba(128,128,128,0.2)",
    borderRadius: "6px",
    overflow: "hidden"
  }}>
              {[{
    v: "imageUrl",
    l: "Image URL"
  }, {
    v: "agentId",
    l: "Agent ID"
  }].map(opt => <button key={opt.v} onClick={() => {
    setAvatarMode(opt.v);
    setFieldVal("");
  }} style={{
    flex: 1,
    padding: "4px 8px",
    fontSize: "10px",
    border: "none",
    cursor: "pointer",
    background: avatarMode === opt.v ? "hsl(262,83%,58%)" : "transparent",
    color: avatarMode === opt.v ? "white" : "#888"
  }}>
                  {opt.l}
                </button>)}
            </div>
          </div>
          <div>
            <label style={{
    fontSize: "10px",
    color: "#888",
    display: "block",
    marginBottom: "3px"
  }}>{avatarMode === "imageUrl" ? "Image URL" : "Agent ID"}</label>
            <input type="text" placeholder={avatarMode === "imageUrl" ? "https://example.com/avatar.png" : "LemonSlice agent ID"} value={fieldVal} onChange={e => setFieldVal(e.target.value)} style={{
    width: "100%",
    padding: "4px 8px",
    border: "1px solid rgba(128,128,128,0.2)",
    borderRadius: "6px",
    fontSize: "12px",
    background: "rgba(128,128,128,0.06)",
    outline: "none",
    boxSizing: "border-box"
  }} />
          </div>
        </div>
      </div>
    </div>;
};

# Component Nodes

Component nodes define the AI capabilities of your agent. Each connects to the Framework node via a dedicated typed handle.

***

## Persona

Defines the agent's identity. Connected to `persona_in` on the Framework node.

| Parameter   | Type   | Description                                                                                          |
| ----------- | ------ | ---------------------------------------------------------------------------------------------------- |
| `agentName` | string | Display name of the agent                                                                            |
| `persona`   | text   | System prompt (supports markdown)                                                                    |
| `language`  | enum   | Primary language: `en`, `es`, `fr`, `de`, `pt`, `ja`, `zh`, `ko`, `it`, `nl`, `hi`, `ar`, `pl`, `tr` |

***

## LLM

The language model that generates the agent's responses. Connected to `llm_in` on the Framework node.

<LlmNodePreview />

| Parameter     | Type         | Description                         |
| ------------- | ------------ | ----------------------------------- |
| `model`       | enum         | Model identifier                    |
| `temperature` | slider (0–2) | Response creativity. Default: `0.7` |
| `max_tokens`  | number       | Max output tokens (advanced)        |

### Providers

| Provider       | Default model                             |
| -------------- | ----------------------------------------- |
| OpenAI         | `gpt-4.1-mini`                            |
| Anthropic      | `claude-sonnet-4-20250514`                |
| Gemini         | `gemini-2.5-pro`                          |
| Groq           | `llama-3.3-70b-versatile`                 |
| Together AI    | `meta-llama/Llama-3.3-70B-Instruct-Turbo` |
| DeepSeek       | `deepseek-v3`                             |
| Moonshot       | `moonshotai/kimi-k2-instruct`             |
| Mistral AI     | `ministral-8b-2410`                       |
| Cerebras       | `llama3.1-8b`                             |
| Fireworks AI   | `llama-v3p3-70b-instruct`                 |
| Perplexity     | `llama-3.1-sonar-small-128k-chat`         |
| xAI            | `grok-4-1-fast`                           |
| OpenRouter     | 500+ models                               |
| Amazon Bedrock | `amazon.nova-2-lite-v1:0`                 |
| OVHCloud       | `gpt-oss-120b`                            |
| Ollama         | Custom (local)                            |
| Azure OpenAI   | Custom deployment                         |

<Note>
  IfElse and Categorize nodes also require their own LLM connection for condition evaluation. You can reuse the same LLM node or connect a separate one.
</Note>

***

## TTS

Text-to-speech engine for voice output. Connected to `tts_in` on the Framework node.

<TtsNodePreview />

| Parameter | Type   | Description                        |
| --------- | ------ | ---------------------------------- |
| `model`   | enum   | TTS model                          |
| `voiceId` | string | Voice identifier from the provider |

### Provider-specific parameters

| Provider   | Extra parameters                            |
| ---------- | ------------------------------------------- |
| ElevenLabs | `stability` (0–1), `similarity_boost` (0–1) |
| Cartesia   | `emotion`                                   |
| OpenAI     | `instructions` (style/tone prompt)          |

### Providers

| Provider    | Default model         |
| ----------- | --------------------- |
| ElevenLabs  | `eleven_flash_v2_5`   |
| Cartesia    | `sonic-3`             |
| Deepgram    | `aura-2`              |
| OpenAI      | `gpt-4o-mini-tts`     |
| Rime        | `arcana`              |
| Inworld     | `inworld-tts-1.5-max` |
| Neuphonic   | —                     |
| Resemble AI | —                     |
| Groq        | —                     |

***

## STT

Speech-to-text engine for voice input. Connected to `stt_in` on the Framework node.

<SttNodePreview />

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `model`   | enum | STT model   |

### Provider-specific parameters

| Provider   | Extra parameters                                                          |
| ---------- | ------------------------------------------------------------------------- |
| Deepgram   | `keyterms` (comma-separated domain terms), `enable_diarization` (boolean) |
| AssemblyAI | `keyterms_prompt`, `language_detection` (boolean)                         |

### Providers

| Provider   | Default model            |
| ---------- | ------------------------ |
| Deepgram   | `nova-3`                 |
| ElevenLabs | `scribe_v2_realtime`     |
| Cartesia   | `ink-whisper`            |
| AssemblyAI | `u3-rt-pro`              |
| OpenAI     | `gpt-4o-transcribe`      |
| Groq       | `whisper-large-v3-turbo` |
| OVHCloud   | `whisper-large-v3-turbo` |

***

## Avatar

Renders a visual avatar during the call. Connected to `avatar_in` on the Framework node (optional).

<AvatarNodePreview />

| Parameter  | Type   | Description                         |
| ---------- | ------ | ----------------------------------- |
| `avatarId` | string | Avatar identifier from the provider |

**Providers:** Anam, Avatario, Beyond Presence, bitHuman, Hedra, LemonSlice, LiveAvatar, Simli, Tavus, TruGen.

<Note>
  Avatar nodes are optional and only supported on the LiveKit framework.
</Note>
