Skip to main content

Documentation Index

Fetch the complete documentation index at: https://morphik.ai/docs/llms.txt

Use this file to discover all available pages before exploring further.

This cookbook demonstrates how to retrieve document chunks from Morphik and send them to OpenAI for completion generation, using both presigned URLs and base64-encoded images.
Prerequisites
  • Install the Morphik SDK: pip install morphik
  • Install OpenAI SDK: pip install openai
  • Provide credentials via MORPHIK_URI and OPENAI_API_KEY
  • Documents ingested with multimodal support (use_colpali=True)

1. Ingest Documents with Multimodal Support

First, ingest your documents with multimodal retrieval enabled:
from datetime import date
from morphik import Morphik
from openai import OpenAI

# Initialize clients
morphik_client = Morphik("morphik://your-app:token@api.morphik.ai")
openai_client = OpenAI(api_key="your-openai-key")

# Ingest PDF with multimodal support
doc = morphik_client.ingest_file(
    file="morphik_platform_brief.pdf",
    metadata={
        "collection": "demo-ai-briefs",
        "published_date": date(2024, 9, 14),
        "priority_score": 42,
        "requires_followup": True,
        "tags": ["morphik", "roadmap"],
    },
    use_colpali=True,  # Enable multimodal retrieval
)

# Wait for processing
doc.wait_for_completion(timeout_seconds=240)
print(f"Ingested: {doc.external_id}")

2. Retrieve Chunks as Presigned URLs

Get chunks as URLs that can be sent directly to vision models:
# Retrieve chunks as URLs
url_chunks = morphik_client.retrieve_chunks(
    query="List notable roadmap items and vision experiments",
    filters={"collection": "demo-ai-briefs"},
    k=4,
    padding=1,
    use_colpali=True,      # Must match ingestion setting
    output_format="url",   # Get presigned URLs
)

# Extract URLs from chunks
urls = []
for chunk in url_chunks:
    if isinstance(chunk.content, str) and chunk.content.startswith("http"):
        urls.append(chunk.content)
    elif chunk.download_url:
        urls.append(chunk.download_url)

print(f"Found {len(urls)} image URLs")

3. Send URLs to OpenAI

Send the presigned URLs to OpenAI’s vision model:
QUESTION = "List notable roadmap items, vision experiments, and follow-up actions"

# Build content with text and image URLs
content = [{"type": "input_text", "text": QUESTION}]
for url in urls:
    content.append({"type": "input_image", "image_url": url})

# Call OpenAI Responses API
response = openai_client.responses.create(
    model="gpt-5.1",
    input=[{"role": "user", "content": content}],
)

print(response.output_text)

4. Retrieve Chunks as Base64 Images

For cases where you need base64-encoded images:
import base64
from io import BytesIO
from PIL.Image import Image as PILImage

# Retrieve chunks as PIL Images (default format)
base64_chunks = morphik_client.retrieve_chunks(
    query=QUESTION,
    filters={"collection": "demo-ai-briefs"},
    k=4,
    padding=1,
    use_colpali=True,
    output_format=None,  # Default: returns PIL Images
)

# Convert PIL Images to base64 data URIs
def encode_chunk_image(chunk) -> str:
    if not isinstance(chunk.content, PILImage):
        return None

    # Always use image/png for PIL Images
    buffer = BytesIO()
    chunk.content.save(buffer, format="PNG")
    encoded = base64.b64encode(buffer.getvalue()).decode("utf-8")
    return f"data:image/png;base64,{encoded}"

data_uris = [encode_chunk_image(chunk) for chunk in base64_chunks]
data_uris = [uri for uri in data_uris if uri]  # Filter out None

print(f"Found {len(data_uris)} base64 images")

5. Send Base64 Images to OpenAI

# Build content with text and base64 images
content = [{"type": "input_text", "text": QUESTION}]
for data_uri in data_uris:
    content.append({"type": "input_image", "image_url": data_uri})

# Call OpenAI
response = openai_client.responses.create(
    model="gpt-5.1",
    input=[{"role": "user", "content": content}],
)

print(response.output_text)

Important Notes

Multimodal Ingestion and Retrieval

When you ingest with use_colpali=True, you must retrieve with use_colpali=True:
# ✅ Correct
doc = client.ingest_file(file="doc.pdf", use_colpali=True)
chunks = client.retrieve_chunks(query="...", use_colpali=True)

# ❌ Wrong - Mismatch will return 0 results
doc = client.ingest_file(file="doc.pdf", use_colpali=True)
chunks = client.retrieve_chunks(query="...", use_colpali=False)

Content Type Handling

Chunks from PDFs ingested with multimodal support will have:
  • chunk.content_type = "application/pdf" (original document type)
  • chunk.content = PIL Image object (actual content)
When encoding to base64, always use "image/png" as the MIME type:
# ✅ Correct: Use image/png for PIL Images
data_uri = f"data:image/png;base64,{encoded}"

# ❌ Wrong: Don't use chunk.content_type (would be "application/pdf")
data_uri = f"data:{chunk.content_type};base64,{encoded}"

Output Format Comparison

FormatWhen to UseProsCons
output_format="url"Production, large imagesNo encoding overhead, fasterURLs expire after some time
output_format=None (base64)Small images, offline processingAlways availableLarger payload size

Use Cases

This pattern is ideal for:
  • Document Q&A over visual documents (PDFs, scans, diagrams)
  • Report generation from technical documentation with charts and tables
  • Visual data analysis combining text and image understanding
  • Multi-document synthesis aggregating information across documents
  • Chart and diagram interpretation using vision-capable models
  • Technical specification review analyzing mixed text-visual content

Best Practices

1. Choose the Right Output Format

Use URLs for production workloads with large images:
# Production: Use URLs
chunks = client.retrieve_chunks(..., output_format="url")
Use base64 for small images or offline processing:
# Small images or offline: Use base64
chunks = client.retrieve_chunks(..., output_format=None)

2. Handle Chunk Padding

Use padding to include adjacent chunks/pages for better context:
chunks = client.retrieve_chunks(
    query="...",
    k=4,
    padding=1,  # Include 1 adjacent chunk on each side
    use_colpali=True,
)

3. Filter with Metadata

Combine retrieval with metadata filtering for precise results:
chunks = client.retrieve_chunks(
    query="roadmap items",
    filters={
        "$and": [
            {"collection": {"$eq": "technical-docs"}},
            {"published_date": {"$gte": "2024-01-01"}},
            {"priority_score": {"$gte": 40}},
        ]
    },
    k=4,
    use_colpali=True,
)

Running the Example

# Set environment variables
export MORPHIK_URI="morphik://your-app:your-token@api.morphik.ai"
export OPENAI_API_KEY="sk-..."

# Run your Python script with the code above
python your_script.py