cocoindex

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

CocoIndex

CocoIndex

CocoIndex is a Python library for building incremental data processing pipelines with declarative target states. Think spreadsheets or React for data pipelines: declare what the output should look like based on current input, and CocoIndex automatically handles incremental updates, change detection, and syncing to external systems.
CocoIndex是一款用于构建增量数据处理管道的Python库,采用声明式目标状态模式。可以将其理解为数据管道领域的电子表格或React:只需基于当前输入声明输出应有的样子,CocoIndex就会自动处理增量更新、变更检测以及与外部系统的同步。

Overview

概述

CocoIndex enables building data pipelines that:
  • Automatically handle incremental updates: Only reprocess changed data
  • Use declarative target states: Declare what should exist, not how to update
  • Support any Python types: No custom DSL -- use dataclasses, Pydantic, NamedTuple
  • Provide function memoization: Skip expensive operations when inputs/code unchanged
  • Sync to multiple targets: PostgreSQL, SQLite, LanceDB, Qdrant, SurrealDB, Apache Doris, file systems, Kafka
Key principle:
TargetState = Transform(SourceState)
CocoIndex支持构建具备以下特性的数据管道:
  • 自动处理增量更新:仅重新处理发生变更的数据
  • 采用声明式目标状态:声明应存在的结果,而非更新的方式
  • 支持所有Python类型:无需自定义领域特定语言(DSL)——可使用dataclasses、Pydantic、NamedTuple
  • 提供函数记忆化:当输入/代码未变更时,跳过昂贵的操作
  • 同步至多个目标系统:PostgreSQL、SQLite、LanceDB、Qdrant、SurrealDB、Apache Doris、文件系统、Kafka
核心原则
TargetState = Transform(SourceState)

When to Use This Skill

何时使用本技能

Use this skill when building pipelines that involve:
  • Document processing: PDF/Markdown conversion, text extraction, chunking
  • Vector embeddings: Embedding documents/code for semantic search
  • Database transformations: ETL from source DB to target DB
  • Knowledge graphs: Extract entities and relationships from data
  • LLM-based extraction: Structured data extraction using LLMs
  • File-based pipelines: Transform files from one format to another
  • Incremental indexing: Keep search indexes up-to-date with source changes
  • Streaming pipelines: Kafka-based real-time data processing
当构建涉及以下场景的管道时,使用本技能:
  • 文档处理:PDF/Markdown转换、文本提取、分块
  • 向量嵌入:为文档/代码生成嵌入用于语义搜索
  • 数据库转换:从源数据库到目标数据库的ETL
  • 知识图谱:从数据中提取实体与关系
  • 基于LLM的提取:使用LLM提取结构化数据
  • 基于文件的管道:将文件从一种格式转换为另一种
  • 增量索引:保持搜索索引与源数据变更同步
  • 流处理管道:基于Kafka的实时数据处理

Quick Start: Creating a New Project

快速开始:创建新项目

Initialize Project

初始化项目

bash
cocoindex init my-project
cd my-project
This creates:
main.py
,
pyproject.toml
,
README.md
. The generated
main.py
sets the database location in its lifespan via
builder.settings.db_path = pathlib.Path("./cocoindex.db")
.
bash
cocoindex init my-project
cd my-project
此命令会创建:
main.py
pyproject.toml
README.md
。生成的
main.py
会在生命周期中通过
builder.settings.db_path = pathlib.Path("./cocoindex.db")
设置数据库位置。

Add Dependencies

添加依赖

toml
undefined
toml
undefined

For vector embeddings with PostgreSQL

用于PostgreSQL的向量嵌入

dependencies = ["cocoindex>=1.0.0", "sentence-transformers", "asyncpg"]
dependencies = ["cocoindex>=1.0.0", "sentence-transformers", "asyncpg"]

For LLM extraction

用于LLM提取

dependencies = ["cocoindex>=1.0.0", "litellm", "instructor", "pydantic>=2.0"]

See [references/setup_project.md](references/setup_project.md) for complete examples.
dependencies = ["cocoindex>=1.0.0", "litellm", "instructor", "pydantic>=2.0"]

完整示例请参考[references/setup_project.md](references/setup_project.md)。

Run the Pipeline

运行管道

bash
uv run cocoindex update main.py   # or: pip install -e . && cocoindex update main.py
bash
uv run cocoindex update main.py   # 或:pip install -e . && cocoindex update main.py

Core Concepts

核心概念

1. Apps

1. Apps

An App is the top-level executable that binds a main function with parameters:
python
import cocoindex as coco

@coco.fn
async def app_main(sourcedir: pathlib.Path) -> None:
    ...

app = coco.App(
    coco.AppConfig(name="MyApp"),
    app_main,
    sourcedir=pathlib.Path("./data"),
)
App是顶级可执行单元,将主函数与参数绑定:
python
import cocoindex as coco

@coco.fn
async def app_main(sourcedir: pathlib.Path) -> None:
    ...

app = coco.App(
    coco.AppConfig(name="MyApp"),
    app_main,
    sourcedir=pathlib.Path("./data"),
)

2. Functions (
@coco.fn
)

2. Functions (
@coco.fn
)

The
@coco.fn
decorator marks functions as CocoIndex processing functions. Add
memo=True
to skip re-execution when inputs/code are unchanged:
python
@coco.fn(memo=True)
async def expensive_operation(data: str) -> Result:
    # LLM call, embedding generation, heavy computation
    return await expensive_transform(data)
Key parameters:
  • memo=True
    -- Enable memoization (skip if inputs/code unchanged)
  • version=1
    -- Explicit version bump to force re-execution
  • batching=True
    -- Auto-batch concurrent calls (async only)
  • runner=coco.GPU
    -- Serialize GPU-bound execution
@coco.fn
装饰器标记函数为CocoIndex处理函数。添加
memo=True
可在输入/代码未变更时跳过重新执行:
python
@coco.fn(memo=True)
async def expensive_operation(data: str) -> Result:
    # LLM调用、嵌入生成、重型计算
    return await expensive_transform(data)
关键参数:
  • memo=True
    -- 启用记忆化(输入/代码未变更时跳过)
  • version=1
    -- 显式版本升级以强制重新执行
  • batching=True
    -- 自动批量处理并发调用(仅异步)
  • runner=coco.GPU
    -- 序列化GPU绑定执行

3. Processing Components

3. 处理组件

A processing component groups an item's processing with its target states.
Mount components with
mount_each()
(preferred for lists) or
mount()
:
python
undefined
处理组件将项的处理逻辑与其目标状态分组。
使用
mount_each()
(推荐用于列表)或
mount()
挂载组件
python
undefined

One component per item (preferred for lists)

每个项对应一个组件(推荐用于列表)

await coco.mount_each(process_file, files.items(), target_table)
await coco.mount_each(process_file, files.items(), target_table)

Single component (subpath auto-derived from fn.name)

单个组件(子路径自动从fn.__name__派生)

await coco.mount(setup_fn, arg1)
await coco.mount(setup_fn, arg1)

Dependent component (blocks until result returned)

依赖组件(等待结果返回后执行)

result = await coco.use_mount(init_fn)
result = await coco.use_mount(init_fn)

Explicit subpath (when you need a specific path, e.g. in loops)

显式子路径(当需要特定路径时,例如循环中)

await coco.mount(coco.component_subpath("item", item_id), process_item, item)

**Key points:**
- All mount APIs are `async`
- `mount()`, `use_mount()`, and `mount_each()` auto-derive subpath from `fn.__name__`; optional explicit subpath as first arg
- Use `use_mount()` when you need the return value
- Use stable component paths for proper memoization and cleanup
await coco.mount(coco.component_subpath("item", item_id), process_item, item)

**关键点:**
- 所有挂载API均为`async`
- `mount()`、`use_mount()`和`mount_each()`自动从`fn.__name__`派生子路径;可选择将显式子路径作为第一个参数
- 需要返回值时使用`use_mount()`
- 使用稳定的组件路径以确保正确的记忆化与清理

4. Target States

4. 目标状态

Declare what should exist -- CocoIndex handles creation/update/deletion:
python
undefined
声明应存在的内容——CocoIndex会处理创建/更新/删除:
python
undefined

Database row target

数据库行目标

table.declare_row(row=MyRecord(id=1, name="example"))
table.declare_row(row=MyRecord(id=1, name="example"))

File target

文件目标

localfs.declare_file(outdir / "output.txt", content, create_parent_dirs=True)
localfs.declare_file(outdir / "output.txt", content, create_parent_dirs=True)

Kafka message target

Kafka消息目标

topic_target.declare_target_state(key="msg-1", value=json.dumps(data))
undefined
topic_target.declare_target_state(key="msg-1", value=json.dumps(data))
undefined

5. Context for Shared Resources

5. 共享资源上下文

Use
ContextKey
to share expensive resources (DB connections, models) across components:
python
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")

@coco.lifespan
async def coco_lifespan(builder: coco.EnvironmentBuilder) -> AsyncIterator[None]:
    builder.provide(EMBEDDER, SentenceTransformerEmbedder("all-MiniLM-L6-v2"))
    yield
使用
ContextKey
在组件间共享昂贵资源(数据库连接、模型):
python
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")

@coco.lifespan
async def coco_lifespan(builder: coco.EnvironmentBuilder) -> AsyncIterator[None]:
    builder.provide(EMBEDDER, SentenceTransformerEmbedder("all-MiniLM-L6-v2"))
    yield

In processing functions:

在处理函数中:

embedder = coco.use_context(EMBEDDER)

The `@coco.lifespan` decorator registers the function to the default CocoIndex environment, which is shared among all apps by default. `ContextKey` also serves as the stable identity for sources/targets -- the `key` string must remain stable across runs.
embedder = coco.use_context(EMBEDDER)

`@coco.lifespan`装饰器将函数注册到默认的CocoIndex环境,该环境默认在所有App间共享。`ContextKey`同时作为源/目标的稳定标识——`key`字符串在多次运行中必须保持稳定。

6. ID Generation

6. ID生成

Generate stable, unique identifiers that persist across incremental updates:
python
from cocoindex.resources.id import generate_id, IdGenerator
生成稳定、唯一的标识符,在增量更新中保持持久:
python
from cocoindex.resources.id import generate_id, IdGenerator

Deterministic: same dep -> same ID

确定性:相同依赖 -> 相同ID

chunk_id = await generate_id(chunk.text)
chunk_id = await generate_id(chunk.text)

Always distinct: each call -> new ID, even with same dep

始终唯一:每次调用 -> 新ID,即使依赖相同

id_gen = IdGenerator() for chunk in chunks: chunk_id = await id_gen.next_id(chunk.text)
undefined
id_gen = IdGenerator() for chunk in chunks: chunk_id = await id_gen.next_id(chunk.text)
undefined

7. Catch-Up vs Live Mode

7. 追赶模式 vs 实时模式

By default,
app.update()
runs in catch-up mode: it scans all sources, processes what changed since the last run (memoized components are skipped), syncs target states, and returns. Each call still has to scan sources to discover changes.
Live mode keeps the app running after catch-up and lets components stream changes continuously from their sources (e.g., file watcher, Kafka consumer), applying them with very low latency.
python
undefined
默认情况下,
app.update()
运行在追赶模式:扫描所有源,处理自上次运行以来变更的内容(跳过记忆化组件),同步目标状态后返回。每次调用仍需扫描源以发现变更。
实时模式在追赶完成后保持App运行,允许组件从源持续流式获取变更(例如文件监视器、Kafka消费者),以极低延迟应用这些变更。
python
undefined

Enable live mode

启用实时模式

app.update_blocking(live=True)
app.update_blocking(live=True)

Or: cocoindex update main.py -L

或:cocoindex update main.py -L


Two things are needed: (1) enable live mode on the app, and (2) use a source that supports live updates.

- **`LiveMapView`** sources (e.g., `localfs.walk_dir(..., live=True)`) scan current state first, then watch for changes. They also work in catch-up mode -- write the pipeline once, choose mode at run time.
- **`LiveMapFeed`** sources (e.g., `kafka.topic_as_map()`) only stream changes with no initial snapshot. `mount_each()` auto-detects these and creates a live component internally.

```python

需要满足两个条件:(1) 在App上启用实时模式,(2) 使用支持实时更新的源。

- **`LiveMapView`**源(例如`localfs.walk_dir(..., live=True)`)先扫描当前状态,然后监视变更。它们也可在追赶模式下工作——只需编写一次管道,在运行时选择模式即可。
- **`LiveMapFeed`**源(例如`kafka.topic_as_map()`)仅流式传输变更,无初始快照。`mount_each()`会自动检测这些源并在内部创建实时组件。

```python

LocalFS with live watching

启用实时监视的LocalFS

files = localfs.walk_dir(sourcedir, live=True, ...) await coco.mount_each(process_file, files.items(), target)
files = localfs.walk_dir(sourcedir, live=True, ...) await coco.mount_each(process_file, files.items(), target)

Kafka -- inherently live

Kafka——天生支持实时

items = kafka.topic_as_map(consumer, ["my-topic"]) await coco.mount_each(process_message, items, target)
undefined
items = kafka.topic_as_map(consumer, ["my-topic"]) await coco.mount_each(process_message, items, target)
undefined

Common Pipeline Patterns

常见管道模式

Pattern 1: File Transformation

模式1:文件转换

python
import pathlib
import cocoindex as coco
from cocoindex.connectors import localfs
from cocoindex.resources.file import FileLike, PatternFilePathMatcher

@coco.fn(memo=True)
async def process_file(file: FileLike, outdir: pathlib.Path) -> None:
    content = await file.read_text()
    transformed = transform_content(content)
    outname = file.file_path.path.stem + ".out"
    localfs.declare_file(outdir / outname, transformed, create_parent_dirs=True)

@coco.fn
async def app_main(sourcedir: pathlib.Path, outdir: pathlib.Path) -> None:
    files = localfs.walk_dir(
        sourcedir,
        recursive=True,
        path_matcher=PatternFilePathMatcher(included_patterns=["**/*.md"]),
    )
    await coco.mount_each(process_file, files.items(), outdir)

app = coco.App(
    coco.AppConfig(name="Transform"),
    app_main,
    sourcedir=pathlib.Path("./data"),
    outdir=pathlib.Path("./out"),
)
python
import pathlib
import cocoindex as coco
from cocoindex.connectors import localfs
from cocoindex.resources.file import FileLike, PatternFilePathMatcher

@coco.fn(memo=True)
async def process_file(file: FileLike, outdir: pathlib.Path) -> None:
    content = await file.read_text()
    transformed = transform_content(content)
    outname = file.file_path.path.stem + ".out"
    localfs.declare_file(outdir / outname, transformed, create_parent_dirs=True)

@coco.fn
async def app_main(sourcedir: pathlib.Path, outdir: pathlib.Path) -> None:
    files = localfs.walk_dir(
        sourcedir,
        recursive=True,
        path_matcher=PatternFilePathMatcher(included_patterns=["**/*.md"]),
    )
    await coco.mount_each(process_file, files.items(), outdir)

app = coco.App(
    coco.AppConfig(name="Transform"),
    app_main,
    sourcedir=pathlib.Path("./data"),
    outdir=pathlib.Path("./out"),
)

Pattern 2: Vector Embedding Pipeline

模式2:向量嵌入管道

python
import pathlib
from dataclasses import dataclass
from typing import AsyncIterator, Annotated

import asyncpg
from numpy.typing import NDArray

import cocoindex as coco
from cocoindex.connectors import localfs, postgres
from cocoindex.ops.text import RecursiveSplitter
from cocoindex.ops.sentence_transformers import SentenceTransformerEmbedder
from cocoindex.resources.chunk import Chunk
from cocoindex.resources.file import FileLike, PatternFilePathMatcher
from cocoindex.resources.id import IdGenerator

DATABASE_URL = "postgres://cocoindex:cocoindex@localhost/cocoindex"
PG_DB = coco.ContextKey[asyncpg.Pool]("pg_db")
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")

_splitter = RecursiveSplitter()

@dataclass
class DocEmbedding:
    id: int
    filename: str
    text: str
    embedding: Annotated[NDArray, EMBEDDER]  # Dimensions inferred from ContextKey
    chunk_start: int
    chunk_end: int

@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("all-MiniLM-L6-v2"))
        yield

@coco.fn
async def process_chunk(
    chunk: Chunk, filename: pathlib.PurePath,
    id_gen: IdGenerator, table: postgres.TableTarget[DocEmbedding],
) -> None:
    table.declare_row(row=DocEmbedding(
        id=await id_gen.next_id(chunk.text),
        filename=str(filename),
        text=chunk.text,
        embedding=await coco.use_context(EMBEDDER).embed(chunk.text),
        chunk_start=chunk.start.char_offset,
        chunk_end=chunk.end.char_offset,
    ))

@coco.fn(memo=True)
async def process_file(file: FileLike, table: postgres.TableTarget[DocEmbedding]) -> None:
    text = await file.read_text()
    chunks = _splitter.split(text, chunk_size=2000, chunk_overlap=500)
    id_gen = IdGenerator()
    await coco.map(process_chunk, chunks, file.file_path.path, id_gen, table)

@coco.fn
async def app_main(sourcedir: pathlib.Path) -> None:
    target_table = await postgres.mount_table_target(
        PG_DB,
        table_name="embeddings",
        table_schema=await postgres.TableSchema.from_class(DocEmbedding, primary_key=["id"]),
    )
    target_table.declare_vector_index(column="embedding")

    files = localfs.walk_dir(sourcedir, recursive=True,
        path_matcher=PatternFilePathMatcher(included_patterns=["**/*.md"]))
    await coco.mount_each(process_file, files.items(), target_table)

app = coco.App(coco.AppConfig(name="Embedding"), app_main, sourcedir=pathlib.Path("./data"))
python
import pathlib
from dataclasses import dataclass
from typing import AsyncIterator, Annotated

import asyncpg
from numpy.typing import NDArray

import cocoindex as coco
from cocoindex.connectors import localfs, postgres
from cocoindex.ops.text import RecursiveSplitter
from cocoindex.ops.sentence_transformers import SentenceTransformerEmbedder
from cocoindex.resources.chunk import Chunk
from cocoindex.resources.file import FileLike, PatternFilePathMatcher
from cocoindex.resources.id import IdGenerator

DATABASE_URL = "postgres://cocoindex:cocoindex@localhost/cocoindex"
PG_DB = coco.ContextKey[asyncpg.Pool]("pg_db")
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")

_splitter = RecursiveSplitter()

@dataclass
class DocEmbedding:
    id: int
    filename: str
    text: str
    embedding: Annotated[NDArray, EMBEDDER]  # 维度从ContextKey自动推断
    chunk_start: int
    chunk_end: int

@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("all-MiniLM-L6-v2"))
        yield

@coco.fn
async def process_chunk(
    chunk: Chunk, filename: pathlib.PurePath,
    id_gen: IdGenerator, table: postgres.TableTarget[DocEmbedding],
) -> None:
    table.declare_row(row=DocEmbedding(
        id=await id_gen.next_id(chunk.text),
        filename=str(filename),
        text=chunk.text,
        embedding=await coco.use_context(EMBEDDER).embed(chunk.text),
        chunk_start=chunk.start.char_offset,
        chunk_end=chunk.end.char_offset,
    ))

@coco.fn(memo=True)
async def process_file(file: FileLike, table: postgres.TableTarget[DocEmbedding]) -> None:
    text = await file.read_text()
    chunks = _splitter.split(text, chunk_size=2000, chunk_overlap=500)
    id_gen = IdGenerator()
    await coco.map(process_chunk, chunks, file.file_path.path, id_gen, table)

@coco.fn
async def app_main(sourcedir: pathlib.Path) -> None:
    target_table = await postgres.mount_table_target(
        PG_DB,
        table_name="embeddings",
        table_schema=await postgres.TableSchema.from_class(DocEmbedding, primary_key=["id"]),
    )
    target_table.declare_vector_index(column="embedding")

    files = localfs.walk_dir(sourcedir, recursive=True,
        path_matcher=PatternFilePathMatcher(included_patterns=["**/*.md"]))
    await coco.mount_each(process_file, files.items(), target_table)

app = coco.App(coco.AppConfig(name="Embedding"), app_main, sourcedir=pathlib.Path("./data"))

Pattern 3: LLM-Based Extraction

模式3:基于LLM的提取

python
import instructor
from pydantic import BaseModel
from litellm import acompletion

_instructor_client = instructor.from_litellm(acompletion, mode=instructor.Mode.JSON)

class ExtractionResult(BaseModel):
    title: str
    topics: list[str]

@coco.fn(memo=True)  # Memo avoids re-calling LLM
async def extract_and_store(content: str, message_id: int, table) -> None:
    result = await _instructor_client.chat.completions.create(
        model="gpt-4",
        response_model=ExtractionResult,
        messages=[{"role": "user", "content": f"Extract topics: {content}"}],
    )
    table.declare_row(row=Message(id=message_id, title=result.title, content=content))
python
import instructor
from pydantic import BaseModel
from litellm import acompletion

_instructor_client = instructor.from_litellm(acompletion, mode=instructor.Mode.JSON)

class ExtractionResult(BaseModel):
    title: str
    topics: list[str]

@coco.fn(memo=True)  # 记忆化避免重复调用LLM
async def extract_and_store(content: str, message_id: int, table) -> None:
    result = await _instructor_client.chat.completions.create(
        model="gpt-4",
        response_model=ExtractionResult,
        messages=[{"role": "user", "content": f"Extract topics: {content}"}],
    )
    table.declare_row(row=Message(id=message_id, title=result.title, content=content))

Connectors and Operations

连接器与操作

CocoIndex provides connectors for reading from and writing to external systems:
ConnectorSourceTargetVectorsUse Case
PostgreSQLYYpgvectorProduction SQL + vectors
SQLite-Ysqlite-vecLocal SQL + vectors
LanceDB-YYCloud-native vector DB
Qdrant-YYSpecialized vector DB
SurrealDB-YYGraph + document DB
Apache Doris-YYAnalytical DB + vectors
LocalFSYYN/AFile-based pipelines
Amazon S3Y-N/ACloud object storage
KafkaYYN/AStreaming pipelines
Google DriveY-N/ACloud file source
For detailed connector documentation, see references/connectors.md.
CocoIndex提供用于读取和写入外部系统的连接器:
连接器目标向量适用场景
PostgreSQLpgvector生产级SQL+向量
SQLite-sqlite-vec本地SQL+向量
LanceDB-云原生向量数据库
Qdrant-专业向量数据库
SurrealDB-图+文档数据库
Apache Doris-分析型数据库+向量
LocalFSN/A基于文件的管道
Amazon S3-N/A云对象存储
KafkaN/A流处理管道
Google Drive-N/A云文件源
详细连接器文档请参考references/connectors.md

Text and Embedding Operations

文本与嵌入操作

Text Splitting

文本分块

python
from cocoindex.ops.text import RecursiveSplitter, detect_code_language

splitter = RecursiveSplitter()
language = detect_code_language(filename="example.py")
chunks = splitter.split(text, chunk_size=1000, chunk_overlap=200, language=language)
python
from cocoindex.ops.text import RecursiveSplitter, detect_code_language

splitter = RecursiveSplitter()
language = detect_code_language(filename="example.py")
chunks = splitter.split(text, chunk_size=1000, chunk_overlap=200, language=language)

Embeddings

嵌入

python
from cocoindex.ops.sentence_transformers import SentenceTransformerEmbedder

embedder = SentenceTransformerEmbedder("sentence-transformers/all-MiniLM-L6-v2")
embedding = await embedder.embed(text)  # Returns NDArray
python
from cocoindex.ops.sentence_transformers import SentenceTransformerEmbedder

embedder = SentenceTransformerEmbedder("sentence-transformers/all-MiniLM-L6-v2")
embedding = await embedder.embed(text)  # 返回NDArray

CLI Commands

CLI命令

bash
cocoindex init my-project              # Create new project
cocoindex update main.py               # Run app
cocoindex update main.py:my_app        # Run specific app
cocoindex update main.py -L            # Run in live mode (continuous)
cocoindex update main.py --full-reprocess  # Reprocess everything
cocoindex drop main.py [-f]            # Drop and reset all state
cocoindex ls [main.py]                 # List apps
cocoindex show main.py [--tree]        # Show component paths
bash
cocoindex init my-project              # 创建新项目
cocoindex update main.py               # 运行App
cocoindex update main.py:my_app        # 运行指定App
cocoindex update main.py -L            # 以实时模式运行(持续执行)
cocoindex update main.py --full-reprocess  # 重新处理所有内容
cocoindex drop main.py [-f]            # 删除并重置所有状态
cocoindex ls [main.py]                 # 列出App
cocoindex show main.py [--tree]        # 显示组件路径

Best Practices

最佳实践

1. Use
@coco.fn
on All Processing Functions

1. 为所有处理函数使用
@coco.fn

Every function that participates in the pipeline (declares target states, calls mount APIs, etc.) must be decorated with
@coco.fn
.
所有参与管道的函数(声明目标状态、调用挂载API等)都必须用
@coco.fn
装饰。

2. Add Memoization for Expensive Operations

2. 为昂贵操作添加记忆化

python
@coco.fn(memo=True)  # Skip re-execution when inputs/code unchanged
async def process_chunk(chunk, table):
    embedding = await embedder.embed(chunk.text)  # Expensive!
    table.declare_row(...)
python
@coco.fn(memo=True)  # 输入/代码未变更时跳过重新执行
async def process_chunk(chunk, table):
    embedding = await embedder.embed(chunk.text)  # 操作昂贵!
    table.declare_row(...)

3. Use Stable Component Paths

3. 使用稳定的组件路径

python
undefined
python
undefined

Good: Stable identifiers

推荐:稳定标识符

coco.component_subpath("file", str(file.file_path.path)) coco.component_subpath("record", record.id)
coco.component_subpath("file", str(file.file_path.path)) coco.component_subpath("record", record.id)

Bad: Unstable identifiers

不推荐:不稳定标识符

coco.component_subpath("file", file) # Object reference coco.component_subpath("idx", idx) # Index changes
undefined
coco.component_subpath("file", file) # 对象引用 coco.component_subpath("idx", idx) # 索引会变化
undefined

4. Use Context for Shared Resources

4. 使用上下文共享资源

python
@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)
        yield
python
@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)
        yield

5. Use
Annotated[NDArray, CONTEXT_KEY]
for Vectors

5. 为向量使用
Annotated[NDArray, CONTEXT_KEY]

python
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")

@dataclass
class Record:
    vector: Annotated[NDArray, EMBEDDER]  # Auto-infer dimensions from ContextKey
python
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")

@dataclass
class Record:
    vector: Annotated[NDArray, EMBEDDER]  # 从ContextKey自动推断维度

6. Use Convenience APIs for Targets

6. 为目标使用便捷API

python
undefined
python
undefined

Mount table target -- subpath is automatic

挂载表目标——子路径自动生成

table = await postgres.mount_table_target( PG_DB, table_name="my_table", table_schema=await postgres.TableSchema.from_class(MyRecord, primary_key=["id"]), )
undefined
table = await postgres.mount_table_target( PG_DB, table_name="my_table", table_schema=await postgres.TableSchema.from_class(MyRecord, primary_key=["id"]), )
undefined

Troubleshooting

故障排除

Everything Reprocessing

所有内容都在重新处理

Add
memo=True
to expensive functions:
python
@coco.fn(memo=True)  # Add this
async def process_item(item):
    ...
为昂贵函数添加
memo=True
python
@coco.fn(memo=True)  # 添加此参数
async def process_item(item):
    ...

Memoization Not Working

记忆化不生效

Check component paths are stable. Use stable IDs, not object references.
检查组件路径是否稳定。使用稳定ID,而非对象引用。

Resources

资源

references/

references/

  • api_reference.md: Quick API reference
  • connectors.md: Complete connector reference
  • patterns.md: Detailed pipeline patterns
  • setup_project.md: Project setup guide
  • setup_database.md: Database setup guide
  • api_reference.md: 快速API参考
  • connectors.md: 完整连接器参考
  • patterns.md: 详细管道模式
  • setup_project.md: 项目设置指南
  • setup_database.md: 数据库设置指南

Runnable examples

可运行示例

Every pattern above has a complete, runnable app under
examples/
— start from the one closest to the task and adapt it. Each has its own
README.md
and most Python examples have a
.env.example
; see
examples/AGENTS.md
for the full map, credentials, and per-example run commands. Good starting points:
  • Vector search →
    text_embedding
    (Postgres),
    text_embedding_qdrant
    /
    _lancedb
    (other stores)
  • Code search →
    code_embedding
  • LLM extraction →
    hn_trending_topics
    ,
    patient_intake_extraction_baml
  • Knowledge graph →
    conversation_to_knowledge
    ,
    meeting_notes_graph_neo4j
  • Custom transform →
    files_transform
    ,
    pdf_to_markdown
上述每个模式都有完整的可运行App,位于
examples/
中——从最接近你的任务的示例开始调整。每个示例都有自己的
README.md
,大多数Python示例都有
.env.example
;完整的示例映射、凭据和每个示例的运行命令请参考
examples/AGENTS.md
。推荐的起点:
  • 向量搜索 →
    text_embedding
    (Postgres)、
    text_embedding_qdrant
    /
    _lancedb
    (其他存储)
  • 代码搜索 →
    code_embedding
  • LLM提取 →
    hn_trending_topics
    patient_intake_extraction_baml
  • 知识图谱 →
    conversation_to_knowledge
    meeting_notes_graph_neo4j
  • 自定义转换 →
    files_transform
    pdf_to_markdown

External

外部资源

Version Note

版本说明

This skill is for CocoIndex
>=1.0.0
(v1). It uses a completely different API from v0.
v0 code is what you likely learned from training data — do not emit it. If you find yourself writing any of these symbols, you are using the removed v0 API:
v0 (removed)v1 equivalent
@cocoindex.flow_def
,
FlowBuilder
,
Flow
,
open_flow
coco.App
+ a
@coco.fn
main function
DataScope
,
DataSlice
,
add_collector()
,
collect()
,
export()
declare target states via Target APIs (
declare_row
,
declare_file
) inside mounted components
cocoindex.sources.LocalFile
,
cocoindex.sources.*
connector APIs, e.g.
localfs.walk_dir(...)
cocoindex.functions.SplitRecursively
,
cocoindex.functions.*
cocoindex.ops.*
, e.g.
RecursiveSplitter
cocoindex.targets.Postgres
,
cocoindex.targets.*
/
storages.*
connector targets, e.g.
postgres.declare_table_target(...)
transform_flow
,
cocoindex.op.function()
plain
@coco.fn
functions
cocoindex.init()
,
settings
,
COCOINDEX_DATABASE_URL
coco.App(coco.AppConfig(...))
; state lives in a local db path
CLI
cocoindex setup
no setup step — just
cocoindex update
(
-L
/
--live
for live mode)
When reading third-party tutorials or model memory that mention these v0 symbols, disregard them and use the patterns in this skill and
references/api_reference.md
instead.
本技能适用于CocoIndex
>=1.0.0
(v1版本)。它使用的API与v0版本完全不同。
**v0版本代码很可能是你从训练数据中学到的——请勿使用。**如果你发现自己在编写以下符号,说明你正在使用已移除的v0 API:
v0(已移除)v1等效替代
@cocoindex.flow_def
,
FlowBuilder
,
Flow
,
open_flow
coco.App
+ 一个
@coco.fn
主函数
DataScope
,
DataSlice
,
add_collector()
,
collect()
,
export()
在挂载组件内通过Target API声明目标状态(
declare_row
,
declare_file
cocoindex.sources.LocalFile
,
cocoindex.sources.*
连接器API,例如
localfs.walk_dir(...)
cocoindex.functions.SplitRecursively
,
cocoindex.functions.*
cocoindex.ops.*
,例如
RecursiveSplitter
cocoindex.targets.Postgres
,
cocoindex.targets.*
/
storages.*
连接器目标,例如
postgres.declare_table_target(...)
transform_flow
,
cocoindex.op.function()
普通
@coco.fn
函数
cocoindex.init()
,
settings
,
COCOINDEX_DATABASE_URL
coco.App(coco.AppConfig(...))
;状态存储在本地数据库路径中
CLI命令
cocoindex setup
无需设置步骤——直接使用
cocoindex update
(实时模式使用
-L
/
--live
当阅读第三方教程或模型记忆中提到这些v0符号时,请忽略它们,改用本技能和
references/api_reference.md
中的模式。