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

# LWAI Chunker Stage

> Index pipeline stage configuration specifications

export const schema = {
  "type": "object",
  "title": "LWAI Chunker Stage",
  "description": "Splits large text into smaller semantic chunks and generates vector embeddings for each chunk using a Lucidworks AI model. Reads the input text from a pipeline context variable and writes the resulting chunks and vectors to configurable destination fields. Use this stage to prepare long-form content for vector search.",
  "required": ["accountName", "chunkingStrategy", "modelName", "inputContextVariable", "outputContextVariable"],
  "properties": {
    "skip": {
      "type": "boolean",
      "title": "Skip This Stage",
      "description": "Controls whether this stage executes during pipeline processing at runtime. When set to `true`, the stage is completely bypassed and documents pass through unchanged to the next stage. Useful for A/B testing, gradual rollouts, or temporarily disabling a stage without removing it from the pipeline.",
      "default": false,
      "hints": ["advanced"]
    },
    "label": {
      "type": "string",
      "title": "Label",
      "description": "Human-readable identifier displayed in the Fusion Admin UI, monitoring dashboards, and log messages. Use descriptive labels like `Parse Product PDFs` to aid debugging and team collaboration. Labels appear in performance metrics and error reports, making it easier to identify which stage failed.",
      "hints": ["advanced"],
      "maxLength": 255
    },
    "condition": {
      "type": "string",
      "title": "Condition",
      "description": "JavaScript expression evaluated at runtime on each document to conditionally execute this stage. The expression must return `true` to execute or `false` to skip. Access document fields using `doc.getFieldValue('fieldName')` and request parameters via `request.getFirstParam('paramName')`. For example, `doc.getFieldValue('type') === 'premium'` executes this stage only for premium content.",
      "hints": ["code", "code/javascript", "advanced"]
    },
    "accountName": {
      "type": "string",
      "title": "Account Name",
      "description": "Specifies the Lucidworks AI account name registered in the Lucidworks AI Gateway Service. This value must match an existing account. An invalid name causes all chunking calls to fail. Use the account selector to choose from configured accounts.",
      "hints": ["enumUrl:/api/query-stages/lwai-accounts"]
    },
    "chunkingStrategy": {
      "type": "string",
      "title": "Chunking Strategy",
      "description": "Specifies the text chunking algorithm used to split the input into segments, such as sentence-based or token-count-based strategies. The available strategies depend on the configured Lucidworks AI account. Choose a strategy appropriate for the document type and downstream retrieval model.",
      "hints": ["enumUrl:/api/query-stages/lwai-chunking-strategies?account=${accountName}"]
    },
    "modelName": {
      "type": "string",
      "title": "Model for Vectorization",
      "description": "Specifies the Lucidworks AI embedding model used to vectorize each text chunk after splitting. The model must be registered under the selected account with an `embedding` use case. An incompatible model type causes vectorization errors.",
      "hints": ["enumUrl:/api/query-stages/lwai-model?account=${accountName}&useCase=embedding"]
    },
    "inputContextVariable": {
      "type": "string",
      "title": "Input context variable",
      "description": "Specifies the name of the pipeline context variable whose value contains the large text to be chunked. Supports template expressions such as `<ctx.myVar>` for dynamic resolution. The variable must be populated by an upstream stage before this stage executes."
    },
    "outputContextVariable": {
      "type": "string",
      "title": "Destination Field Name & Context Output",
      "description": "Specifies the destination field name and context variable for the chunk vector output. Must contain `*_chunk_vector_*` in the name and target a dense vector field type in the Solr schema. The value populates both the document field and the pipeline context under this key."
    },
    "outputTextSpans": {
      "type": "string",
      "title": "Destination Field Name for Text Spans",
      "description": "Specifies the document field where the character span positions `[start, stop]` for each text chunk are stored, for example `body_spans_ss`. Use this field to map chunks back to their source positions in the original document. Leave empty to skip span output."
    },
    "outputTextChunks": {
      "type": "string",
      "title": "Destination Field Name for Text Chunks (not the vectors)",
      "description": "Specifies the document field where the raw text of each chunk is stored, for example `body_chunks_ss`. This field contains the human-readable text segments before vectorization. Leave empty to skip plain-text chunk output."
    },
    "chunkerConfig": {
      "type": "array",
      "title": "Chunker Configuration",
      "description": "Sends additional key-value parameters to the Lucidworks AI chunker alongside the standard configuration. Use this to pass chunker-specific options such as maximum chunk size or overlap. Each entry requires a `key` and an optional `value`.",
      "minItems": 0,
      "items": {
        "type": "object",
        "required": ["key"],
        "properties": {
          "key": {
            "type": "string",
            "title": "Parameter Name"
          },
          "value": {
            "type": "string",
            "title": "Parameter Value"
          }
        }
      }
    },
    "modelConfig": {
      "type": "array",
      "title": "Model Configuration",
      "description": "Sends additional key-value configuration parameters to the Lucidworks AI embedding model alongside the vectorization request. Use this to supply model-specific options not covered by the standard configuration. Each entry requires a `key` and an optional `value`.",
      "minItems": 0,
      "items": {
        "type": "object",
        "required": ["key"],
        "properties": {
          "key": {
            "type": "string",
            "title": "Parameter Name"
          },
          "value": {
            "type": "string",
            "title": "Parameter Value"
          }
        }
      }
    },
    "maxTries": {
      "type": "integer",
      "title": "Maximum Asynchronous Call Tries",
      "description": "Sets the maximum number of attempts for each asynchronous Lucidworks AI API call. Increase this value to add retry resilience for transient network or service errors. Each retry is attempted immediately after a failure with no delay.",
      "default": 1,
      "minimum": 1,
      "exclusiveMinimum": false
    },
    "failOnError": {
      "type": "boolean",
      "title": "Fail on Error",
      "description": "Controls whether a chunking or vectorization error causes the entire pipeline to fail. When `true`, any error throws an exception and stops the document. When `false`, errors are logged and the document continues. Set to `true` in production to surface integration issues immediately.",
      "default": false
    }
  },
  "category": "AI",
  "categoryPriority": 10,
  "unsafe": false
};

export const SchemaParamFields = ({schema}) => {
  const sanitize = str => {
    if (typeof str !== "string") return str;
    return str.replace(/^"(.*)"$/s, "$1").replace(/\\/g, "").replace(/"/g, "'");
  };
  const renderMd = str => {
    const s = sanitize(str);
    const text = (/[.!?]\)*$/).test(s) ? s : `${s}.`;
    return text.split(/(\*\*[^*]+\*\*|_[^_]+_|`[^`]+`)/g).map((part, i) => {
      if (part.startsWith("**")) return <strong key={i}>{part.slice(2, -2)}</strong>;
      if (part.startsWith("_")) return <em key={i}>{part.slice(1, -1)}</em>;
      if (part.startsWith("`")) return <code key={i}>{part.slice(1, -1)}</code>;
      return part;
    });
  };
  const {description, properties = {}, required: requiredProps = []} = schema;
  const visibleProps = useMemo(() => Object.entries(properties).filter(([, prop]) => !prop.hints?.includes("hidden")), [properties]);
  const renderProp = ([name, prop]) => {
    const isRequired = requiredProps.includes(name);
    const hasDefault = prop.default !== undefined;
    const rawDefault = prop.default;
    const hints = prop.hints || [];
    const isComplexDefault = hasDefault && (typeof rawDefault === "object" || typeof rawDefault === "string" && (rawDefault.length > 20 || rawDefault.includes('"')));
    const postBadges = [];
    if (prop.title) {
      postBadges.push(<><span className="text-stone-400 dark:text-stone-500">API property: </span>{name}</>);
    }
    const constraints = [];
    if (prop.minimum !== undefined && prop.maximum !== undefined) {
      constraints.push(`Range: ${prop.minimum} – ${prop.maximum}`);
    } else if (prop.minimum !== undefined) {
      constraints.push(`Min: ${prop.minimum}`);
    } else if (prop.maximum !== undefined) {
      constraints.push(`Max: ${prop.maximum}`);
    }
    if (prop.minLength !== undefined && prop.maxLength !== undefined) {
      constraints.push(`Length: ${prop.minLength} – ${prop.maxLength}`);
    } else if (prop.minLength !== undefined) {
      constraints.push(`Min length: ${prop.minLength}`);
    } else if (prop.maxLength !== undefined) {
      constraints.push(`Max length: ${prop.maxLength}`);
    }
    const fieldProps = {
      key: name,
      body: prop.title || name,
      type: prop.type,
      ...postBadges.length > 0 && ({
        post: postBadges
      }),
      ...isRequired && ({
        required: true
      }),
      ...!isComplexDefault && hasDefault ? {
        default: sanitize(String(rawDefault))
      } : {}
    };
    const isObject = prop.type === "object" && prop.properties;
    const isArrayOfObjects = prop.type === "array" && prop.items?.type === "object" && prop.items.properties;
    return <ParamField {...fieldProps}>
        {prop.description && <p>{renderMd(prop.description)}</p>}

        {prop.enum && <p>
            Allowed values: 
            {prop.enum.map((v, i) => <>{i > 0 && ", "}<code key={i}>{String(v)}</code></>)}
          </p>}

        {constraints.length > 0 && <p className="text-stone-500 dark:text-stone-400 text-sm">
            {constraints.join(" · ")}
          </p>}

        {isComplexDefault && <div className="flex">
            <p>
              <strong>Default:</strong>
            </p>
            <pre className="!my-0">
              <code>
                {JSON.stringify(rawDefault, null, 2)}
              </code>
            </pre>
          </div>}

        {isArrayOfObjects && <Expandable title="item properties">
            <SchemaParamFields schema={{
      properties: prop.items.properties,
      required: prop.items.required
    }} />
          </Expandable>}

        {isObject && <Expandable title="properties">
            <SchemaParamFields schema={{
      properties: prop.properties,
      required: prop.required
    }} />
          </Expandable>}
      </ParamField>;
  };
  return <div>
      {description && <p>{renderMd(description)}</p>}

      {visibleProps.map(renderProp)}
    </div>;
};

export const LwTemplate = ({title = "Key questions to get you started", icon = "sparkles", cta = "Powered by Agent Studio", linkHref = "https://lucidworks.com/demo/?utm_source=docs&utm_medium=referral&utm_campaign=docs_cta_ai"}) => {
  const [isLoaded, setIsLoaded] = useState(false);
  useEffect(() => {
    const timer = setTimeout(() => {
      setIsLoaded(true);
    }, 500);
    return () => clearTimeout(timer);
  }, []);
  return <div className="lw-template-container">
      <Card title={title} icon={icon}>
        {isLoaded && <span dangerouslySetInnerHTML={{
    __html: `<lw-template id="a029c1a9-28be-427e-b0e1-5d918920246a"></lw-template
            >`
  }} />}
        <Link href={linkHref} className="agent-studio-link text-left text-gray-600 gap-2 dark:text-gray-400 text-sm font-medium flex flex-row items-center hover:text-primary dark:hover:text-primary-light group-hover:text-primary group-hover:dark:text-primary-light">Powered by Lucidworks Agent Studio</Link>
      </Card>
    </div>;
};

[localhost link]: http://localhost:3000/docs/lucidworks-search/09-developer-documentation/config-specs/index-pipeline-stages/lwai-chunker-stage

[mintlify link]: https://doc.lucidworks.com/docs/lucidworks-search/09-developer-documentation/config-specs/index-pipeline-stages/lwai-chunker-stage

[old doc.lw link]: https://doc.lucidworks.com/managed-fusion/5.9/uiycyd

Lucidworks Search 5.9.12 and later integrates with Lucidworks AI to perform [chunking](/docs/lucidworks-search/11-vector-search/chunking).
When you include chunking in your index pipeline, Lucidworks AI automatically splits large documents into smaller, more focused segments.
This approach is especially powerful when paired with Neural Hybrid Search to surface the most relevant chunks instead of entire documents.
Chunking also improves the accuracy of AI assistants by delivering semantically rich training data in precise, context-aware pieces.

This stage performs chunking [asynchronously](/docs/lucidworks-search/09-developer-documentation/config-specs/index-pipeline-stages/overview#run-stages-asynchronously) and stores those vectors in Solr.

Click your use case below to see examples of how chunking can enhance the search experience:

<Tabs>
  <Tab title="Business-to-Consumer" icon="cart-shopping" iconType="sharp-solid">
    * Break product descriptions into focused chunks so customers can find relevant details faster.
    * Reduce support tickets by training AI assistants on semantically-segmented help articles for more accurate answers.
    * Split multimedia transcripts (like product videos or webinars) into meaningful chunks so customers can find answers in content they wouldn't normally read.
  </Tab>

  <Tab title="Business-to-Business" icon="briefcase" iconType="sharp-solid">
    * Break down long technical specs so buyers can validate requirements without reading full documents.
    * Improve Request For Quote (RFQ) matching by extracting key details so sales teams can respond faster and more accurately.
    * Surface relevant regulatory or compliance info within dense documents for more efficient legal reviews.
  </Tab>

  <Tab title="Knowledge Management" icon="lightbulb" iconType="sharp-solid">
    * Chunk large case studies, policies, or contracts into semantically useful segments so employees get faster answers.
    * Boost AI assistant quality by providing smaller, context-rich units of information so responses are more precise.
    * Improve onboarding by connecting new hires with task-relevant excerpts from training content, runbooks, and policy documents.
  </Tab>
</Tabs>

<Note>
  This feature is available starting in Lucidworks Search 5.9.12 and in all subsequent Lucidworks Search 5.9 releases.
</Note>

<LwTemplate />

## Prerequisites

To use this stage, non-admin Fusion users must be granted the `PUT,POST,GET:/LWAI-ACCOUNT-NAME/**` permission in Fusion, which is the Lucidworks AI API Account Name defined in [Lucidworks AI Gateway](/docs/lw-platform/lw-ai/lw-ai-gateway) when this stage is configured.

Click **Get Started** below to see how to enable chunking in Fusion:

<iframe src="https://app.supademo.com/embed/cmfzg6uw4009oxx0i1ptmac82?embed_v=2&utm_source=embed" loading="lazy" title="Enable chunking in Fusion" allow="clipboard-write" frameborder="0" webkitallowfullscreen="true" mozallowfullscreen="true" allowfullscreen style={{  width: '100%', height: '500px' }} />

<Note>
  Additional requirements for the stage are:

  * Use a V2 connector. Only V2 connectors work for this task and not other options, such as PBL or V1 connectors.
  * Remove the `Apache Tika` stage from your parser because it can cause datasource failures with the following error: "The following components failed: \[class com.lucidworks.connectors.service.components.job.processor.DefaultDataProcessor : Only Tika Container parser can support Async Parsing.]"
</Note>

## Strategies

Choose one of these chunking strategies.

<Accordion title="Strategy descriptions" defaultOpen="true">
  <ParamField path="dynamic-newline">
    Split on newlines, then merges lines up to `maxChunkSize`. Default for `maxChunkSize` is 512 tokens.
  </ParamField>

  <ParamField path="dynamic-sentence">
    Join sentences until `maxChunkSize`. Able to overlap using `overlapSize`.
  </ParamField>

  <ParamField path="sentence">
    Fixes the number of sentences per chunk. The default for `chunkSize` is 5.  Able to overlap using `overlapSize`.
  </ParamField>

  <ParamField path="regex-splitter">
    Set `regex` to split with regex using Python `re` conventions.
  </ParamField>

  <ParamField path="semantic">
    Group semantically similar sentences up to `maxChunkSize`. This strategy is the slowest but most precise. Able to overlap using `overlapSize`.
  </ParamField>
</Accordion>

Additional information about these Chunker names and keys are defined in the [Async Chunking API](/docs/lw-platform/lw-ai/lw-ai-apis/lw-ai-async-chunking-api#chunkerconfig).

## How asynchronous results return

The LWAI Chunker Index stage submits text to the Async Chunking API, which returns a `chunkingId`.
Later, results are fetched and written back to the same index pipeline using [Solr Partial Update Indexer](/docs/lucidworks-search/09-developer-documentation/config-specs/index-pipeline-stages/solr-partial-update-indexer).
This means the same pipeline is visited twice: once for the original document and once to apply chunk fields and vectors.

## What this stage writes

* Vector field (required): in **Destination Field Name & Context Output**, use a **dense vector** field and include `chunk_vector` in the field name, for example, `body_chunk_vector_384v`.
* Text chunks field (recommended): set **Destination Field Name for Text Chunks**, for example, `body_chunks_ss`.
* Doctype marker (required for chunking queries): add `_lw_chunk_doctype_s` with a marker for use in [Chunking Neural Hybrid Query stage](/docs/lucidworks-search/09-developer-documentation/config-specs/query-pipeline-stages/chunking-neural-hybrid-query-stage).\
  Markers:
  * `_lw_chunk_root` on the root document
  * The vector field name, such as `body_chunk_vector_384v`, on child documents

## Example setup for this stage

1. Add **LWAI Chunker Index Stage** to your index pipeline:
   * **Chunking Strategy:** for example, you can use `sentence`.
   * **Model for Vectorization:** pick your embedding model.
   * **Input context variable:** field/ctx containing the text to chunk.
   * **Destination Field Name & Context Output:** `body_chunk_vector_384v`. This must contain `chunk_vector` and be a dense vector field.
   * **Destination Field Name for Text Chunks:** `body_chunks_ss`.
   * (Optional) In **Chunker Configuration**, set `chunkSize=5` or `overlapSize=1`.
2. In the same pipeline, add **Solr Partial Update Indexer**:
   * Uncheck **Map to Solr Schema**
   * Uncheck **Enable Concurrency Control**
   * Uncheck **Reject Update if Solr Document is not Present**
   * Check **Process All Pipeline Doc Fields**
   * Check **Allow reserved fields**
3. Save to let the async results come back to the same pipeline.
4. Index a sample and verify:
   * The original doc is present.
   * After async completes, the doc has `body_chunk_vector_384v` to indicate a vector, `body_chunks_ss` to indicate text chunks, and any `_lw_chunk_doctype_s` markers for root and children.

<Tip>
  Fusion truncates text sent for chunking to \~50,000 characters, so plan chunking inputs accordingly.
</Tip>

## What to use for query

Use **Chunking Neural Hybrid Query** to combine lexical and vector search over parent and child chunks.
It expects a vector field like `body_chunk_vector_384v` and the `_lw_chunk_doctype_s` markers described above.

## Configuration

<Tip>
  When entering configuration values in the UI, use *unescaped* characters, such as `\t` for the tab character. When entering configuration values in the API, use *escaped* characters, such as `\\t` for the tab character.
</Tip>

<SchemaParamFields schema={schema} />
