skills/cocoindex/references/api_reference.md
Quick reference for the most commonly used CocoIndex APIs.
import cocoindex as coco
All APIs live directly on the coco module. There is no separate cocoindex.asyncio module.
@coco.fn DecoratorMark a function as a CocoIndex processing function.
@coco.fn
async def my_function(arg1: str) -> None: ...
@coco.fn(memo=True, version=1)
async def expensive_fn(data: str) -> Result: ...
# Force async interface for a sync function (useful for batching)
@coco.fn.as_async(memo=True, batching=True)
def batch_embed(texts: list[str]) -> list[NDArray]: ...
Parameters:
memo: bool = False -- Enable memoization (skip if inputs/code unchanged)version: int | None = None -- Explicit version bump to force re-executionlogic_tracking: "full" | "self" | None = "full" -- How code changes are trackedbatching: bool = False -- Auto-batch concurrent calls (async only)max_batch_size: int | None = None -- Max batch sizerunner: Runner | None = None -- e.g. coco.GPU for serialized GPU executionAll mount APIs accept an optional ComponentSubpath as their first argument. When omitted, the subpath is auto-derived from Symbol(fn.__name__). Provide an explicit subpath when mounting the same function multiple times, using multi-part paths, or needing a specific path name.
coco.mount()Mount a processing component in the background.
# Subpath auto-derived from fn.__name__
handle = await coco.mount(processor_fn, *args, **kwargs)
# Explicit subpath
handle = await coco.mount(coco.component_subpath("name"), processor_fn, *args, **kwargs)
await handle.ready() # Optional: wait until component finishes
Parameters:
subpath (optional) -- Component subpath. Auto-derived from fn.__name__ when omitted.processor_fn -- Function (or LiveComponent class) to run.*args, **kwargs -- Arguments passed to the function.Returns: ComponentMountHandle
coco.use_mount()Mount a dependent component and return its result. Parent depends on the child.
# Subpath auto-derived from fn.__name__
result = await coco.use_mount(init_fn, *args, **kwargs)
# Explicit subpath
result = await coco.use_mount(coco.component_subpath("setup"), init_fn, *args, **kwargs)
Parameters:
subpath (optional) -- Component subpath. Auto-derived from fn.__name__ when omitted.processor_fn -- Function to run.*args, **kwargs -- Arguments passed to the function.Returns: The return value of processor_fn.
coco.mount_each()Mount one component per item in a keyed iterable. Preferred for processing lists.
# Subpath auto-derived from fn.__name__
await coco.mount_each(process_file, files.items(), *extra_args)
# Explicit subpath
await coco.mount_each(coco.component_subpath("process"), process_file, files.items(), table)
Parameters:
subpath (optional) -- Component subpath. Auto-derived from fn.__name__ when omitted.fn -- Function to run per item. Item value is passed as first argument.items -- Keyed iterable of (StableKey, T) pairs, or a LiveMapFeed for live mode.*args, **kwargs -- Additional arguments passed to fn after the item.Returns: ComponentMountHandle
coco.mount_target()Mount a target state, ensuring the container is applied before returning the child provider.
provider = await coco.mount_target(target_state)
Prefer connector convenience methods (postgres.mount_table_target(), etc.) which call this internally.
coco.map()Run a function concurrently on each item. No processing components are created -- pure concurrent execution within the current component.
results = await coco.map(process_chunk, chunks, *extra_args)
Parameters:
fn -- Async function to apply. Item is passed as first argument.items -- Iterable or async iterable.*args, **kwargs -- Additional arguments passed to fn after the item.Returns: list[T]
coco.component_subpath()Create a stable component path for mounting.
coco.component_subpath("setup")
coco.component_subpath("file", str(file_path))
coco.component_subpath("record", record.id)
# Chaining with /
subpath = coco.component_subpath("a") / "b" / "c"
# Context manager form (applies to all nested mount calls)
with coco.component_subpath("process"):
for f in files:
await coco.mount(coco.component_subpath(str(f.path)), process_file, f)
StableKey types: str | int | bool | bytes | uuid.UUID | Symbol | tuple[StableKey, ...]
coco.ContextKey[T]Type-safe key for sharing resources. The key string is the stable identity across runs.
PG_DB = coco.ContextKey[asyncpg.Pool]("pg_db")
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")
detect_change=True -- Opt in to auto-invalidate dependent memos when value changes (models, configs)detect_change=False (default) -- For resources not affecting computation (DB connections, loggers)builder.provide()Register a resource in context (used in lifespan).
builder.provide(PG_DB, pool)
builder.provide_with(KEY, context_manager) # Sync CM
await builder.provide_async_with(KEY, async_cm) # Async CM
coco.use_context()Retrieve a resource from context inside a processing function.
pool = coco.use_context(PG_DB)
embedder = coco.use_context(EMBEDDER)
@coco.lifespanDefine environment setup/teardown. Registered to the default environment.
@coco.lifespan
async def coco_lifespan(builder: coco.EnvironmentBuilder) -> AsyncIterator[None]:
async with await asyncpg.create_pool(DATABASE_URL) as pool:
builder.provide(PG_DB, pool)
builder.provide(EMBEDDER, SentenceTransformerEmbedder(MODEL))
yield
Can also be sync:
@coco.lifespan
def coco_lifespan(builder: coco.EnvironmentBuilder) -> Iterator[None]:
builder.settings.db_path = pathlib.Path("./my.db")
yield
coco.Appapp = coco.App(
coco.AppConfig(name="MyApp"),
main_fn,
**params,
)
# Async
await app.update()
handle = app.update(live=True) # Live mode
await app.drop()
# Sync (blocking)
app.update_blocking(report_to_stdout=True)
app.drop_blocking()
coco.AppConfigcoco.AppConfig(
name="MyApp", # Required
environment=env, # Optional: custom Environment
max_inflight_components=1024, # Optional: concurrency limit
)
# Context manager (preferred)
async with coco.runtime():
await app.update()
# Or manually
await coco.start()
try:
await app.update()
finally:
await coco.stop()
# Sync variants
with coco.runtime():
app.update_blocking()
builder.set_exception_handler(my_handler)
async with coco.exception_handler(my_handler):
await coco.mount_each(process_file, files.items(), table)
async def my_handler(exc: BaseException, ctx: coco.ExceptionContext) -> None:
logger.error(f"Error in {ctx.stable_path}: {exc}")
ExceptionContext provides: env_name, stable_path, processor_name, mount_kind, parent_stable_path, is_background, source, original_exception.
Run the pipeline continuously, streaming changes from sources:
# CLI
cocoindex update main.py -L
# Programmatic
handle = app.update(live=True)
async for snapshot in handle.watch():
print(snapshot.stats)
Sources that support live mode:
localfs.walk_dir(..., live=True) -- File watchingkafka.topic_as_map(consumer, topics) -- Kafka topic consumptioncocoindex init [PROJECT_NAME] # Create new project
cocoindex update APP_TARGET # Run app once
cocoindex update APP_TARGET -L # Run in live mode
cocoindex update APP_TARGET --full-reprocess # Force reprocess all
cocoindex update APP_TARGET --reset # Reset state before running
cocoindex drop APP_TARGET [-f] # Drop app and all state
cocoindex ls [APP_TARGET] [--db PATH] # List apps
cocoindex show APP_TARGET [--tree] # Show component paths
APP_TARGET format: main.py, main.py:app_name, my_module:app_name
Import: from cocoindex.ops.text import ...
detect_code_language()language = detect_code_language(filename="example.py") # -> "python"
RecursiveSplittersplitter = RecursiveSplitter()
chunks = splitter.split(
text,
chunk_size=1000,
chunk_overlap=200,
min_chunk_size=300,
language="python", # Syntax-aware splitting
)
# Each chunk: Chunk(text=..., start=TextPosition(...), end=TextPosition(...))
SeparatorSplittersplitter = SeparatorSplitter(separators_regex=[r"\n\n", r"\n"])
chunks = splitter.split(text)
Import: from cocoindex.ops.sentence_transformers import SentenceTransformerEmbedder
embedder = SentenceTransformerEmbedder("sentence-transformers/all-MiniLM-L6-v2")
embedding = await embedder.embed(text) # -> NDArray (float32)
As VectorSchemaProvider:
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")
@dataclass
class Record:
vector: Annotated[NDArray, EMBEDDER] # Auto-infer dimensions
Import: from cocoindex.resources.file import ...
FileLike (async file object)text = await file.read_text()
data = await file.read() # bytes, lazy/cached
fp = await file.content_fingerprint()
PatternFilePathMatchermatcher = PatternFilePathMatcher(
included_patterns=["**/*.py", "**/*.md"],
excluded_patterns=[".*/**", "__pycache__/**"],
)
Import: from cocoindex.resources.id import ...
# Deterministic: same dep -> same ID
chunk_id = await generate_id(chunk.text)
chunk_uuid = generate_uuid(chunk.text)
# Distinct per call (even with same dep)
id_gen = IdGenerator()
chunk_id = await id_gen.next_id(chunk.text)
uuid_gen = UuidGenerator()
chunk_uuid = uuid_gen.next_uuid(chunk.text)
Import: from cocoindex.resources.schema import VectorSchema
# Via ContextKey (preferred -- auto-infer from embedder)
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")
@dataclass
class Record:
vector: Annotated[NDArray, EMBEDDER]
# Explicit dimensions
schema = VectorSchema(dtype=np.dtype(np.float32), size=384)
@dataclass
class Record:
vector: Annotated[NDArray, schema]