cocoindex

cocoindex-ioApache-2.0活跃维护

面向长周期智能体的增量数据索引引擎,用 Rust 实现,支持变更数据捕获和 ETL,让代码库与数据源持续保持可查询的最新状态。

Incremental engine for long horizon agents 🌟 Star if you like it!

v1.2.0发布于 2026-11-09 19:22 UTC11,871star +39 / 7 天930fork同步于 2026-11-10 17:28 UTC

怎么上手

由 README 提炼,执行前请确认
pip install -U cocoindex
硬件要求按 README 说明

README 未说明

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

用声明式 Python 定义数据流,只重算变化部分,为 AI 智能体持续提供新鲜上下文

面向长周期智能体的增量数据索引引擎,用 Rust 实现,支持变更数据捕获和 ETL,让代码库与数据源持续保持可查询的最新状态。

核心能力

  • 声明式定义目标表内容,引擎自动保持同步
  • 按输入与代码哈希做 memo 缓存,只重算增量
  • 内置 RecursiveSplitter 文本切块与向量索引声明
  • 默认并行处理,支持任意规模数据源
  • 提供 localfs、postgres 等连接器挂载源与目标

容易踩的坑

示例依赖 Postgres 目标表,需自行准备数据库连接;向量化需自行提供 embed 实现;Python 版本需在 3.10–3.13 之间

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v1.2.0
  1. v1.2.0最新正式版2026-11-09 · 2026-11-09 19:22 UTC

    What's Changed

    Discord source connector

    cocoindex.connectors.discord indexes the messages and threads of Discord text, announcement and forum channels, using the token of a bot you create and invite to your server (pip install cocoindex[discord]): DiscordChannel(app=DiscordApp(token=...), channel_id=...).

    • DiscordChannel.items() yields one DiscordMessage per message in the channel and one DiscordThread per thread, active or archived, including forum posts. Both have read_text() for embedding
    • Each run lists the channel, and only the messages and threads that changed are reprocessed; a thread's messages are fetched only when the thread reruns. message_fields and thread_fields choose which fields are exposed, and so what counts as a change
    • Misconfigurations that Discord answers with an empty or text-less success (the Message Content intent off, no Read Message History) and lost access raise an error, so the channel keeps its outputs instead of having them deleted
    • Two examples: semantic search over a channel (discord_text_embedding), and support threads turned into an FAQ knowledge base (discord_support_faq)

    by @jerryqu-coco in https://github.com/cocoindex-io/cocoindex/pull/2695

    Google Drive: sign in with a personal Google account (experimental)

    Beside a service account, the Google Drive connector can read a Drive as a personal Google account signed in once through OAuth: GoogleDriveSource(root_folder_ids=[...], user_account=google_drive.UserAccount()). Sign in with cocoindex auth sign-in google-drive <client_secret.json> (sign out with cocoindex auth sign-out google-drive), or call UserAccount().sign_in(...) from your own program. The client file comes from a Desktop app OAuth client you create in your own Google Cloud project; the connector docs walk through it. A host with no browser prints the link and takes the pasted redirect address. The sign-in asks for read-only Drive access and is stored only on your computer; the connector docs describe the token file and how to revoke it. The service-account path is unchanged by @georgeh0 in https://github.com/cocoindex-io/cocoindex/pull/2687, https://github.com/cocoindex-io/cocoindex/pull/2689, https://github.com/cocoindex-io/cocoindex/pull/2791 and https://github.com/cocoindex-io/cocoindex/pull/2792

    Data dependencies for memoized functions and components (experimental)

    A calmer, clearer progress display

    cocoindex update, cocoindex drop and update_blocking(report_to_stdout=True) show one status line (Running…, ● Watching for changes, ✓ Done in 3.0s), with each component in an aligned column showing a progress bar, its rate and its failures. The summary lists the first failures under their component, and the final status counts failures inside stats groups too. Every f

  2. v1.1.12026-11-05 · 2026-11-05 07:00 UTC
    查看更新说明

    What's Changed

    New Contributors

    Full Changelog: https://github.com/cocoindex-io/cocoindex/compare/v1.1.0...v1.1.1

  3. v1.1.02026-11-01 · 2026-11-01 18:15 UTC
    查看更新说明

    What's Changed

    Connector key fixes: no more collisions or lost writes

    Every source item and target state has a key. CocoIndex uses the key to match an object with its previous run, and to decide what to create, update or delete. Prompted by the Google Drive fix in #2485, this release audits how every Python connector builds its keys. It fixes each case where the connector gave two different objects one key, or where a key that changed for the same object made CocoIndex delete that object. Two keys you choose can also reach one object, such as overlapping directories or two ContextKeys for one database. CocoIndex can't detect that, so it stays your responsibility; the docs now point out where it can happen.

    Two different objects shared one key, so one overwrote or deleted the other:

    One object's key changed between runs, and the delete for the old key removed what the new key had just written:

    A delete missed its object, leaving stale data behind:

    • LanceDB: every row delete failed for a key column that needs quoting (doc-id), or that holds binary, fixed-size binary or the int64 minimum by @georgeh0 in https://github.com/cocoindex-io/cocoindex/pull/2600
    • Doris: keys containing \. Each update appended a duplicate row, removed items stayed, and a trailing \ broke the statement by @georgeh0 in https://github.com/cocoindex-io/cocoindex/pull/2605
    • Neo4j and FalkorDB: an edge with an explicit id whose endpoints changed. Neo4j kept the old edge beside the new one, and FalkorDB failed with `unique constraint
  4. v1.0.262026-10-22 · 2026-10-22 05:15 UTC
    查看更新说明

    What's Changed

    • Security: exclude litellm 1.107.0, which was published to PyPI with a credential stealer and backdoor, from every litellm requirement (the litellm, entity_resolution_llm and all extras, and the examples). The locked version stays 1.96.2, which is unaffected by @georgeh0 in https://github.com/cocoindex-io/cocoindex/pull/2576
    • Preview works on real pipelines and writes nothing
      • Preview now handles target states under a container (rows in a Postgres / Qdrant / LanceDB table, and so on), so it no longer stops at once on most pipelines. A preview no longer stores, deletes or refreshes memos and no longer writes committed state, so it can't make the next real update skip work. Connector authors: TargetActionSink.from_fn(fn, child_handler=...) / from_async_fn(...) replace from_fn_with_children / from_async_fn_with_children, which now raise DeprecationWarning and don't support preview. Neo4j, FalkorDB and SurrealDB ConnectionFactory keep one connection per event loop and gain close() (neo4j, falkordb). FalkorDB now requires falkordb>=1.6.0 by @badmonster0 in https://github.com/cocoindex-io/cocoindex/pull/2510
    • feat(engine): max_inflight_components now bounds every running component. A parent lends its slot to one child at a time, so wide fan-outs stay within the limit and a hierarchy of any depth can't deadlock by @georgeh0 in https://github.com/cocoindex-io/cocoindex/pull/2486
    • feat(core): coco.logic_propagated_to_callers(fn) makes a callback's logic count toward its callers' memos, even when it is called through a library function that uses logic_tracking="self". Editing the callback now invalidates those memos by @georgeh0 in https://github.com/cocoindex-io/cocoindex/pull/2483 and https://github.com/cocoindex-io/cocoindex/pull/2529 (coco.logic_tracked remains as a deprecated alias)
    • feat(code): Nix syntax support for splitting and structural code patterns by @mulatta in https://github.com/cocoindex-io/cocoindex/pull/2434
    • Rust SDK fixes
      • Memoized calls join the enclosing caller's frame, so a memoized caller no longer returns stale values or deletes its children's outputs on a cache hit. Mounting inside #[function(memo)] is rejected, as in Python. use_mount! / mount_each! key by memo identity, so touching a file no longer re-runs every component. Connectors use one sink per handler instead of one apply per row by @badmonster0 in https://github.com/cocoindex-io/cocoindex/pull/2503
      • App::drop_state and normal updates now remove targets declared through root providers (directory files, Postgres/SQLite/LanceDB/Qdrant/… tables) when they're no longer declared. Breaking: the neo4j/falkordb table and relation target functions take &ContextKey<Graph> instead of &Graph by @badmonster0 in https://github.com/cocoindex-io/cocoindex/pull/2512
    • fix(graph): cocoindex drop and un-declaring a table delete the Neo4j/FalkorDB nodes and relationships the app wrote, and a primary-key change rebuilds the table instead of leaving old-key nodes behind by @georgeh0 in https://github.com/cocoindex-io/cocoindex/pull/2498
    • fix(neo4j): an apply batch retries on transient errors such as DeadlockDetected instead of failing the component by @georgeh0 in https://github.com/cocoindex-io/cocoindex/pull/2491
    • fix(ops): LiteLLMEmbedder embeds text starting with data:…;base64,, gs:// or files/ as text rather than as a file reference. A request litellm refuses to build no longer retries until the 10-minute deadline; the batch is split to isolate the bad text by @georgeh0 in https://github.com/cocoindex-io/cocoindex/pull/2532
    • fix(code_match): regex prefilters no longer drop nested alternatives, which made searches miss matches. regex_required_literals is now public by @georgeh0 in https://github.com/cocoindex-io/cocoindex/pull/2525
    • fix(fn): @coco.fn(batching=...) / runner=... are now type errors (use @coco.fn.as_async), and a lone max_batch_size= on
  5. v1.0.252026-10-06 · 2026-10-06 05:50 UTC
    查看更新说明

    What's Changed

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    stars downloads pypi python rust license discord

    CI release links

    Deutsch | English | Español | français | 日本語 | 한국어 | Português | Русский | 中文

    pip install -U cocoindex
    

    Declare what should be in your target — CocoIndex keeps it in sync forever, recomputing only the Δ.

    import cocoindex as coco
    from cocoindex.connectors import localfs, postgres
    from cocoindex.ops.text import RecursiveSplitter
    
    @coco.fn(memo=True)                          # ← cached by hash(input) + hash(code)
    async def index_file(file, table):
        for chunk in RecursiveSplitter().split(await file.read_text()):
            table.declare_row(text=chunk.text, embedding=embed(chunk.text))
    
    @coco.fn
    async def main(src):
        table = await postgres.mount_table_target(PG, table_name="docs")
        table.declare_vector_index(column="embedding")
        await coco.mount_each(index_file, localfs.walk_dir(src).items(), table)
    
    coco.App(coco.AppConfig(name="docs"), main, src="./docs").update_blocking()