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

# prompting silk mulberry 1.6

> how to steer silk mulberry 1.6 with a style, an accent and a pace.

export const VoiceSample = ({tone, label, note, description, text, src}) => {
  const TONE_PILL = {
    happy: "bg-amber-100 text-amber-700 dark:bg-amber-500/10 dark:text-amber-300",
    excited: "bg-orange-100 text-orange-700 dark:bg-orange-500/10 dark:text-orange-300",
    sad: "bg-sky-100 text-sky-700 dark:bg-sky-500/10 dark:text-sky-300",
    whisper: "bg-indigo-100 text-indigo-700 dark:bg-indigo-500/10 dark:text-indigo-300",
    angry: "bg-red-100 text-red-700 dark:bg-red-500/10 dark:text-red-300",
    neutral: "bg-gray-100 text-gray-600 dark:bg-white/10 dark:text-gray-300"
  };
  const PLAY_EVENT = "voicesample:play";
  const fmtTime = s => {
    if (!s || !isFinite(s)) return "0:00";
    const m = Math.floor(s / 60);
    const sec = Math.floor(s % 60).toString().padStart(2, "0");
    return m + ":" + sec;
  };
  const audioRef = React.useRef(null);
  const idRef = React.useRef(null);
  if (idRef.current === null) idRef.current = Math.random();
  const [playing, setPlaying] = React.useState(false);
  const [cur, setCur] = React.useState(0);
  const [dur, setDur] = React.useState(0);
  const [copied, setCopied] = React.useState(false);
  React.useEffect(() => {
    const onOther = e => {
      if (e.detail !== idRef.current && audioRef.current) {
        audioRef.current.pause();
        setPlaying(false);
      }
    };
    window.addEventListener(PLAY_EVENT, onOther);
    return () => window.removeEventListener(PLAY_EVENT, onOther);
  }, []);
  const toggle = () => {
    const a = audioRef.current;
    if (!a) return;
    if (playing) {
      a.pause();
      setPlaying(false);
    } else {
      window.dispatchEvent(new CustomEvent(PLAY_EVENT, {
        detail: idRef.current
      }));
      a.play();
      setPlaying(true);
    }
  };
  const copyValue = description ? "description: " + description + "\ntext: " + text : text;
  const copy = () => {
    if (navigator.clipboard) navigator.clipboard.writeText(copyValue);
    setCopied(true);
    setTimeout(() => setCopied(false), 1500);
  };
  const seekTo = (clientX, el) => {
    const a = audioRef.current;
    if (!a || !dur) return;
    const rect = el.getBoundingClientRect();
    const ratio = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width));
    a.currentTime = ratio * dur;
    setCur(a.currentTime);
  };
  const pct = dur ? cur / dur * 100 : 0;
  const pill = label ? TONE_PILL.neutral : TONE_PILL[tone] || TONE_PILL.neutral;
  const pillLabel = label || "[" + (tone || "neutral") + "]";
  const lbl = "text-gray-400 dark:text-gray-500";
  return <div className="not-prose my-3 flex flex-col gap-3 rounded-2xl border border-gray-200 bg-white p-4 dark:border-white/10 dark:bg-white/[0.03]">
      <div className="flex items-start justify-between gap-3">
        <div className="flex flex-wrap items-center gap-x-2 gap-y-1">
          <span className={"rounded-md px-2 py-0.5 font-mono text-xs font-semibold " + pill}>
            {pillLabel}
          </span>
          {note && <span className="text-xs text-gray-500 dark:text-gray-400">{note}</span>}
        </div>
        <button type="button" onClick={copy} aria-label={copied ? "Copied" : "Copy prompt"} className="flex shrink-0 items-center gap-1 rounded-md px-2 py-1 text-xs text-gray-500 transition hover:bg-gray-100 hover:text-gray-800 focus:outline-none focus-visible:ring-2 focus-visible:ring-gray-400 dark:text-gray-400 dark:hover:bg-white/10 dark:hover:text-gray-200">
          {copied ? <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <path d="M20 6 9 17l-5-5" />
            </svg> : <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <rect x="9" y="9" width="13" height="13" rx="2" />
              <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
            </svg>}
          {copied ? "copied" : "copy"}
        </button>
      </div>

      <div className="overflow-hidden rounded-lg bg-gray-50 px-3 py-2.5 font-mono text-xs leading-relaxed break-words whitespace-pre-wrap text-gray-700 dark:bg-white/5 dark:text-gray-300">
        {description ? <>
            <span className={lbl}>description:</span> {description}
            {"\n"}
            <span className={lbl}>text:</span> {text}
          </> : text}
      </div>

      <audio ref={audioRef} src={src} preload="metadata" onLoadedMetadata={e => setDur(e.currentTarget.duration)} onTimeUpdate={e => setCur(e.currentTarget.currentTime)} onEnded={() => {
    setPlaying(false);
    setCur(0);
    if (audioRef.current) audioRef.current.currentTime = 0;
  }} />

      <div className="flex items-center gap-3">
        <button type="button" onClick={toggle} aria-label={playing ? "Pause" : "Play " + (label || tone || "voice") + " sample"} className="flex size-9 shrink-0 items-center justify-center rounded-full bg-gray-900 text-white transition hover:bg-gray-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-gray-900 focus-visible:ring-offset-2 dark:bg-white dark:text-gray-900 dark:focus-visible:ring-white dark:focus-visible:ring-offset-gray-900">
          {playing ? <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
              <rect x="6" y="5" width="4" height="14" rx="1" />
              <rect x="14" y="5" width="4" height="14" rx="1" />
            </svg> : <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
              <path d="M8 5v14l11-7z" />
            </svg>}
        </button>

        <div role="slider" aria-label="Seek" aria-valuemin={0} aria-valuemax={Math.round(dur) || 0} aria-valuenow={Math.round(cur)} tabIndex={0} onClick={e => seekTo(e.clientX, e.currentTarget)} onKeyDown={e => {
    const a = audioRef.current;
    if (!a || !dur) return;
    if (e.key === "ArrowRight") {
      a.currentTime = Math.min(dur, a.currentTime + 2);
      setCur(a.currentTime);
    } else if (e.key === "ArrowLeft") {
      a.currentTime = Math.max(0, a.currentTime - 2);
      setCur(a.currentTime);
    }
  }} className="relative h-1.5 flex-1 cursor-pointer rounded-full bg-gray-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-gray-400 dark:bg-white/15">
          <div className="absolute inset-y-0 left-0 rounded-full bg-gray-900 dark:bg-white" style={{
    width: pct + "%"
  }} />
        </div>

        <span className="shrink-0 font-mono text-xs tabular-nums text-gray-500 dark:text-gray-400">
          {fmtTime(cur)} / {fmtTime(dur)}
        </span>
      </div>
    </div>;
};

mulberry 1.6 is steered by a short **description**: a style, an accent and a
pace. write your text in the language's own script, then say how it should sound
in `description`. hear one:

<VoiceSample label="kannada" description="professional, Kannada accent, steady pace" text="ನಮಸ್ಕಾರ, ನೀವು ಹೇಗಿದ್ದೀರಿ? ಇಂದು ಹವಾಮಾನ ತುಂಬಾ ಚೆನ್ನಾಗಿದೆ." src="/audio/mulberry-1-6/kannada.mp3" />

## the description formula

three parts, in this order, separated by commas:

> `{style}`, `{accent}`, `{pace}`

the `{...}` parts are placeholders you fill in from the [vocabulary](#vocabulary)
below. every part is optional; leave one out and silk fills it in.

<CodeGroup>
  ```text use theme={null}
  happy, Tamil accent, steady pace
  ```

  ```text avoid theme={null}
  a cheerful female 30s voice, warm timbre, like a support agent
  ```
</CodeGroup>

the second one still works, but only `cheerful` in it changes anything. see
[free text](#free-text).

## vocabulary

every description comes down to these values:

* **style**: `professional`, `happy`, `sad`, `excited`, `angry`
* **accent**: `Indian English accent`, `Hindi accent`, `Telugu accent`,
  `Tamil accent`, `Kannada accent`, `Bengali accent`, `Punjabi accent`
* **pace**: `fast pace`, `steady pace`, `slow pace`

## what silk fills in

a part you leave out gets a default:

* **style**: `professional`
* **pace**: `fast pace`
* **accent**: the one that matches the script of your `text`

| your text is in               | accent                  |
| ----------------------------- | ----------------------- |
| devanagari                    | `Hindi accent`          |
| bengali-assamese script       | `Bengali accent`        |
| gurmukhi                      | `Punjabi accent`        |
| tamil                         | `Tamil accent`          |
| telugu                        | `Telugu accent`         |
| kannada                       | `Kannada accent`        |
| anything else, latin included | `Indian English accent` |

so `happy, slow pace` on tamil text is read as `happy, Tamil accent, slow pace`,
and marathi text sent with no `description` at all is read as
`professional, Hindi accent, fast pace`.

## free text

a mulberry 1.5 style sentence works too. silk picks out the style, accent and
pace words it contains, plus a few common synonyms, and ignores the rest:

| you write                    | silk reads              |
| ---------------------------- | ----------------------- |
| `calm`, `neutral`, `formal`  | `professional`          |
| `cheerful`, `friendly`       | `happy`                 |
| `energetic`                  | `excited`               |
| `brisk`, `quick`             | `fast pace`             |
| `conversational`, `moderate` | `steady pace`           |
| `hinglish`                   | `Hindi accent`          |
| `bangla`                     | `Bengali accent`        |
| `english`, `indian english`  | `Indian English accent` |

gender, age, pitch, timbre and role are ignored: the voice comes from `speaker`.
if a description names more than one style, the first one wins, so
`excited but calm` is `excited`.

accents are ranked instead of first-come: an explicit accent (`tamil accent`,
`bengali-accented`, `indian english accent`) beats a bare language name
(`hindi`, `indian english`), which beats a plain `english`. so
`an english-speaking voice with a bengali accent` is `Bengali accent`, and
`english voice, tamil` is `Tamil accent`.

```text theme={null}
a friendly female voice, conversational pacing, like a support agent
→ happy, <accent of the script>, steady pace
```

in the [playground](https://playground.rumik.ai), a paragraph tone like `[happy]`
becomes that paragraph's style.

## examples

press play on any one, and copy it to start from it.

<VoiceSample label="kannada" description="professional, Kannada accent, steady pace" text="ನಮಸ್ಕಾರ, ನೀವು ಹೇಗಿದ್ದೀರಿ? ಇಂದು ಹವಾಮಾನ ತುಂಬಾ ಚೆನ್ನಾಗಿದೆ." src="/audio/mulberry-1-6/kannada.mp3" />

<VoiceSample label="telugu" description="professional, Telugu accent, steady pace" text="నమస్కారం, మీరు ఎలా ఉన్నారు? ఈ రోజు వాతావరణం చాలా బాగుంది." src="/audio/mulberry-1-6/telugu.mp3" />

<VoiceSample label="bengali" description="professional, Bengali accent, steady pace" text="রবীন্দ্রনাথ ঠাকুরের গান আর কবিতা আজও বাঙালির জীবনে গভীরভাবে মিশে আছে, বিশেষ করে দুর্গাপূজার দিনগুলোতে।" src="/audio/mulberry-1-6/bengali.mp3" />

and a request body per language:

<CodeGroup>
  ```json tamil theme={null}
  {
    "model": "mulberry-1.6",
    "text": "வணக்கம், நீங்கள் எப்படி இருக்கிறீர்கள்? இன்று வானிலை மிகவும் இனிமையாக இருக்கிறது.",
    "description": "happy, Tamil accent, fast pace",
    "speaker": "siya"
  }
  ```

  ```json telugu theme={null}
  {
    "model": "mulberry-1.6",
    "text": "మీ అభ్యర్థన మాకు అందింది, రేపు ఉదయానికల్లా మా ప్రతినిధి ఒకరు మిమ్మల్ని సంప్రదిస్తారు.",
    "description": "professional, Telugu accent, steady pace",
    "speaker": "aisha"
  }
  ```

  ```json bengali theme={null}
  {
    "model": "mulberry-1.6",
    "text": "রবীন্দ্রনাথ ঠাকুরের গান আর কবিতা আজও বাঙালির জীবনে গভীরভাবে মিশে আছে, বিশেষ করে দুর্গাপূজার দিনগুলোতে।",
    "description": "professional, Bengali accent, slow pace",
    "speaker": "ira"
  }
  ```

  ```json punjabi theme={null}
  {
    "model": "mulberry-1.6",
    "text": "ਵਿਸਾਖੀ ਦੇ ਮੇਲੇ ਵਿੱਚ ਪਿੰਡਾਂ ਦੇ ਲੋਕ ਢੋਲ ਦੀ ਤਾਲ ਉੱਤੇ ਭੰਗੜਾ ਪਾਉਂਦੇ ਹਨ, ਅਤੇ ਹਰ ਪਾਸੇ ਖੁਸ਼ੀਆਂ ਦਾ ਮਾਹੌਲ ਹੁੰਦਾ ਹੈ।",
    "description": "excited, Punjabi accent, fast pace",
    "speaker": "zoya"
  }
  ```

  ```json marathi theme={null}
  {
    "model": "mulberry-1.6",
    "text": "नमस्कार, तुमची ऑर्डर उद्या संध्याकाळपर्यंत पोहोचेल.",
    "description": "professional, steady pace"
  }
  ```

  ```json gujarati theme={null}
  {
    "model": "mulberry-1.6",
    "text": "નવરાત્રિ દરમિયાન ગુજરાતના લોકો આખી રાત ગરબા રમે છે, અને આખું શહેર રંગબેરંગી રોશનીથી ઝગમગી ઊઠે છે.",
    "description": "happy, steady pace",
    "speaker": "siya"
  }
  ```
</CodeGroup>

the marathi and gujarati bodies name no accent, so silk picks one from the script:
`Hindi accent` for devanagari, `Indian English accent` for gujarati. the marathi
one names no `speaker` either, so it gets `ira`.

## send it

put your text in `text` and the delivery in `description`. add `speaker` to pick
a voice other than `ira`.

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://silk-api.rumik.ai/v1/tts \
    -H "Authorization: Bearer rk_live_•••••••••" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "mulberry-1.6",
      "text": "ನಮಸ್ಕಾರ, ನೀವು ಹೇಗಿದ್ದೀರಿ? ಇಂದು ಹವಾಮಾನ ತುಂಬಾ ಚೆನ್ನಾಗಿದೆ.",
      "description": "professional, Kannada accent, steady pace"
    }' \
    --output mulberry-1-6.wav
  ```

  ```python python theme={null}
  import requests

  resp = requests.post(
      "https://silk-api.rumik.ai/v1/tts",
      headers={"Authorization": "Bearer rk_live_•••••••••"},
      json={
          "model": "mulberry-1.6",
          "text": "ನಮಸ್ಕಾರ, ನೀವು ಹೇಗಿದ್ದೀರಿ? ಇಂದು ಹವಾಮಾನ ತುಂಬಾ ಚೆನ್ನಾಗಿದೆ.",
          "description": "professional, Kannada accent, steady pace",
      },
  )
  with open("mulberry-1-6.wav", "wb") as f:
      f.write(resp.content)
  ```

  ```javascript javascript theme={null}
  const resp = await fetch("https://silk-api.rumik.ai/v1/tts", {
    method: "POST",
    headers: {
      Authorization: "Bearer rk_live_•••••••••",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "mulberry-1.6",
      text: "ನಮಸ್ಕಾರ, ನೀವು ಹೇಗಿದ್ದೀರಿ? ಇಂದು ಹವಾಮಾನ ತುಂಬಾ ಚೆನ್ನಾಗಿದೆ.",
      description: "professional, Kannada accent, steady pace",
    }),
  });
  const fs = require("fs");
  fs.writeFileSync("mulberry-1-6.wav", Buffer.from(await resp.arrayBuffer()));
  ```
</CodeGroup>

see the [api reference](/api-reference) for the full request schema.

## writing text

* **use the language's own script.** tamil in tamil script, marathi in
  devanagari, urdu in perso-arabic. don't romanise indian languages.
* **english words can stay in latin** inside indian-language text, e.g.
  `आपका order कल तक पहुँच जाएगा।`
* **there's no `language` field.** the text decides, and its script picks the
  accent unless you name one.
* **three inline tags, no `[tone]` markers.** `<laugh>`, `<chuckle>` and
  `<sigh>` are performed where you place them:
  `हमारा नाम लिस्ट में है! <laugh> आज घर में जश्न होगा।`. any other `<tag>` is
  removed before synthesis. the overall delivery comes from `description`.
* **numbers, money, dates and times are normalized** before speaking, exactly as
  on mulberry. they come out as english number words, which read naturally inside
  any indian-language text. wrap anything that must be read digit by digit, like
  an otp, in double quotes. see [numbers, ids & dates](/normalization).

<CodeGroup>
  ```text use theme={null}
  नमस्ते, आपका order कल तक पहुँच जाएगा। आपका OTP "482093" है।
  ```

  ```text avoid theme={null}
  namaste, aapka order kal tak pahunch jayega. aapka otp 482093 hai.
  ```
</CodeGroup>

<Note>
  **keep normalization on.** `"normalization": false` sends your text through
  untouched, and raw digits and `₹` amounts are often misread.
</Note>

## length

one request speaks up to about **80 seconds** of audio: roughly 1,200 english
characters, or about 600 characters of indic-script text at a steady pace. split
longer text into sentences and send them one after another, with the same
`speaker` and `description`.

a generation that can't finish doesn't come back cut off. you get
`502 GENERATION_TOO_LONG` over http, or an `error` frame with code
`GENERATION_TOO_LONG` over a websocket, and you aren't charged for it. any other
failure, like the engine being unreachable, is `GENERATION_ERROR`.

## using mulberry 1.6 in your own voice agent

mulberry 1.6 reads a **complete sentence or utterance** at once, and **streams the
audio out** as it's generated. send each sentence as soon as your llm has
finished it, one `text` frame per utterance, and play the audio as it arrives.
keep the same `speaker` and `description` for the whole conversation.

a new `text` frame while one is still generating **interrupts** it (latest-wins),
so never send a sentence in pieces: only the last piece is spoken in full.

<CodeGroup>
  ```python use theme={null}
  # one complete utterance per frame
  await ws.send(json.dumps({"text": "உங்கள் ஆர்டர் உறுதி செய்யப்பட்டது."}))
  ```

  ```python avoid theme={null}
  # pieces of one sentence: each frame interrupts the one before
  for piece in ["உங்கள் ", "ஆர்டர் ", "உறுதி செய்யப்பட்டது."]:
      await ws.send(json.dumps({"text": piece}))
  ```
</CodeGroup>

for the session and frames, see [streaming](/streaming). the dashboard's hosted
agents don't offer mulberry 1.6 yet.
