Search Documents with Vector Stores and OCI Generative AI API Keys

Create a vector store, upload and ingest a document, and search its contents with curl or the OpenAI Python SDK using an OCI Generative AI API key.

Overview

A vector store makes your documents searchable by meaning. You upload a file, ingest it into the store, and search with a natural-language question. The service returns relevant passages, filenames, and relevance scores. Retrieved passages can also provide context for a language model application.

Use vector stores to search policies, product guides, and support articles without building your own document parsing and embedding pipeline. For an introduction, see Vector Stores and Semantic Stores.

In this tutorial, you:

  • Create, retrieve, and list vector stores.
  • Upload a sample document and ingest it into searchable chunks.
  • Search the document and inspect matching passages.
  • Rename the store and delete the test resources.

Each numbered task provides a curl example and a Python (OpenAI SDK) example. Choose one path and follow it from beginning to end. The curl examples make HTTP requests from Bash. The Python examples use the OpenAI SDK configured to call OCI. Both paths authenticate with an OCI Generative AI API key and don't require an OCI CLI session.

Before You Begin

To complete this tutorial, you need:

The examples use this OCI base URL:

https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/20231130/openai/v1

Use the service endpoint for your selected region. Keep the same endpoint, compartment, and project throughout the tutorial.

The sample document contains a fictional shop policy. Replace only the connection placeholders with your own values. The examples prompt for the API key without displaying it or including it in source code.

Set Up the curl Path

In a Bash terminal, replace the compartment and project placeholders, then run:

set -o pipefail
export GENAI_BASE_URL='https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/20231130/openai/v1'
export GENAI_COMPARTMENT_ID='<your-compartment-ocid>'
export GENAI_PROJECT_ID='<your-project-ocid>'

read -r -s -p 'OCI Generative AI API key: ' GENAI_API_KEY
printf '\n'
export GENAI_API_KEY

VECTOR_STORES_URL="$GENAI_BASE_URL/vector_stores"
FILES_URL="$GENAI_BASE_URL/files"

Run each curl example in this terminal so that the settings and returned resource IDs remain available. Requests send the bearer API key and opc-compartment-id. File uploads and file deletion also send OpenAI-Project to identify your OCI project. Set the base URL without a trailing slash.

If a command fails, resolve the error before continuing. Keep the IDs returned by successful requests for cleanup. Don't rerun a successful create or upload command just to retrieve a resource's status.

Set Up the Python Path

Create and activate a virtual environment, then install the OpenAI Python SDK:

python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade openai

Create vector_store_tutorial.py with the following setup. Replace the compartment and project placeholders. Then append the Python block from each of the eight numbered tasks in order. All blocks share the same client and resource variables.

import getpass
import time

from openai import OpenAI

BASE_URL = "https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/20231130/openai/v1"
COMPARTMENT_ID = "<your-compartment-ocid>"
PROJECT_ID = "<your-project-ocid>"
API_KEY = getpass.getpass("OCI Generative AI API key: ")

client = OpenAI(
    api_key=API_KEY,
    base_url=BASE_URL.rstrip("/") + "/",
    default_headers={"opc-compartment-id": COMPARTMENT_ID},
    timeout=30,
)

After saving the sample document and assembling all the Python blocks, run the completed script from the same directory:

python vector_store_tutorial.py

The completed script creates resources, searches the document, and deletes its store and uploaded file. To try more questions before cleanup, add search calls after task 6 and before the deletion block. If the script stops on an error, use the resource IDs printed before the error to inspect and clean up the resources already created.

Prepare a Sample Document

Save this fictional policy as sample-document.txt in the directory where you run the curl commands or Python script:

Example shop policy

Returns
Customers can return unused purchases within 30 days of delivery.
Include the order number and keep the item in its original packaging.
Contact support before shipping a return.
Refunds go to the original payment method.

Delivery
Standard delivery usually takes 3 to 5 business days.
Express delivery usually takes 1 to 2 business days.

1. Create a Vector Store

Create an empty store with a recognizable name. The example sets an expiration policy of one day based on last_active_at. Creating the store doesn't upload or ingest a document.

Run the example for your chosen path.

curl

STORE_RESPONSE=$(curl --fail-with-body --silent --show-error \
  --request POST "$VECTOR_STORES_URL" \
  --header "Authorization: Bearer $GENAI_API_KEY" \
  --header "opc-compartment-id: $GENAI_COMPARTMENT_ID" \
  --header 'Content-Type: application/json' \
  --data '{"name":"document-search-demo","expires_after":{"anchor":"last_active_at","days":1}}')

printf '%s\n' "$STORE_RESPONSE" | jq '{id, name, status}'
VECTOR_STORE_ID=$(printf '%s' "$STORE_RESPONSE" | jq -er '.id')

Python (OpenAI SDK)

store = client.vector_stores.create(
    name="document-search-demo",
    expires_after={"anchor": "last_active_at", "days": 1},
)
print("Created store:", store.id, store.status)

Save the returned store ID. Creation can be asynchronous. Check its status in the next task.

2. Retrieve the Store and Check Its Status

Retrieve the store's metadata and wait for completed before ingesting a document.

Run the example for your chosen path.

curl

curl --fail-with-body --silent --show-error \
  "$VECTOR_STORES_URL/$VECTOR_STORE_ID" \
  --header "Authorization: Bearer $GENAI_API_KEY" \
  --header "opc-compartment-id: $GENAI_COMPARTMENT_ID" \
  | jq '{id, name, status}'

If the status is in_progress, wait two seconds and run the retrieve command again. Continue only when the status is completed. Stop and investigate if the store expires or remains in progress for more than two minutes.

Python (OpenAI SDK)

store = client.vector_stores.retrieve(store.id)
deadline = time.monotonic() + 120
while store.status == "in_progress":
    if time.monotonic() >= deadline:
        raise TimeoutError(f"Store creation is still in progress: {store.id}")
    time.sleep(2)
    store = client.vector_stores.retrieve(store.id)

if store.status != "completed":
    raise RuntimeError(f"Store is not ready: {store.id}, {store.status}")
print("Store is ready:", store.id)

The two-minute polling timeout is a limit for this example, not a service completion guarantee. If it is reached, retain the store ID and inspect the store before retrying.

The store is ready for ingestion.

3. List Vector Stores

List stores to find resources you have already created. This example displays the first page with up to ten entries. A store might be on a later page. Retrieve by ID to check a specific store.

Run the example for your chosen path.

curl

curl --fail-with-body --silent --show-error \
  "$VECTOR_STORES_URL?limit=10" \
  --header "Authorization: Bearer $GENAI_API_KEY" \
  --header "opc-compartment-id: $GENAI_COMPARTMENT_ID" \
  | jq '.data[] | {id, name, status}'

Python (OpenAI SDK)

for item in client.vector_stores.list(limit=10).data:
    print(item.id, item.name, item.status)

The output shows each returned store's ID, name, and status.

4. Upload a Document

Upload the sample document to create a file resource. The OpenAI-Project header associates the upload with your OCI Generative AI project. The file becomes searchable after ingestion in the next task.

Run the example for your chosen path.

curl

FILE_RESPONSE=$(curl --fail-with-body --silent --show-error \
  --request POST "$FILES_URL" \
  --header "Authorization: Bearer $GENAI_API_KEY" \
  --header "opc-compartment-id: $GENAI_COMPARTMENT_ID" \
  --header "OpenAI-Project: $GENAI_PROJECT_ID" \
  --form 'purpose=assistants' \
  --form 'file=@sample-document.txt')

printf '%s\n' "$FILE_RESPONSE" | jq '{id, filename}'
FILE_ID=$(printf '%s' "$FILE_RESPONSE" | jq -er '.id')

Python (OpenAI SDK)

with open("sample-document.txt", "rb") as document:
    uploaded_file = client.files.create(
        file=document,
        purpose="assistants",
        extra_headers={"OpenAI-Project": PROJECT_ID},
    )
print("Uploaded file:", uploaded_file.id, uploaded_file.filename)

For curl, let the command set the multipart content type and boundary automatically.

Save the returned file ID for ingestion and cleanup.

5. Ingest the Document into the Store

Ingestion associates the file with the store, splits the document into chunks, and creates embeddings for retrieval. A file batch can contain several files. This example uses one file and the default chunking settings.

Run the example for your chosen path.

curl

BATCH_RESPONSE=$(curl --fail-with-body --silent --show-error \
  --request POST "$VECTOR_STORES_URL/$VECTOR_STORE_ID/file_batches" \
  --header "Authorization: Bearer $GENAI_API_KEY" \
  --header "opc-compartment-id: $GENAI_COMPARTMENT_ID" \
  --header 'Content-Type: application/json' \
  --data "$(jq -n --arg file_id "$FILE_ID" '{file_ids:[$file_id]}')")

printf '%s\n' "$BATCH_RESPONSE" | jq '{id, status, file_counts}'
BATCH_ID=$(printf '%s' "$BATCH_RESPONSE" | jq -er '.id')

Check the batch status:

curl --fail-with-body --silent --show-error \
  "$VECTOR_STORES_URL/$VECTOR_STORE_ID/file_batches/$BATCH_ID" \
  --header "Authorization: Bearer $GENAI_API_KEY" \
  --header "opc-compartment-id: $GENAI_COMPARTMENT_ID" \
  | jq '{id, status, file_counts}'

If the status is in_progress, wait two seconds and retrieve the batch again. Continue when the status is completed, the completed file count is one, and the failed and cancelled counts are zero. Stop and investigate if ingestion fails, is cancelled, or remains in progress for more than two minutes.

Python (OpenAI SDK)

batch = client.vector_stores.file_batches.create(
    vector_store_id=store.id,
    file_ids=[uploaded_file.id],
)
print("Ingestion batch:", batch.id)

deadline = time.monotonic() + 120
while batch.status == "in_progress":
    if time.monotonic() >= deadline:
        raise TimeoutError(f"Document ingestion is still in progress: {batch.id}")
    time.sleep(2)
    batch = client.vector_stores.file_batches.retrieve(
        batch.id, vector_store_id=store.id,
    )

counts = batch.file_counts
if (
    batch.status != "completed"
    or counts.completed != 1
    or counts.failed
    or counts.cancelled
):
    raise RuntimeError(
        f"Ingestion did not succeed: {batch.id}, {batch.status}, {counts}"
    )
print("Document is ready to search.")

The document is ready to search after successful ingestion. See Vector Store File Batches API for more information.

6. Search the Document

Ask a natural-language question to retrieve relevant passages. The search call returns document content. It doesn't generate a language-model answer.

Run the example for your chosen path.

curl

Python (OpenAI SDK)

Look for a passage containing the sample policy's 30-day return period. Passage boundaries, ordering, and scores can vary. max_num_results limits matching chunks, not documents.

Try another question, such as How fast is express delivery? or What should I include with a return? Repeat only the search call while the store and file still exist.

7. Rename the Store

Update the store's name. Renaming changes the store's metadata and leaves the uploaded policy text unchanged.

Run the example for your chosen path.

curl

curl --fail-with-body --silent --show-error \
  --request POST "$VECTOR_STORES_URL/$VECTOR_STORE_ID" \
  --header "Authorization: Bearer $GENAI_API_KEY" \
  --header "opc-compartment-id: $GENAI_COMPARTMENT_ID" \
  --header 'Content-Type: application/json' \
  --data '{"name":"document-search-demo-updated"}' \
  | jq '{id, name, status}'

If the status is in_progress, use the retrieve command from task 2 until the status is completed. Stop and investigate if the update doesn't complete within the example's two-minute polling period.

Python (OpenAI SDK)

store = client.vector_stores.update(
    store.id, name="document-search-demo-updated",
)
deadline = time.monotonic() + 120
while store.status == "in_progress":
    if time.monotonic() >= deadline:
        raise TimeoutError(f"Store update is still in progress: {store.id}")
    time.sleep(2)
    store = client.vector_stores.retrieve(store.id)

if store.status != "completed":
    raise RuntimeError(f"Store update did not complete: {store.id}, {store.status}")
print("Updated store:", store.id, store.name)

The store has the updated name. Wait for the update to complete before deleting it.

8. Delete the Test Resources

Delete the store and the uploaded file created in this tutorial. They are separate resources. Deleting the store doesn't replace deleting the file. Use only the IDs of the test resources you created.

If an earlier task failed, use the IDs printed by the successful tasks to clean up only resources that exist. Keep the same endpoint, compartment, project, and API key settings. For Python, run the setup block with the deletion block and replace store.id and uploaded_file.id with the saved IDs as quoted strings. Omit the deletion for any resource that wasn't created.

Run the example for your chosen path.

curl

curl --fail-with-body --silent --show-error \
  --request DELETE "$VECTOR_STORES_URL/$VECTOR_STORE_ID" \
  --header "Authorization: Bearer $GENAI_API_KEY" \
  --header "opc-compartment-id: $GENAI_COMPARTMENT_ID" \
  | jq '{id, deleted}'

curl --fail-with-body --silent --show-error \
  --request DELETE "$FILES_URL/$FILE_ID" \
  --header "Authorization: Bearer $GENAI_API_KEY" \
  --header "opc-compartment-id: $GENAI_COMPARTMENT_ID" \
  --header "OpenAI-Project: $GENAI_PROJECT_ID" \
  | jq '{id, deleted}'

Python (OpenAI SDK)

deleted_store = client.vector_stores.delete(store.id)
print("Store deleted:", deleted_store.id, deleted_store.deleted)

deleted_file = client.files.delete(
    uploaded_file.id,
    extra_headers={"OpenAI-Project": PROJECT_ID},
)
print("File deleted:", deleted_file.id, deleted_file.deleted)
client.close()

A successful deletion acknowledgement contains deleted: true. Confirm this value for both resources.

Troubleshooting

SymptomWhat to check
Authentication error or HTTP 401Confirm that the OCI Generative AI API key secret is valid for the selected region and is sent as a bearer token.
Authorization error or HTTP 403Check API key permissions and File and Vector Store permissions for the compartment and project.
Resource not foundVerify the service URL, region, compartment, project header for file requests, and resource ID.
Upload rejectedCheck the file type and upload limits in the Files API topic. Start with the small sample document.
Ingestion failedRetrieve the batch and inspect its status and file counts. Retrieve individual vector store file records for error details.
No search resultsConfirm that ingestion completed successfully and that you are searching the store containing the document.
Polling timeoutRetrieve the resource again to inspect its current status. Retain its ID for troubleshooting and cleanup.
The SDK has no vector_stores or search methodActivate the Python environment used by the script and upgrade the openai package.