From a0e21c771ce3dca40f1a8bf7ec60e5743dd44933 Mon Sep 17 00:00:00 2001 From: alhendrickson Date: Wed, 21 Oct 2026 12:26:41 +0000 Subject: [PATCH 1/3] feat(helm): Run OCR service in EE and make OCR tutorial --- helm-charts/cogstack-ce-helm/Chart.lock | 13 +- helm-charts/cogstack-ce-helm/Chart.yaml | 5 + helm-charts/cogstack-ce-helm/README.md | 6 + .../cogstack-ce-helm/templates/NOTES.txt | 8 + .../cogstack-ce-helm/templates/_helpers.tpl | 18 ++ .../templates/jupyterhub-configmap.yaml | 1 + helm-charts/cogstack-ce-helm/values.yaml | 17 ++ .../examples/ocr-service-tutorial.ipynb | 237 ++++++++++++++++++ 8 files changed, 300 insertions(+), 5 deletions(-) create mode 100644 helm-charts/cogstack-jupyterhub-helm/examples/ocr-service-tutorial.ipynb diff --git a/helm-charts/cogstack-ce-helm/Chart.lock b/helm-charts/cogstack-ce-helm/Chart.lock index a0aa576c..413f5334 100644 --- a/helm-charts/cogstack-ce-helm/Chart.lock +++ b/helm-charts/cogstack-ce-helm/Chart.lock @@ -1,13 +1,13 @@ dependencies: - name: medcat-service-helm repository: file://../medcat-service-helm - version: 0.5.0 + version: 0.6.0 - name: medcat-service-helm repository: file://../medcat-service-helm - version: 0.5.0 + version: 0.6.0 - name: medcat-trainer-helm repository: file://../medcat-trainer-helm - version: 0.5.0 + version: 0.5.1 - name: opensearch repository: https://opensearch-project.github.io/helm-charts/ version: 3.5.0 @@ -17,5 +17,8 @@ dependencies: - name: cogstack-jupyterhub-helm repository: file://../cogstack-jupyterhub-helm version: 0.0.1 -digest: sha256:53f0d3986395d1e402a5409d7b69beb576ead2376630a05f608e44acd451c2c9 -generated: "2026-09-17T09:23:54.811701571Z" +- name: ocr-service-helm + repository: file://../ocr-service-helm + version: 0.1.0 +digest: sha256:f03a094b399753bca80b2ebe922bdd1f365bcde4f3c0d5c4d678b61b3834c3c2 +generated: "2026-10-21T09:43:39.637902212Z" diff --git a/helm-charts/cogstack-ce-helm/Chart.yaml b/helm-charts/cogstack-ce-helm/Chart.yaml index cb98ce1f..ec229924 100644 --- a/helm-charts/cogstack-ce-helm/Chart.yaml +++ b/helm-charts/cogstack-ce-helm/Chart.yaml @@ -58,3 +58,8 @@ dependencies: condition: cogstack-jupyterhub.enabled version: ">=0.0.1" repository: "file://../cogstack-jupyterhub-helm" + - name: ocr-service-helm + alias: ocr-service + condition: cogstack-jupyterhub.enabled + version: ">=0.0.1" + repository: "file://../ocr-service-helm" diff --git a/helm-charts/cogstack-ce-helm/README.md b/helm-charts/cogstack-ce-helm/README.md index e7fdb284..bb99a657 100644 --- a/helm-charts/cogstack-ce-helm/README.md +++ b/helm-charts/cogstack-ce-helm/README.md @@ -87,6 +87,7 @@ kubectl delete namespace cogstack | file://../medcat-service-helm | medcat-service(medcat-service-helm) | >=0.0.1 | | file://../medcat-service-helm | anoncat-service(medcat-service-helm) | >=0.0.1 | | file://../medcat-trainer-helm | medcat-trainer(medcat-trainer-helm) | >=0.0.1 | +| file://../ocr-service-helm | ocr-service(ocr-service-helm) | >=0.0.1 | | https://opensearch-project.github.io/helm-charts/ | opensearch | 3.5.0 | | https://opensearch-project.github.io/helm-charts/ | opensearch-dashboards | 3.5.0 | @@ -111,6 +112,9 @@ kubectl delete namespace cogstack | cogstack-jupyterhub.jupyterhub.singleuser.extraEnv.ANONCAT_SERVICE_URL.valueFrom.configMapKeyRef.name | string | `"cogstack-ce-helm-jupyterhub-config"` | | | cogstack-jupyterhub.jupyterhub.singleuser.extraEnv.MEDCAT_SERVICE_URL.valueFrom.configMapKeyRef.key | string | `"MEDCAT_SERVICE_URL"` | | | cogstack-jupyterhub.jupyterhub.singleuser.extraEnv.MEDCAT_SERVICE_URL.valueFrom.configMapKeyRef.name | string | `"cogstack-ce-helm-jupyterhub-config"` | | +| cogstack-jupyterhub.jupyterhub.singleuser.extraEnv.OCR_SERVICE_URL.name | string | `"OCR_SERVICE_URL"` | | +| cogstack-jupyterhub.jupyterhub.singleuser.extraEnv.OCR_SERVICE_URL.valueFrom.configMapKeyRef.key | string | `"OCR_SERVICE_URL"` | | +| cogstack-jupyterhub.jupyterhub.singleuser.extraEnv.OCR_SERVICE_URL.valueFrom.configMapKeyRef.name | string | `"cogstack-ce-helm-jupyterhub-config"` | | | cogstack-jupyterhub.jupyterhub.singleuser.image.name | string | `"cogstacksystems/jupyter-singleuser"` | JupyterHub singleuser image name. | | cogstack-jupyterhub.jupyterhub.singleuser.image.pullPolicy | string | `"IfNotPresent"` | JupyterHub singleuser image pull policy. | | cogstack-jupyterhub.jupyterhub.singleuser.image.tag | string | `"2.2.2"` | JupyterHub singleuser image tag. | @@ -124,6 +128,8 @@ kubectl delete namespace cogstack | medcat-trainer.provisioning.enabled | bool | `true` | Enable provisioning of projects and models on startup. | | medcat-trainer.provisioning.existingConfigMap.name | string | `"cogstack-ce-helm-example-trainer-provisioining"` | Existing ConfigMap name containing the provisioning configuration. | | nameOverride | string | `""` | This is to override the chart name. | +| ocr-service.enabled | bool | `true` | Enable MedCAT service deployment. | +| ocr-service.replicasCount | int | `1` | Number of MedCAT service replicas. | | opensearch-dashboards.enabled | bool | `true` | Deploy an opensearch-dashboards instance | | opensearch.enabled | bool | `true` | Deploy an opensearch cluster | | opensearch.extraEnvs | list | Sets the initial admin password for OpenSearch | Extra environment variables to pass to OpenSearch. | diff --git a/helm-charts/cogstack-ce-helm/templates/NOTES.txt b/helm-charts/cogstack-ce-helm/templates/NOTES.txt index c0e06a27..7f1a638b 100644 --- a/helm-charts/cogstack-ce-helm/templates/NOTES.txt +++ b/helm-charts/cogstack-ce-helm/templates/NOTES.txt @@ -36,6 +36,13 @@ export JUPYTERHUB_PROXY_SERVICE_PORT=$(kubectl get svc $JUPYTERHUB_PROXY_PUBLIC_ kubectl --namespace {{ .Release.Namespace }} port-forward svc/$JUPYTERHUB_PROXY_PUBLIC_SERVICE 8000:$JUPYTERHUB_PROXY_SERVICE_PORT & {{- end }} +{{- if index .Values "ocr-service" "enabled" }} +# 5. OCR Service +export OCR_SERVICE_POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name=ocr-service,app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") +export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $OCR_SERVICE_POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") +kubectl --namespace {{ .Release.Namespace }} port-forward $OCR_SERVICE_POD_NAME 8090:$CONTAINER_PORT --pod-running-timeout=5m0s & +{{- end }} + echo "Visit http://127.0.0.1:5000 to use MedCAT Service" echo "Visit http://127.0.0.1:5001 to use AnonCAT" echo "Visit http://127.0.0.1:8080 to use MedCAT Trainer" @@ -43,6 +50,7 @@ echo "Visit http://127.0.0.1:8080 to use MedCAT Trainer" {{ if index .Values "opensearch-dashboards" "enabled" }}echo "Visit http://127.0.0.1:5601 to use OpenSearch Dashboards"{{- end }} {{ if index .Values "cogstack-jupyterhub" "enabled" }}echo "Visit http://127.0.0.1:8000 to use jupyterhub"{{- end }} {{ if index .Values "cogstack-jupyterhub" "enabled" }}echo "Visit http://127.0.0.1:8000/user/admin/notebooks/medcat-service-tutorial.ipynb to get started with a tutorial"{{- end }} +{{ if index .Values "ocr-service" "enabled" }}echo "Visit http://127.0.0.1:8090/ to use OCR Service"{{- end }} # Setup Complete # Run this command line to setup port-forwarding and access services diff --git a/helm-charts/cogstack-ce-helm/templates/_helpers.tpl b/helm-charts/cogstack-ce-helm/templates/_helpers.tpl index dc309da5..b24239a1 100644 --- a/helm-charts/cogstack-ce-helm/templates/_helpers.tpl +++ b/helm-charts/cogstack-ce-helm/templates/_helpers.tpl @@ -104,6 +104,24 @@ http://{{ include "anoncat-service.service-name" . }}:{{ include "anoncat-servic {{- end -}} {{- end -}} +{{- define "ocr-service.service-name" -}} +{{ include "ocr-service-helm.fullname" (dict "Values" .Values "Chart" (dict "Name" "ocr-service") "Release" .Release ) }} +{{- end -}} + +{{- define "ocr-service.port" -}} +{{ index .Values "ocr-service" "service" "port" }} +{{- end -}} + +{{- define "ocr-service.url" -}} +{{- $url := (index .Values "ocr-service" "url") | default "" -}} +{{- if $url -}} +{{ $url }} +{{- else if not (index .Values "ocr-service" "enabled") -}} +"unset" +{{- else -}} +http://{{ include "ocr-service.service-name" . }}:{{ include "ocr-service.port" . }} +{{- end -}} +{{- end -}} {{- define "opensearch.url" -}} diff --git a/helm-charts/cogstack-ce-helm/templates/jupyterhub-configmap.yaml b/helm-charts/cogstack-ce-helm/templates/jupyterhub-configmap.yaml index 01358f3f..17d17df0 100644 --- a/helm-charts/cogstack-ce-helm/templates/jupyterhub-configmap.yaml +++ b/helm-charts/cogstack-ce-helm/templates/jupyterhub-configmap.yaml @@ -5,3 +5,4 @@ metadata: data: MEDCAT_SERVICE_URL: {{ include "medcat-service.url" . }} ANONCAT_SERVICE_URL: {{ include "anoncat-service.url" . }} + OCR_SERVICE_URL: {{ include "ocr-service.url" . }} \ No newline at end of file diff --git a/helm-charts/cogstack-ce-helm/values.yaml b/helm-charts/cogstack-ce-helm/values.yaml index bcd06527..0833406f 100644 --- a/helm-charts/cogstack-ce-helm/values.yaml +++ b/helm-charts/cogstack-ce-helm/values.yaml @@ -38,6 +38,17 @@ medcat-service: # -- MedCAT service image tag. # tag: "1.3.0" +ocr-service: + # -- Enable MedCAT service deployment. + enabled: true + # -- Number of MedCAT service replicas. + replicasCount: 1 + # image: + # -- OCR service image repository. + # repository: cogstacksystems/ocr-service + # -- OCR service image tag. + # tag: "1.1.1" + medcat-trainer: # -- Enable MedCAT Trainer deployment. enabled: true @@ -126,3 +137,9 @@ cogstack-jupyterhub: configMapKeyRef: name: cogstack-ce-helm-jupyterhub-config key: ANONCAT_SERVICE_URL + OCR_SERVICE_URL: + name: OCR_SERVICE_URL + valueFrom: + configMapKeyRef: + name: cogstack-ce-helm-jupyterhub-config + key: OCR_SERVICE_URL diff --git a/helm-charts/cogstack-jupyterhub-helm/examples/ocr-service-tutorial.ipynb b/helm-charts/cogstack-jupyterhub-helm/examples/ocr-service-tutorial.ipynb new file mode 100644 index 00000000..2a0fa885 --- /dev/null +++ b/helm-charts/cogstack-jupyterhub-helm/examples/ocr-service-tutorial.ipynb @@ -0,0 +1,237 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a1c4e8f2-1b3d-4c9a-9e2f-ocr-tutorial-intro", + "metadata": {}, + "source": [ + "# OCR Service Tutorial\n", + "\n", + "This is a step-by-step walkthrough that shows how to call the CogStack OCR service over HTTP:\n", + "OCR of scanned files, and text extraction from a Word document.\n", + "\n", + "OCR is optical character recognition. Use this when you have images or scanned in documents containing text. The OCR service is also able to work with machine readable documents that do not need OCR, like word documents or pdfs containing text. This tutorial will show how it can parse both in the same way.\n", + "\n", + "## Overview\n", + "\n", + "### Who is this tutorial for:\n", + "This is for developers, data engineers, and analysts who want a quick, practical example of how\n", + "to integrate OCR Service into a Python workflow (and later into a notebook-based analysis).\n", + "\n", + "### What will the tutorial do:\n", + "1) Define sample files and the service URL.\n", + "2) OCR a PNG of a scanned document, by calling the ocr-service API\n", + "3) Extract text from a Word document\n", + "4) OCR a scanned PDF\n", + "\n", + "### Prerequisites\n", + "The best way to run this notebook interactively is to run the CogStack Community Edition with Helm. Look at https://docs.cogstack.org/ to get started." + ] + }, + { + "cell_type": "markdown", + "id": "b2d5f9a3-2c4e-4d0b-8f31-ocr-tutorial-init-md", + "metadata": {}, + "source": [ + "## Initialisation: Define the inputs and services\n", + "We pick three sample files and the HTTP endpoint we will call.\n", + "\n", + "The samples are a PNG of a scanned document, a Word document that already contains text, and a scanned PDF:\n", + "- [pat_id_1.png](https://github.com/CogStack/cogstack-platform/blob/ocr-service-v1.1.3/apps/ocr-service/ocr_service/tests/resources/docs/generic/pat_id_1.png)\n", + "- [pat_id_1.docx](https://github.com/CogStack/cogstack-platform/blob/ocr-service-v1.1.3/apps/ocr-service/ocr_service/tests/resources/docs/generic/pat_id_1.docx)\n", + "- [pat_id_1.pdf](https://github.com/CogStack/cogstack-platform/blob/ocr-service-v1.1.3/apps/ocr-service/ocr_service/tests/resources/docs/generic/pat_id_1.pdf)\n", + "\n", + "For the service URL, if using the cogstack community edition helm chart, this should be setup for you automatically using kubernetes services and env vars. Otherwise change it accordingly." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3e6a0b4-3d5f-4e1c-9a42-ocr-tutorial-init-code", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import os\n", + "\n", + "import requests\n", + "\n", + "ocr_base_url = os.getenv(\n", + " \"OCR_SERVICE_URL\", \"http://cogstack-ocr-service:8090\"\n", + ").rstrip(\"/\")\n", + "\n", + "ocr_url = ocr_base_url + \"/api/process\"\n", + "\n", + "base_url = \"https://raw.githubusercontent.com/CogStack/cogstack-platform/ocr-service-v1.1.3/apps/ocr-service/ocr_service/tests/resources/docs\"\n", + "os.makedirs(\"data/ocr-service-tutorial\", exist_ok=True)\n", + "\n", + "open(\"data/ocr-service-tutorial/pat_id_1.png\", \"wb\").write(requests.get(f\"{base_url}/generic/pat_id_1.png\").content)\n", + "open(\"data/ocr-service-tutorial/pat_id_1.docx\", \"wb\").write(requests.get(f\"{base_url}/generic/pat_id_1.docx\").content)\n", + "open(\"data/ocr-service-tutorial/pat_id_1.pdf\", \"wb\").write(requests.get(f\"{base_url}/generic/pat_id_1.pdf\").content)" + ] + }, + { + "cell_type": "markdown", + "id": "a7c0e4f8-7b93-4250-de86-ocr-tutorial-png-md", + "metadata": {}, + "source": [ + "## OCR a scanned document\n", + "We can now use ocr service to read text from an image of a page.\n", + "\n", + "We have a PNG file that represents a scanned document. It does not already contain text, so the service will need to OCR it." + ] + }, + { + "cell_type": "markdown", + "id": "b8d1f5a9-8c04-4361-ef97-ocr-tutorial-png-display", + "metadata": {}, + "source": [ + "The scanned document is a PNG file, that looks like this:\n", + "\n", + "![Scanned document](https://raw.githubusercontent.com/CogStack/cogstack-platform/ocr-service-v1.1.3/apps/ocr-service/ocr_service/tests/resources/docs/generic/pat_id_1.png)" + ] + }, + { + "cell_type": "markdown", + "id": "c9e2a6b0-9d15-4472-fa08-ocr-tutorial-png-ocr-md", + "metadata": {}, + "source": [ + "We will send this image to the ocr service and print the text it recovers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0f3b7c1-0e26-4583-ab19-ocr-tutorial-png-ocr-code", + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"data/ocr-service-tutorial/pat_id_1.png\", \"rb\") as f:\n", + " png_result = requests.post(ocr_url, files={\"file\": (\"pat_id_1.png\", f)}).json()\n", + "\n", + "print(\"=== OCR Service: PNG ===\")\n", + "print(png_result.get(\"result\").get(\"text\"))" + ] + }, + { + "cell_type": "markdown", + "id": "e1a4c8d2-1f37-4694-bc20-ocr-tutorial-png-commentary", + "metadata": {}, + "source": [ + "From the above result, we can see that the service has recovered the clinical note from the scanned image. The wording can differ slightly, because the service is reading an image of the page.\n", + "\n", + "We can see the raw JSON response from ocr service by printing it" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f2b5d9e3-2a48-4705-cd31-ocr-tutorial-raw-json", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"=== OCR Service: Raw results ===\")\n", + "print(json.dumps(png_result, indent=2))" + ] + }, + { + "cell_type": "markdown", + "id": "d4f7b1c5-4e60-4f2d-ab53-ocr-tutorial-docx-md", + "metadata": {}, + "source": [ + "## Extract text from a Word document\n", + "We can also use ocr service to extract text from a Word file.\n", + "\n", + "This document already contains text, so the service can read it without OCR." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e5a8c2d6-5f71-403e-bc64-ocr-tutorial-docx-code", + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"data/ocr-service-tutorial/pat_id_1.docx\", \"rb\") as f:\n", + " docx_result = requests.post(ocr_url, files={\"file\": (\"pat_id_1.docx\", f)}).json()\n", + "\n", + "print(\"=== OCR Service: Word document ===\")\n", + "print(docx_result.get(\"result\").get(\"text\"))" + ] + }, + { + "cell_type": "markdown", + "id": "f6b9d3e7-6a82-414f-cd75-ocr-tutorial-docx-commentary", + "metadata": {}, + "source": [ + "From the above result, we can see that the service has extracted the clinical note from the Word document." + ] + }, + { + "cell_type": "markdown", + "id": "a3c7e8f4-7b93-4250-de86-ocr-tutorial-pdf-md", + "metadata": {}, + "source": [ + "## OCR a scanned PDF\n", + "A scanned PDF is the same idea as the PNG: the pages are images, so the service uses OCR to recover the text." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b4d8f9a5-8c04-4361-ef97-ocr-tutorial-pdf-code", + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"data/ocr-service-tutorial/pat_id_1.pdf\", \"rb\") as f:\n", + " pdf_result = requests.post(ocr_url, files={\"file\": (\"pat_id_1.pdf\", f)}).json()\n", + "\n", + "print(\"=== OCR Service: scanned PDF ===\")\n", + "print(pdf_result.get(\"result\").get(\"text\"))" + ] + }, + { + "cell_type": "markdown", + "id": "c5e0a6b6-9d15-4472-fa08-ocr-tutorial-pdf-commentary", + "metadata": {}, + "source": [ + "From the above result, we can see that the service has recovered text from the scanned PDF." + ] + }, + { + "cell_type": "markdown", + "id": "e1a4c8d2-1f37-4694-bc21-ocr-tutorial-summary", + "metadata": {}, + "source": [ + "## Summary\n", + "This is the end of this tutorial.\n", + "\n", + "We can see by calling the OCR service, we are able to extract text from images, Word files, and scanned PDFs just by calling one HTTP API.\n", + "\n", + "## What next?\n", + "Take the extracted text and follow the MedCAT + OpenSearch end-to-end tutorial (`medcat-opensearch-e2e.ipynb`). That shows how to ingest the text into OpenSearch and run MedCAT on top." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 6ab90ac96003dbe1ffdc9378333e2ed427fb4c3b Mon Sep 17 00:00:00 2001 From: alhendrickson Date: Wed, 21 Oct 2026 12:33:41 +0000 Subject: [PATCH 2/3] feat(helm): Run OCR service in EE and make OCR tutorial --- docs/mkdocs.yml | 1 + docs/scripts/copy_files_from_repo.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index a28e433e..3fe8fdec 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -35,6 +35,7 @@ nav: - Introduction: index.md - Data Engineering: - Overview: data-engineering/index.md + - OCR Service Tutorial: data-engineering/ocr-service-tutorial.ipynb - Major Changes: data-engineering/changes.md # - Data pipelines: overview/Data pipelines.md - Elasticsearch: overview/Elasticsearch.md diff --git a/docs/scripts/copy_files_from_repo.py b/docs/scripts/copy_files_from_repo.py index bb513c38..5cf76377 100644 --- a/docs/scripts/copy_files_from_repo.py +++ b/docs/scripts/copy_files_from_repo.py @@ -29,6 +29,10 @@ "sourceFilePath": "helm-charts/cogstack-jupyterhub-helm/examples/medcat-opensearch-e2e.ipynb", "outputFilePath": "cogstack-ce/tutorial/medcat-opensearch-e2e.ipynb", }, + { + "sourceFilePath": "helm-charts/cogstack-jupyterhub-helm/examples/ocr-service-tutorial.ipynb", + "outputFilePath": "data-engineering/ocr-service-tutorial.ipynb", + }, ] From e91dc53da78457621b936ed3aa50d0e051b53c10 Mon Sep 17 00:00:00 2001 From: alhendrickson Date: Wed, 21 Oct 2026 12:34:39 +0000 Subject: [PATCH 3/3] feat(helm): Run OCR service in EE and make OCR tutorial --- docs/mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 3fe8fdec..d45cdff5 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -35,12 +35,12 @@ nav: - Introduction: index.md - Data Engineering: - Overview: data-engineering/index.md - - OCR Service Tutorial: data-engineering/ocr-service-tutorial.ipynb - Major Changes: data-engineering/changes.md # - Data pipelines: overview/Data pipelines.md - Elasticsearch: overview/Elasticsearch.md # - NiFi: data-engineering/nifi.md - NiFi: '!import https://github.com/CogStack/CogStack-NiFi?branch=main&docs_dir=docs/*&config=mkdocs.yml' + - OCR Service Tutorial: data-engineering/ocr-service-tutorial.ipynb - NLP: - Overview: cogstack-ai/index.md