Context Service
Overview
The Context Service (ContextService, accessed via client.context) is the SDK
wrapper for the Kamiwaza Context Service. Located in
kamiwaza_sdk/services/context.py, it manages workroom-scoped vector databases,
ontologies (knowledge graphs), ingestion pipelines, and retrieval against the
documents and knowledge held inside a Workroom.
Every Context Service resource lives inside exactly one workroom. The SDK targets
a workroom by passing workroom_id to a method (sent to the server as the
X-Workroom-ID header). Some methods make workroom_id a required
keyword-only argument (e.g. list_collections, create_pipeline_job, search,
retrieve, upload_file); others accept it optionally, and when it is omitted
the server resolves the caller's default workroom.
For PAT/API-key automation that makes several calls against the same workroom,
derive a local scoped client instead of calling workrooms.enter():
with client.workroom_scope(my_workroom_id) as scoped:
db = scoped.context.create_vectordb(name="project-vdb", engine="milvus")
scoped.context.insert_vectors(
db["id"],
collection_name="project_docs",
vectors=[embedding],
metadata=[{"source": "seed"}],
)
The scoped client only adds the explicit workroom header on SDK requests; it does not change the parent client or mutate server-side selected-session state. It is not a client-side security boundary; the server must still authorize the caller for the requested workroom on every request.
Workrooms and the Global Workroom
A Workroom is the collaboration and isolation boundary for context: vector collections, ontologies, ingested files, and pipeline jobs all belong to a workroom, and access is scoped to that workroom's members.
The Global Workroom is a special, well-known workroom (the all-f sentinel
ffffffff-ffff-ffff-ffff-ffffffffffff, exposed as
ContextService.DEFAULT_WORKROOM_ID). It holds the platform's shared,
read-only catalog of context — knowledge that is visible to everyone but owned
by no single member.
The Global Workroom is read-only for tenant writes
Key semantics: Any direct write that targets the Global Workroom is rejected by the server with HTTP 403 and a body of
Global Workroom is read-only for <operation>(for exampleGlobal Workroom is read-only for ontology creation). This is intentional and by design, not a bug — the Global Workroom is a shared catalog, so it is populated only by the platform's own ingestion paths, never by ad-hoc tenant writes.
Reads against the Global Workroom are always allowed (results are
requester-scoped where appropriate). Writes are blocked. The table below lists
the server-side policy categories — some (e.g. pipeline retry/rerun/delete,
workroom archive/restore/purge) are enforced by the Context Service but are not
all surfaced as client.context methods; the SDK exposes a subset (see the
method lists below).
| Operation category | Against a normal workroom | Against the Global Workroom |
|---|---|---|
| List / get / query / search / retrieve (reads) | ✅ allowed | ✅ allowed |
| VectorDB create / update / delete / insert | ✅ allowed | ❌ 403 read-only |
| Ontology create / delete / add_knowledge / add_entity / delete_group | ✅ allowed | ❌ 403 read-only |
| Collection create / delete, chunk indexing / deletion | ✅ allowed | ❌ 403 read-only |
| Pipeline job create / cancel / retry / rerun / delete | ✅ allowed | ❌ 403 read-only |
| Raw file upload | ✅ allowed | ❌ 403 read-only |
| Workroom archive / restore / purge (lifecycle) | ✅ allowed | ❌ 403 read-only |
Note on
*_globalhelpers.query_vectors_global()is a read against the shared catalog and works for any caller.insert_vectors_global()targets the same shared catalog and is reserved for the platform's connector-runtime ingestion path — a direct tenant call will hit the same 403 read-only policy. To store your own vectors, create or use a normal (non-global) workroom.
If you are migrating tests or code that assumed the Global Workroom was writable:
point the write at a workroom you own (pass its workroom_id), and only read
from the Global Workroom.
VectorDB
Available Methods
list_vectordbs(*, workroom_id=None)get_vectordb(vectordb_id, *, workroom_id=None)create_vectordb(*, workroom_id=None, ...)update_vectordb(vectordb_id, *, workroom_id=None, ...)scale_vectordb(vectordb_id, *, workroom_id=None, ...)delete_vectordb(vectordb_id, *, workroom_id=None)query_vectors(vectordb_id, *, collection_name, vectors, limit=10, workroom_id=None)query_vectors_global(*, vectordb_id, collection_name, vectors, limit=10)— read the shared cataloginsert_vectors(vectordb_id, *, collection_name, vectors, metadata, workroom_id=None)insert_vectors_global(...)— connector-runtime catalog writes only (see note above)
# db_id, embedding, and my_workroom_id are values you supply.
# Read from the shared Global catalog (allowed)
hits = client.context.query_vectors_global(
vectordb_id=db_id,
collection_name="shared-docs",
vectors=[embedding],
limit=5,
)
# Write to a workroom you own (allowed) — NOT the Global Workroom
client.context.insert_vectors(
db_id,
collection_name="my-docs",
vectors=[embedding],
metadata=[{"source": "doc1"}],
workroom_id=my_workroom_id,
)
Ontology (Knowledge Graph)
Available Methods
list_ontologies(*, workroom_id=None)/get_ontology(...)create_ontology(...)/delete_ontology(...)add_knowledge(...)/add_entity(...)/delete_group(...)search_knowledge(...)/get_memory(...)/get_episodes(...)ontology_health(...)
Reads (list_*, get_*, search_knowledge, get_memory, get_episodes,
ontology_health) work against any workroom including Global. Writes
(create_ontology, delete_ontology, add_knowledge, add_entity,
delete_group) are rejected on the Global Workroom.
Collections, Pipelines, and Retrieval
Available Methods
list_collections(...)/create_collection(...)/get_collection(...)/delete_collection(...)create_pipeline_job(...)/list_pipeline_jobs(...)/get_pipeline_job(...)get_supported_file_types()search(...)/retrieve(...)/upload_file(...)
retrieve(...) score threshold
score_threshold=Nonesends no threshold, so the platform's default applies: no floor on Kamiwaza 1.0 and later.- A number sets a floor, which the platform compares with each hit's raw vector similarity. Lexical matches are not filtered, and when the platform's lexical leg runs, returned scores are fused rank scores that can be lower than the floor.
- Omitting it is deprecated: the SDK still sends 0.7 and emits a
DeprecationWarning. In kamiwaza-sdk 2.0 the default becomesNone(the 2.0 default change). A 0.7 floor often drops natural-language matches. - Hosts and agent-tool dispatchers: Python hides
DeprecationWarningin library code by default, so a host that omits the argument keeps the 0.7 floor without seeing the warning. Passscore_threshold=Noneexplicitly to drop it before 2.0.
Pipeline cancel vs. delete
The SDK exposes two distinct teardown verbs for a pipeline job:
cancel_pipeline_job(...)— graceful cancel (POST /context/pipelines/{job_id}/cancel). Stops the job but preserves its recorded history; the job stays fetchable viaget_pipeline_job(...).delete_pipeline_job(...)— destructive delete (DELETE /context/pipelines/{job_id}). Cancels a pending/running job and then removes the job and its history.
Breaking change:
cancel_pipeline_job(...)previously mapped to the destructiveDELETEroute. It now maps to the graceful cancel route, and the destructive behavior moved to the newdelete_pipeline_job(...). Update any caller that relied on the oldcancel_pipeline_jobto hard-delete a job.
Provider-neutral source import and replay
For Kaizen / import-shell automation, the SDK wraps the provider-neutral source-import and replay surface:
get_import_options(...)/evaluate_import_options(...)— aggregate and validate import options for selected source descriptors.create_source_import_job(...)— start a provider-neutral source-import job.list_import_items(...)— workroom-wide source-import inventory/history.rerun_import_items(...)— rerun selected inventory items by recorded source descriptor.list_pipeline_job_items(...)— per-item statuses for one job.retry_pipeline_job(...)— retry failed/incomplete items from a replayable import job.rerun_pipeline_job(...)— rerun all recorded source descriptors from a prior import job.
Collection/pipeline/file writes follow the same rule: allowed in a normal
workroom, 403 on the Global Workroom.
Raw-file Object Storage
The SDK wraps the workroom-scoped raw-file object-storage CRUD surface
(/context/storage/raw). These methods are keyword-only and take an explicit
workroom_id (sent as the X-Workroom-ID header); they return raw dict
records rather than typed models.
Available Methods
store_raw_file(*, workroom_id, filename, content, ...)— store a raw file directly into workroom object storage (POST /context/storage/raw).contentacceptsbytesor astr(UTF-8 encoded) and is base64-encoded on the wire for you. Optionalcontent_type,source_urn,source_kind,source_ref, andmetadataare forwarded only when set.list_raw_files(*, workroom_id, ...)— list stored raw files (GET /context/storage/raw), returning the{"items": [...], "count": N}shape. Supportssource_urn/job_id/connector_idfilters,limit/offsetpaging, andinclude_markings=Trueto attach aggregated security markings.get_raw_file(file_id, *, workroom_id, ...)— fetch one raw-file record (GET /context/storage/raw/{file_id}). Setinclude_download_url=Truefor a temporary presigned download URL (when S3 metadata exists);expires_seconds(30–3600) overrides its TTL.update_raw_file(file_id, *, workroom_id, content, if_match=None)— replace the plain-text body of an editable raw file (PUT /context/storage/raw/{file_id}). Pass the file'supdated_attoken from a prior response asif_matchfor optimistic concurrency; a stale token returns409with the current token in the error detail.
# my_workroom_id is a workroom you own.
stored = client.context.store_raw_file(
workroom_id=my_workroom_id,
filename="notes.txt",
content="hello raw storage",
content_type="text/plain",
)
file_id = stored["id"]
current = client.context.get_raw_file(file_id, workroom_id=my_workroom_id)
client.context.update_raw_file(
file_id,
workroom_id=my_workroom_id,
content="edited body",
if_match=current["updated_at"], # optimistic concurrency
)
OmniParse Instances
The SDK wraps the workroom-scoped OmniParse instance lifecycle CRUD surface
(/context/omniparses). OmniParse instances are the document-parsing runtimes
that ingest pipelines depend on; wrapping them lets callers provision and manage
OmniParse from automation rather than relying only on implicit lazy
provisioning. These methods are keyword-only, take an explicit workroom_id
(sent as the X-Workroom-ID header), and return raw dict / list[dict]
records rather than typed models.
Available Methods
list_omniparses(*, workroom_id)— list OmniParse instances for a workroom (GET /context/omniparses), returning a list of instance records.get_omniparse(omniparse_id, *, workroom_id)— fetch one instance by id (GET /context/omniparses/{omniparse_id}).create_omniparse(*, name, workroom_id, template_name="tool-omniparse", config=None)— provision an OmniParse runtime instance (POST /context/omniparses).template_nameselects the App Garden tool template;configcarries optional OmniParse environment configuration and is forwarded only when set.update_omniparse(omniparse_id, *, workroom_id, config=None)— update an instance's environment configuration (PUT /context/omniparses/{omniparse_id}).delete_omniparse(omniparse_id, *, workroom_id)— delete an instance (DELETE /context/omniparses/{omniparse_id}).
# my_workroom_id is a workroom you own.
instance = client.context.create_omniparse(
name="docs-parser",
workroom_id=my_workroom_id,
config={"replicas": 1},
)
instance_id = instance["id"]
client.context.update_omniparse(
instance_id,
workroom_id=my_workroom_id,
config={"replicas": 2},
)
client.context.delete_omniparse(instance_id, workroom_id=my_workroom_id)
Global Settings, Document Download, and Audio Readiness
These cover platform-wide Context configuration, presigned download of an original document, and an audio-ingest preflight probe.
Available Methods
get_global_settings()— read platform-wide Context global settings (GET /context/global-settings). Platform-scoped (ContextAdmin), not workroom-scoped — noX-Workroom-IDheader is sent.update_global_settings(*, omniparse=None, reason=None)— patch the global settings (PATCH /context/global-settings).omniparseis a partial settings dict (e.g.{"force_insecure_model_ssl": True});reasonis an optional audit annotation. Only the provided fields change. Also ContextAdmin-scoped.get_document_download_url(source_urn, *, workroom_id)— get a presigned S3 download URL for an original document by source URN (GET /context/documents/{source_urn}). Returnsdownload_urlplusfilename/content_hash/source_urn. Requires S3 document storage to be enabled server-side.get_audio_readiness(*, workroom_id, mime_type=None, filename=None)— probe whether a workroom is ready to ingest audio (GET /context/audio-readiness). Optionalmime_type(e.g.audio/aiff) andfilenamerefine the check. Returns the readiness verdict (ready/code/message/remediation/details). This GET never side-effect-provisions an OmniParse instance.
# Platform-scoped admin config (no workroom).
settings = client.context.get_global_settings()
client.context.update_global_settings(
omniparse={"force_insecure_model_ssl": False},
reason="rotate certs",
)
# Workroom-scoped: preflight audio ingest, then resolve a stored document.
readiness = client.context.get_audio_readiness(
workroom_id=my_workroom_id,
mime_type="audio/aiff",
)
if readiness["ready"]:
download = client.context.get_document_download_url(
"urn:source:abc123",
workroom_id=my_workroom_id,
)
url = download["download_url"]
Error Handling
from kamiwaza_sdk.exceptions import KamiwazaError
# my_workroom_id is a workroom you own. The Global Workroom id is available as
# client.context.DEFAULT_WORKROOM_ID.
try:
client.context.create_ontology(
name="my-graph",
backend="graphiti",
workroom_id=client.context.DEFAULT_WORKROOM_ID, # Global → will 403
)
except KamiwazaError as exc:
if exc.status_code == 403 and "read-only" in str(exc):
# Expected: the Global Workroom is a shared, read-only catalog.
# Retry the write against a workroom you own instead.
client.context.create_ontology(
name="my-graph",
backend="graphiti",
workroom_id=my_workroom_id,
)
else:
raise
Best Practices
- Treat the Global Workroom as read-only: query/search it, but write your own resources into a workroom you own.
- Pass an explicit
workroom_idon writes so a resource never accidentally resolves to the Global Workroom (an omitted workroom can resolve to Global on some paths and will be rejected). - Branch on
status_code == 403+"read-only"to distinguish the shared-catalog policy from a genuine permission error. - Use
query_vectors_global()/ read-only ontology queries to consume shared knowledge; never rely on writing to Global from tenant code.