Skip to main content

RAG Endpoint

The RAG (Retrieval-Augmented Generation) Endpoint is a flexible and modular system built to enhance LLM responses with relevant context from your own documents. This system provides a standardized API for document ingestion, context retrieval, and augmented generation, with support for temporal-aware search to automatically find the most time-relevant content.

Base URL and Authentication

All requests to the RAG endpoint must include:

  1. Base URL: Your endpoint-specific base URL obtained from the portal

    https://api.example.com/v1/api/{endpoint-id}
  2. Authorization Header: Your API key in the Authorization header

    Authorization: Bearer your-api-key-from-portal

API Reference

Files API

The /files endpoint provides a RESTful interface for managing files in the system.

Upload a File

POST {base_url}/files

Headers:

  • Authorization: Bearer your-api-key
  • Content-Type: multipart/form-data

Parameters:

  • file: File data (required)
  • user_id: ID of the user uploading the file (optional, defaults to "system")
  • document_id: Custom ID for the document (optional, auto-generated if not provided)
  • group_ids: List of group IDs to associate with the file (optional)
  • metadata: JSON string of additional metadata (optional)
  • content_timestamp: ISO 8601 timestamp for when the content was created/effective (optional, defaults to upload time)
  • use_ocr: Enable OCR for PDFs and images when an OCR model is configured (optional)

Example metadata:

{
"source": "email",
"author": "John Doe",
"tags": ["important", "reference"],
"custom_field": "value"
}

Example with custom timestamp:

# Upload a document with its actual creation date
with open("q3_report_2024.pdf", 'rb') as file:
response = requests.post(
f"{api_base_url}/files",
headers={"Authorization": f"Bearer {api_key}"},
files={"file": file},
data={
"content_timestamp": "2024-09-30T23:59:59Z", # Q3 end date
"metadata": json.dumps({"quarter": "Q3", "year": 2024})
}
)

Response:

{
"id": "document_id",
"filename": "example.pdf",
"created_at": 1677409068,
"status": "processed"
}

List Files

GET {base_url}/files

Headers:

  • Authorization: Bearer your-api-key

Query Parameters:

  • user_id: Filter files by user ID (optional)
  • group_id: Filter files by group ID (optional)
  • source_id: Return only the files of this data source. Use null to return only manually uploaded files (optional)
  • limit: How many files to return, from 1 to 500. Omit it to return all matching files (optional)
  • cursor: The next_cursor from the previous response (optional)

Response:

{
"files": [
{
"id": "doc_123",
"user_id": "user_456",
"group_ids": ["group_789", "group_101"],
"metadata": {
"filename": "report.pdf",
"created_at": "2025-02-26T12:32:39.265082",
"source": "upload",
"author": "Jane Smith"
}
}
],
"next_cursor": "eyJmaWx0ZXJzIjp7fSwib3BlcmF0aW9uIjoiZmlsZXMubGlzdCJ9"
}

Files from a data source also include source_id, source_key, and source_version.

Paging: Set limit to enable paging. next_cursor is null on the last page. To get the next page, send it back as cursor and keep the same user_id, group_id, and source_id. An invalid cursor returns 400.

Delete a File

DELETE {base_url}/files/{file_id}

Headers:

  • Authorization: Bearer your-api-key

Parameters:

  • file_id: ID of the file to delete (path parameter)

Response:

{
"success": true,
"message": "File doc_123 deleted successfully",
"files_deleted": ["doc_123"]
}

If the file came from a connected data source, deleting it is temporary. The next sync indexes it again. Remove it from the source store to keep it out of the RAG.

Content API

The /content endpoint allows direct text upload and management.

Upload Content

POST {base_url}/content

Headers:

  • Authorization: Bearer your-api-key
  • Content-Type: application/json

Request Body:

{
"contents": [
{
"id": "doc_2024_q1",
"txt": "Q1 2024 Financial Report...",
"content_timestamp": "2024-03-31T23:59:59Z", // Optional: defaults to upload time
"metadata": {
"type": "financial_report",
"quarter": "Q1"
}
}
],
"user_id": "finance_team",
"group_id": "financial_reports"
}

Important: The content_timestamp field allows you to specify when the content was created or is effective from. This is crucial for temporal search to work correctly when documents have specific dates (e.g., dated reports, versioned documentation).

Update Content

PATCH {base_url}/content/{content_id}

Request Body:

{
"txt": "Updated content...", // Optional
"content_timestamp": "2024-04-01T00:00:00Z", // Optional
"metadata": {
"revised": true
}
}

Context API

The /context endpoint provides a flexible way to retrieve relevant chunks of text from your document repository based on a query.

POST {base_url}/context

Headers:

  • Authorization: Bearer your-api-key
  • Content-Type: application/json

Request Body:

{
"query": "your search query",
"max_chunks": 4,
"filter_ids": ["doc1", "doc2"],
"group_id": "group1",
"user_id": "user123",
"metadata_filters": [
{
"field": "author",
"value": "Alice",
"operator": "eq"
},
{
"field": "rating",
"value": 4.5,
"operator": "gt"
}
],
"smart_temporal_search": true // Optional: override default temporal search setting
}

Temporal Search: When smart_temporal_search is enabled (either by default in portal config or explicitly in the request), the system automatically detects temporal intent in queries like "latest policy", "what changed last month", or "original version" and retrieves the most time-relevant content.

Parameters:

ParameterTypeRequiredDefaultDescription
querystringYes-The search query to find relevant text chunks
max_chunksintegerNo4Maximum number of chunks to return
filter_idsarrayNonullList of document IDs to restrict the search to
group_idstringNonullGroup ID to filter documents by (for permission-based filtering)
user_idstringNonullUser ID to filter documents by (only returns documents owned by this user)
metadata_filtersarrayNonullList of metadata filters to apply
smart_temporal_searchbooleanNoPortal configEnable/disable intelligent temporal search

Metadata Filters

Each metadata filter object supports:

FieldTypeRequiredDescription
fieldstringYesMetadata field path (e.g., "author" or "details.category")
valuescalar or scalar arrayYesScalar to match; flat scalar arrays are supported only by contains
operatorstringNoOperator to use (default: "eq")

Supported operators:

  • eq: Exact equality (default)
  • contains: Case-insensitive substring match for strings, or element match for arrays
  • gt: Greater than
  • gte: Greater than or equal to
  • lt: Less than
  • lte: Less than or equal to

eq compares the stored JSON value exactly, including its type and string case. Send 4.5, not "4.5", to match a JSON number, and send true, not "true", to match a boolean. A lossless exact-number object returned by metadata discovery can also be copied into a numeric equality filter. Use contains rather than eq for a stored array. Only contains accepts a filter value that is an array; it matches when any requested value matches. Scalar string contains uses case-insensitive substring matching, while stored arrays use case-insensitive string membership and typed equality for non-string elements.

Range operators accept finite JSON numbers, canonical numeric strings, strict ISO dates such as 2026-08-17, local ISO times such as 09:30:15.125, and timezone-qualified ISO datetimes such as 2026-08-17T10:30:00Z. Dates represent midnight UTC when compared with datetimes. Time-only values do not accept a timezone because they have no date for a UTC conversion. If a stored field is an array, any compatible scalar element can satisfy a range filter. Incompatible stored types do not match the filter.

All metadata filters in one request use AND: every filter must match before a document is eligible for the existing semantic ranking and reranking.

Limits for one request:

LimitValue
Filters20
Field path200 characters and 5 dot-separated segments using letters, numbers, _, -, or :
Values in one contains array50
Encoded size of one filter value4 KiB
Encoded size of all metadata filters16 KiB

The Context API returns 422 Unprocessable Entity without searching documents when a metadata filter is invalid. Common causes include an unsupported operator, an invalid field path, an array value used with an operator other than contains, an unsupported range value, or a request that exceeds one of these limits. An MCP search_documents call with the same invalid input fails as a tool-call error instead of returning a Context API HTTP status.

Advanced Temporal Parameters (for testing/evaluation):

  • temporal_mode: Manual override for temporal behavior ("earliest", "latest", "specific", "range", "none")
  • temporal_weight: Balance between semantic and temporal relevance (0-1)
  • temporal_value: ISO timestamp or date range for specific/range modes
  • temporal_strictness: How strictly to enforce temporal boundaries (0-1)

Response:

{
"chunks": [
"This is a document about Python programming...",
"Machine learning frameworks in Python include..."
],
"scores": [0.92, 0.87],
"files": [
{
"id": "doc1",
"user_id": "user123",
"group_ids": ["group1", "group2"],
"created_at": 1728987000,
"metadata": {
"filename": "python_intro.pdf",
"author": "Alice",
"topic": "programming",
"tags": ["python", "beginner"],
"rating": 4.5
},
"content_timestamp": "2024-10-15T10:30:00Z",
"top_score": 0.92,
"n_chunks": 1
},
{
"id": "doc2",
"user_id": "user456",
"group_ids": ["group1"],
"created_at": 1728987500,
"metadata": {
"filename": "ml_frameworks.pdf",
"author": "Bob",
"topic": "machine learning",
"tags": ["python", "advanced"],
"rating": 4.8
},
"content_timestamp": "2024-10-15T10:30:00Z",
"top_score": 0.87,
"n_chunks": 1
}
]
}

Response fields:

FieldTypeDescription
chunksarray of stringsThe retrieved text chunks
scoresarray of numbersRelevance score per chunk, same order as chunks
filesarray of objectsSource document for each chunk, same order as chunks (empty when nothing matched)

files holds one entry per returned chunk, not per document: top_score is that chunk's score and n_chunks is always 1, so the same id repeats when several chunks come from one document.

Each files entry contains:

FieldTypeDescription
idstringDocument ID
user_idstringOwner of the document ("system" when uploaded without a user_id)
group_idsarray of stringsGroups the document belongs to
created_atintegerUpload time, Unix seconds
metadataobjectMetadata supplied at upload, plus the filename the service adds
content_timestampstringISO 8601 timestamp for when the content was created/effective
top_scorenumberRelevance score of the chunk this entry belongs to
n_chunksintegerAlways 1

Chat Completions API

Generate completions with context-aware responses using an OpenAI-compatible API.

POST {base_url}/v1/chat/completions

Headers:

  • Authorization: Bearer your-api-key
  • Content-Type: application/json

Request Body:

{
"model": "cm-llm",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What are the key features of the product?"}
],
"temperature": 0.7,
"max_tokens": 500,
"max_chunks": 3,
"enable_query_enhancement": true, // Optional: enhance queries for conversational context (default: true)
"smart_temporal_search": false // Optional: override default temporal search setting
}

Parameters:

ParameterTypeRequiredDefaultDescription
modelstringYes-The model to use (typically "cm-llm")
messagesarrayYes-Array of messages in the conversation
temperaturenumberNo0.7Controls randomness (0-1)
max_tokensintegerNo500Maximum number of tokens to generate
max_chunksintegerNo4Maximum number of context chunks to retrieve
streambooleanNofalseWhether to stream the response
enable_query_enhancementbooleanNotrueEnhance queries using conversation history for better retrieval
smart_temporal_searchbooleanNoPortal configEnable/disable intelligent temporal search

Response (non-streaming):

{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "cm-llm",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Based on the documentation, the key features of the product include..."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 25,
"completion_tokens": 320,
"total_tokens": 345
}
}

Groups API

Manage document groups for organization and access control.

List Groups

GET {base_url}/groups

Headers:

  • Authorization: Bearer your-api-key

Response:

{
"groups": ["group1", "group2", "group3"]
}

Get Group Information

GET {base_url}/groups/{group_id}

Headers:

  • Authorization: Bearer your-api-key

Response:

{
"group_id": "group1",
"document_count": 42,
"created_at": "2023-05-15T12:00:00Z",
"last_updated": "2023-10-28T14:30:22Z"
}

Get Group Documents

GET {base_url}/groups/{group_id}/documents

Headers:

  • Authorization: Bearer your-api-key

Response:

{
"group_id": "group1",
"document_count": 42,
"documents": [
{
"id": "doc1",
"user_id": "user123",
"group_ids": ["group1", "group2"],
"metadata": {
"filename": "example.txt",
"created_at": "2023-10-15T10:30:00Z",
"tags": ["report", "finance"]
}
}
]
}

Storage Usage API

Monitor storage usage across your RAG system for billing and capacity planning.

Get Storage Summary

GET {base_url}/storage/usage/summary

Headers:

  • Authorization: Bearer your-api-key

Response:

{
"total_size_bytes": 1523456789,
"total_size": "1.42 GB",
"document_count": 1250,
"chunk_count": 45678,
"group_count": 15,
"tables_size_bytes": 987654321,
"indexes_size_bytes": 535802468,
"tables_size_percent": 64.8,
"indexes_size_percent": 35.2,
"vector_index_size_percent": 33.1
}

Get Detailed Storage Usage

GET {base_url}/storage/usage?include_groups=true

Query Parameters:

  • include_tables: Include table-by-table breakdown (optional)
  • include_indexes: Include index-by-index breakdown (optional)
  • include_groups: Include group-by-group usage (optional)
  • include_vector: Include vector index details (optional)

Response with groups:

{
"total_size_bytes": 1523456789,
"total_size": "1.42 GB",
"document_count": 1250,
"chunk_count": 45678,
"group_usage": [
{
"group_id": "client-a",
"size_bytes": 534567890,
"content_size_bytes": 356789012,
"estimated_index_size_bytes": 177778878,
"document_count": 456,
"chunk_count": 15678,
"human_readable_size": "509.8 MB"
}
]
}

Get Group Storage Usage

GET {base_url}/storage/usage/{group_id}

Headers:

  • Authorization: Bearer your-api-key

Get detailed storage metrics for a specific group (useful for client billing).

Response:

{
"group_id": "client-a",
"size_bytes": 534567890,
"content_size_bytes": 356789012,
"estimated_index_size_bytes": 177778878,
"document_count": 456,
"chunk_count": 15678,
"text_content_size_bytes": 145678901,
"embeddings_size_bytes": 201234567,
"metadata_size_bytes": 9875544,
"human_readable": {
"size": "509.8 MB",
"content_size": "340.3 MB",
"index_size": "169.5 MB"
}
}

For billing purposes:

  • size_bytes: Total storage including indexes (recommended for billing)
  • content_size_bytes: Content only, excluding index overhead
  • human_readable: Pre-formatted values for display

Data Sources API

A data source connects a RAG to an external store, such as an S3 bucket or an Azure Blob Storage container. The RAG reads the files in the store, indexes them, and reads the store again later to pick up changes. The /data-sources calls let you follow that work and start it by hand.

Data sources are configured through the portal; the source_id in these paths is the UUID assigned when the source is connected to the RAG.

These calls need an API key with write on the RAG. The connection test and removal of a deleted source's files are exceptions — they need RAG admin rights.

If a RAG could not start its data source runtime, every call here answers 503 with the code runtime_unavailable.

How often a source syncs

A data source with a sync interval syncs by itself. When the time in next_sync_at passes, the RAG reads the source again and picks up files that were added, changed, or removed since the last pass. New files are searchable once their status is indexed.

A source with no interval (sync_interval_seconds is null) syncs only when you ask for it.

If a sync fails, the RAG does not try again straight away. It waits for the next scheduled time. To retry sooner, start a sync yourself.

List the status of every data source

Deleted sources remain listed while they have retained files.

GET {base_url}/data-sources/status

Headers:

  • Authorization: Bearer your-api-key

Query Parameters:

  • source_id: Return only the listed source IDs (optional, repeatable)

Response:

{
"sources": [
{
"source_id": "9f3c1a20-2a1e-4b7f-9e2d-6c1b0d4a55f1",
"lifecycle": "active",
"sync_status": "succeeded",
"sync_interval_seconds": 3600,
"next_sync_at": "2025-10-01T09:15:00Z",
"last_sync_requested_at": "2025-10-01T08:15:00Z",
"last_sync_started_at": "2025-10-01T08:15:02Z",
"last_sync_completed_at": "2025-10-01T08:17:41Z",
"current_generation": 4,
"last_completed_generation": 4,
"scan_complete": true,
"total_files": 128,
"pending_files": 0,
"indexed_files": 126,
"failed_files": 2,
"latest_error": null
}
]
}

Response fields:

FieldTypeDescription
source_idstringID of the data source
lifecyclestringactive while the source is connected to the RAG, deleted once it is removed
sync_statusstringnever_synced, queued, syncing, succeeded, succeeded_with_errors, failed, or cancelled
sync_interval_secondsnumberHow often the source syncs on its own. null when it syncs only when you ask
next_sync_atstringWhen the next scheduled sync is due
last_sync_requested_atstringWhen a sync was last asked for
last_sync_started_atstringWhen the last sync began
last_sync_completed_atstringWhen the last sync ended
current_generationintegerCounts the passes the RAG has made over the source
last_completed_generationintegerThe last pass that finished
scan_completebooleantrue once the source has finished at least one full pass over its files
total_filesintegerFiles the RAG has seen in the source
pending_filesintegerFiles still waiting to be indexed
indexed_filesintegerFiles indexed and searchable
failed_filesintegerFiles the RAG could not index
latest_errorobjectcode and message of the last failure, or null

Start a sync now

POST {base_url}/data-sources/{source_id}/sync

Headers:

  • Authorization: Bearer your-api-key

Asks the RAG to read the source now. The call returns as soon as the request is accepted; it does not wait for the sync to finish. Watch the source's status to see when it is done.

Response:

{
"source_id": "9f3c1a20-2a1e-4b7f-9e2d-6c1b0d4a55f1",
"sync_status": "queued"
}

sync_status is syncing when a sync is already running. In that case the RAG finishes the current pass and then makes one more, so nothing is missed. Asking several times in a row has the same effect as asking once.

If the RAG has no active source with that ID, the answer is 404 with the code data_source_not_found.

List the files of a data source

GET {base_url}/data-sources/{source_id}/files

Headers:

  • Authorization: Bearer your-api-key

Query Parameters:

  • limit: How many files to return, from 1 to 200 (optional, default 100)
  • status: Return only files with this status — pending, indexed, or failed (optional)
  • cursor: The next_cursor from the previous answer, to get the next page (optional)

Response:

{
"files": [
{
"source_id": "9f3c1a20-2a1e-4b7f-9e2d-6c1b0d4a55f1",
"source_key": "reports/q3-2025.pdf",
"source_version": "d41d8cd98f00b204e9800998ecf8427e",
"source_modified_at": "2025-09-30T23:59:59Z",
"desired_operation": "upsert",
"status": "indexed",
"document_id": "doc_123",
"error": null,
"updated_at": "2025-10-01T08:17:12Z"
}
],
"next_cursor": "eyJzb3VyY2VfaWQiOiI5ZjNjMWEyMCJ9"
}

Response fields:

FieldTypeDescription
source_keystringWhere the file sits in the store, for example the object key in a bucket
source_versionstringThe version the store reports for the file, or null
source_modified_atstringWhen the store last changed the file, or null
desired_operationstringupsert when the file should be indexed, delete when it should be removed from the RAG
statusstringpending, indexed, or failed
document_idstringID of the document in the RAG, once the file is indexed. Use it with the Files API
errorobjectcode and message of the last failure for this file, or null
updated_atstringWhen this entry last changed

Paging: next_cursor is null on the last page. To get the next page, send the value back as cursor and keep the same status — a cursor only works with the filter it was made with. A cursor the RAG cannot use gets 400 with the code invalid_cursor.

Remove the files of a deleted data source

DELETE {base_url}/data-sources/{source_id}/files

Headers:

  • Authorization: Bearer your-api-key

Removes documents left in the RAG after a data source is disconnected. The source must already be deleted; an active source returns 409.

Query Parameters:

  • limit: How many documents to remove in this call, from 1 to 500 (optional, default 100)

Response:

{
"deleted_count": 100,
"has_more": true
}

Call the endpoint again while has_more is true. deleted_count can be 0 while the RAG clears the remaining inventory records. If the source is already absent locally, the response has deleted_count 0 and has_more false.

Test a connection

POST {base_url}/data-sources/test

Headers:

  • Authorization: Bearer your-api-key
  • Content-Type: application/json

Checks that the RAG can reach a store with the settings you give it. Nothing is saved and nothing is indexed. You need admin rights on the RAG; write is not enough.

Request Body:

{
"type": "s3",
"config": {
"endpoint": "https://s3.example.com",
"bucket": "reports",
"prefix": "2025/",
"region": "eu-north-1"
},
"credentials": {
"access_key_id": "your-access-key-id",
"secret_access_key": "your-secret-access-key"
}
}

For an Azure Blob Storage container, send:

{
"type": "azure_blob",
"config": {
"account_endpoint": "https://youraccount.blob.core.windows.net",
"container": "reports",
"prefix": "2025/"
},
"credentials": {
"connection_string": "your-storage-account-connection-string"
}
}
FieldDescription
account_endpointThe blob endpoint of the storage account, for example https://youraccount.blob.core.windows.net
containerName of the container to read. Lower case, 1 to 63 characters
prefixRead only the files whose name starts with this text. Send "" to read the whole container
connection_stringConnection string of the storage account

Send every field. Leaving one out, or adding one that is not listed, gets 422 with the code invalid_configuration.

account_endpoint must be the blob endpoint of the same storage account as the connection_string. If the two do not match, the RAG does not reach the store and answers 503 with the code unavailable.

Response:

{
"object_read_verified": true
}

A 200 answer means the RAG reached the store and listed it. object_read_verified is true when the RAG also read one file. It is false when the store held no file to read — for example an empty bucket, or a prefix that matches nothing.

Errors:

StatusCodeMeaning
422invalid_configurationThe settings or the credentials cannot be used
503timeoutThe store did not answer in time
503unavailableThe RAG could not reach the store

Error answers never repeat the credentials you sent.

Temporal Search Configuration

Setting Timestamps

For temporal search to work effectively, ensure your documents have accurate timestamps:

# When uploading files
with open("annual_report_2023.pdf", 'rb') as file:
response = requests.post(
f"{api_base_url}/files",
headers=headers,
files={"file": file},
data={
"content_timestamp": "2023-12-31T23:59:59Z" # End of 2023
}
)

# When uploading content
response = requests.post(
f"{api_base_url}/content",
headers=headers,
json={
"contents": [{
"id": "policy_v3",
"txt": "Updated policy effective March 2024...",
"content_timestamp": "2024-03-01T00:00:00Z" # Effective date
}]
}
)

Note: If you don't specify content_timestamp, the system uses the upload time, which is often correct for real-time content like news updates or live documentation.

Configuring Default Behavior

Set the default temporal search behavior in your portal configuration:

{
"smart_temporal_search_default": true // or false
}

When enabled by default, queries like "latest updates", "current policy", or "what changed recently" will automatically use temporal-aware retrieval.

Overriding the Default

Override the portal configuration on a per-request basis:

# Disable temporal search for this specific request
response = requests.post(
f"{api_base_url}/context",
headers=headers,
json={
"query": "python programming basics",
"smart_temporal_search": false # Override portal default
}
)

# Enable temporal search for this specific request
response = requests.post(
f"{api_base_url}/v1/chat/completions",
headers=headers,
json={
"model": "cm-llm",
"messages": [{"role": "user", "content": "What's the latest company policy?"}],
"smart_temporal_search": true # Override portal default
}
)

Code Examples

Upload Files

from pathlib import Path
import requests

# Configuration from portal
api_base_url = "https://api.example.com/v1/api/your-endpoint-id"
api_key = "your-api-key-from-portal"

# Headers with authorization
headers = {
"Authorization": f"Bearer {api_key}"
}

file_path = Path("/path/to/your/document.txt")

# Upload a single file
with open(file_path, 'rb') as file:
response = requests.post(
f"{api_base_url}/files",
headers=headers,
files={"file": (file_path.name, file, "application/octet-stream")}
)

file_id = response.json()["id"]
print(f"File uploaded with ID: {file_id}")

Get Context

import requests

# Configuration from portal
api_base_url = "https://api.example.com/v1/api/your-endpoint-id"
api_key = "your-api-key-from-portal"

# Headers with authorization
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}

payload = {
"query": "What are the key features of the product?",
"max_chunks": 3,
"user_id": "user123" # Filter to only documents owned by this user
}

response = requests.post(
f"{api_base_url}/context",
headers=headers,
json=payload
)
result = response.json()

for i, (chunk, score) in enumerate(zip(result["chunks"], result["scores"])):
print(f"Chunk {i + 1} (score: {score:.2f}): {chunk[:100]}...")

Generate Chat Completions

import requests

# Configuration from portal
api_base_url = "https://api.example.com/v1/api/your-endpoint-id"
api_key = "your-api-key-from-portal"

# Headers with authorization
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}

payload = {
"model": "cm-llm",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What are the key features of the product?"}
],
"temperature": 0.7,
"max_tokens": 500,
"max_chunks": 3
}

response = requests.post(
f"{api_base_url}/v1/chat/completions",
headers=headers,
json=payload
)
result = response.json()

print("Assistant response:", result["choices"][0]["message"]["content"])

Streaming Example

import requests
import json

# Configuration from portal
api_base_url = "https://api.example.com/v1/api/your-endpoint-id"
api_key = "your-api-key-from-portal"

# Headers with authorization
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}

payload = {
"model": "cm-llm",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What are the key features of the product?"}
],
"temperature": 0.7,
"stream": True,
"max_chunks": 3
}

response = requests.post(
f"{api_base_url}/v1/chat/completions",
headers=headers,
json=payload,
stream=True
)

for line in response.iter_lines():
if line:
line = line.decode('utf-8')
if line.startswith('data: '):
chunk_data = line[6:] # Remove 'data: ' prefix
if chunk_data != '[DONE]':
chunk = json.loads(chunk_data)
content = chunk['choices'][0]['delta'].get('content', '')
if content:
print(content, end='', flush=True)

OpenAI SDK Integration

from openai import OpenAI

# Configuration from portal
api_base_url = "https://api.example.com/v1/api/your-endpoint-id"
api_key = "your-api-key-from-portal"

# Initialize client with base URL and API key
client = OpenAI(
base_url=f"{api_base_url}/v1/", # Note the addition of "/v1/"
api_key=api_key
)

# Make a request
response = client.chat.completions.create(
model="cm-llm",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What are the key features of the product?"}
],
temperature=0.7
)

print(response.choices[0].message.content)

Context-Aware Response Behavior

The chat completions endpoint is designed to provide accurate, context-grounded responses with clear attribution.

Context Attribution

The default system prompt instructs the LLM to:

  • Clearly indicate when using retrieved context vs. general knowledge
  • Decline to answer when context is insufficient for specific questions
  • Continue handling simple conversational questions naturally
  • Cite the context when using it in responses

Example Response:

"According to the documentation provided, the Enterprise plan is $299/month. However, I don't see information about custom pricing options in the retrieved context. You may want to contact sales for custom arrangements."

This behavior ensures users can trust the information and understand when the AI is working from your documents versus general knowledge.

Conversational Query Enhancement

The chat completions endpoint includes automatic query enhancement for multi-turn conversations (enabled by default). This feature improves retrieval accuracy for follow-up questions and conversational queries.

How It Works

When enable_query_enhancement is enabled (default):

  1. The system analyzes the full conversation history
  2. Generates a standalone, context-aware query for retrieval
  3. Uses the enhanced query to find relevant document chunks
  4. Sends the original conversation to the LLM for the final response

Example:

# First message
{"role": "user", "content": "Tell me about your enterprise features"}
# System retrieves: Documents about enterprise features

# Follow-up message (without enhancement)
{"role": "user", "content": "What about the pricing?"}
# Would retrieve: Generic pricing info (missing context)

# Follow-up message (with enhancement - default)
{"role": "user", "content": "What about the pricing?"}
# Enhanced query: "What is the pricing for the enterprise features?"
# Retrieves: Specific enterprise pricing information ✓

When to Disable

Set enable_query_enhancement: false when:

  • Using custom query preprocessing in your application
  • Testing or debugging retrieval with exact user queries
  • You have specific requirements for query formulation

Note: The system automatically skips enhancement for single-message conversations, so there's no need to disable it for standalone queries.

When to use /context vs /v1/chat/completions

  • Use /context when you:

    • Need to retrieve relevant passages without generating LLM responses
    • Want to implement custom processing on the retrieved context
    • Are building a search interface that shows document snippets
    • Need to debug or evaluate the retrieval component separately
  • Use /v1/chat/completions when you:

    • Need complete LLM responses that incorporate the retrieved context
    • Want a drop-in replacement for OpenAI's chat API with RAG capabilities
    • Need to maintain conversational context with retrieved information
    • Want automatic query enhancement for multi-turn conversations

Best Practices

  • For optimal results, use clear and specific questions that match how information is presented in your documentation
  • Use the max_chunks parameter to control how much context is retrieved (3-5 is usually optimal)
  • Apply metadata filters to narrow down results when you have a large document repository
  • For categorized documents, use the group_id parameter to search within specific document groups
  • Use the user_id parameter when you need to restrict context retrieval to documents owned by a specific user
  • Consider using lower temperature (0.0-0.3) for more factual responses based on your documentation
  • For temporal search: Ensure documents have accurate content_timestamp values when the effective date differs from upload time