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:
-
Base URL: Your endpoint-specific base URL obtained from the portal
https://api.example.com/v1/api/{endpoint-id} -
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-keyContent-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. Usenullto 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: Thenext_cursorfrom 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-keyContent-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-keyContent-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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
query | string | Yes | - | The search query to find relevant text chunks |
max_chunks | integer | No | 4 | Maximum number of chunks to return |
filter_ids | array | No | null | List of document IDs to restrict the search to |
group_id | string | No | null | Group ID to filter documents by (for permission-based filtering) |
user_id | string | No | null | User ID to filter documents by (only returns documents owned by this user) |
metadata_filters | array | No | null | List of metadata filters to apply |
smart_temporal_search | boolean | No | Portal config | Enable/disable intelligent temporal search |
Metadata Filters
Each metadata filter object supports:
| Field | Type | Required | Description |
|---|---|---|---|
field | string | Yes | Metadata field path (e.g., "author" or "details.category") |
value | scalar or scalar array | Yes | Scalar to match; flat scalar arrays are supported only by contains |
operator | string | No | Operator to use (default: "eq") |
Supported operators:
eq: Exact equality (default)contains: Case-insensitive substring match for strings, or element match for arraysgt: Greater thangte: Greater than or equal tolt: Less thanlte: 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:
| Limit | Value |
|---|---|
| Filters | 20 |
| Field path | 200 characters and 5 dot-separated segments using letters, numbers, _, -, or : |
Values in one contains array | 50 |
| Encoded size of one filter value | 4 KiB |
| Encoded size of all metadata filters | 16 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 modestemporal_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:
| Field | Type | Description |
|---|---|---|
chunks | array of strings | The retrieved text chunks |
scores | array of numbers | Relevance score per chunk, same order as chunks |
files | array of objects | Source 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:
| Field | Type | Description |
|---|---|---|
id | string | Document ID |
user_id | string | Owner of the document ("system" when uploaded without a user_id) |
group_ids | array of strings | Groups the document belongs to |
created_at | integer | Upload time, Unix seconds |
metadata | object | Metadata supplied at upload, plus the filename the service adds |
content_timestamp | string | ISO 8601 timestamp for when the content was created/effective |
top_score | number | Relevance score of the chunk this entry belongs to |
n_chunks | integer | Always 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-keyContent-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:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
model | string | Yes | - | The model to use (typically "cm-llm") |
messages | array | Yes | - | Array of messages in the conversation |
temperature | number | No | 0.7 | Controls randomness (0-1) |
max_tokens | integer | No | 500 | Maximum number of tokens to generate |
max_chunks | integer | No | 4 | Maximum number of context chunks to retrieve |
stream | boolean | No | false | Whether to stream the response |
enable_query_enhancement | boolean | No | true | Enhance queries using conversation history for better retrieval |
smart_temporal_search | boolean | No | Portal config | Enable/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 overheadhuman_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:
| Field | Type | Description |
|---|---|---|
source_id | string | ID of the data source |
lifecycle | string | active while the source is connected to the RAG, deleted once it is removed |
sync_status | string | never_synced, queued, syncing, succeeded, succeeded_with_errors, failed, or cancelled |
sync_interval_seconds | number | How often the source syncs on its own. null when it syncs only when you ask |
next_sync_at | string | When the next scheduled sync is due |
last_sync_requested_at | string | When a sync was last asked for |
last_sync_started_at | string | When the last sync began |
last_sync_completed_at | string | When the last sync ended |
current_generation | integer | Counts the passes the RAG has made over the source |
last_completed_generation | integer | The last pass that finished |
scan_complete | boolean | true once the source has finished at least one full pass over its files |
total_files | integer | Files the RAG has seen in the source |
pending_files | integer | Files still waiting to be indexed |
indexed_files | integer | Files indexed and searchable |
failed_files | integer | Files the RAG could not index |
latest_error | object | code 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, orfailed(optional)cursor: Thenext_cursorfrom 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:
| Field | Type | Description |
|---|---|---|
source_key | string | Where the file sits in the store, for example the object key in a bucket |
source_version | string | The version the store reports for the file, or null |
source_modified_at | string | When the store last changed the file, or null |
desired_operation | string | upsert when the file should be indexed, delete when it should be removed from the RAG |
status | string | pending, indexed, or failed |
document_id | string | ID of the document in the RAG, once the file is indexed. Use it with the Files API |
error | object | code and message of the last failure for this file, or null |
updated_at | string | When 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-keyContent-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"
}
}
| Field | Description |
|---|---|
account_endpoint | The blob endpoint of the storage account, for example https://youraccount.blob.core.windows.net |
container | Name of the container to read. Lower case, 1 to 63 characters |
prefix | Read only the files whose name starts with this text. Send "" to read the whole container |
connection_string | Connection 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:
| Status | Code | Meaning |
|---|---|---|
422 | invalid_configuration | The settings or the credentials cannot be used |
503 | timeout | The store did not answer in time |
503 | unavailable | The 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):
- The system analyzes the full conversation history
- Generates a standalone, context-aware query for retrieval
- Uses the enhanced query to find relevant document chunks
- 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
/contextwhen 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/completionswhen 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_chunksparameter 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_idparameter to search within specific document groups - Use the
user_idparameter 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_timestampvalues when the effective date differs from upload time