opencode-config/docs/decisions/032-pr-75-rename-openai-env-chunking.md
Sergey 80a4be1d21
refactor(memory): rename to memory, OpenAI env, chunking, batching, dedup (#75)
* refactor(memory): rename package second-brain to memory

* refactor(memory): use OpenAI env naming and fix trailing slash

* feat(memory): add chunking with env-configurable size and overlap

* feat(memory): dedup search results by source in top-K

* test(memory): add chunking, batching, dedup, live tests

* docs(memory): update README and project map after rename

* docs(handoff): add handoff and ADR-032 for memory refactor

* docs(handoff): set PR number

* fix(ci): reduce index.py complexity to rank A

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-07-26 16:13:05 +03:00

4.2 KiB
Raw Permalink Blame History

ADR-032: Rename to memory, OpenAI env, chunking, batching, dedup (PR #75)

Статус

Accepted (2026-07-26)

Контекст

Python-пакет src/memory/ (RAG CLI для opencode-memory plugin) имел 7 проблем: нейминг-путаница (3 разных имени для одного пакета), нестандартный env-нейминг (AI_PROVIDER_API_URLAI_PROVIDER_BASE_URL из .env.example/opencode.json), реальная 404-бага из-за trailing slash, грубый индекс (1 эмбеддинг на файл без чанков), отсутствие batching (риск упереться в OpenAI лимит input), отсутствие дедупа в search (засорение top-K одним файлом), захардкоженная модель эмбеддингов.

PR #1 в серии из 2 — этот PR чинит CLI, PR #2 интегрирует CLI в плагин через setup-memory.sh.

Решение

  1. Нейминг: pyproject.toml name=second-brainmemory, [project.scripts] rag = "src.memory.cli:main" (script entry point, совместимость с wrapper). cli.py prog="rag"prog="memory". Модуль src/memory/ НЕ переименован (src-layout, packages=["src"]).
  2. Env-нейминг OpenAI-стандарт: OPENAI_BASE_URL/OPENAI_API_KEY (вместо AI_PROVIDER_API_URL/AI_PROVIDER_API_KEY). API_URL.rstrip("/") фиксит trailing-slash. EMBEDDING_MODEL из env OPENAI_EMBEDDING_MODEL (default gemini-embedding-2-preview).
  3. Chunking: _chunk_text(text, size, overlap) в index.py, параметры из env MEMORY_CHUNK_SIZE (512)/MEMORY_CHUNK_OVERLAP (64). file_map с chunk_idx/offset/text. Graceful при size<=overlap (1 chunk, не infinite loop).
  4. Batching: BATCH_SIZE = int(os.environ.get("OPENAI_EMBEDDING_BATCH_SIZE", "2048")) в embedder.py. embed_texts режет texts > BATCH_SIZE, конкатенирует результаты. Default 2048 = OpenAI лимит для text-embedding-3-small.
  5. Дедуп: search.py после sort по score итерирует с seen set, оставляет highest score per source, останавливается на K уникальных source'ов.
  6. Тесты: test_chunking.py (7 edge cases), test_embedder_live.py (2 live, skip без RUN_LIVE), + 4 embedder теста (default/custom model, trailing slash, batches), + 3 index теста (long file, env override, .rag skip), + 1 search тест (dedup).

Альтернативы

  • Полный rename src/memory/src/rag/ — отклонено: src-layout packages=["src"] в pyproject, rename модуля требует обновления всех imports (cli, embedder, index, search, tests, main). Script entry point rag = "src.memory.cli:main" даёт исполняемый rag без rename модуля. Module name memory (что package делает) + script name rag (как пользователь вызывает) — разумное разделение.
  • Читать OPENAI_BASE_URL/EMBEDDING_MODEL/BATCH_SIZE в embed_texts каждый вызов (вместо module-level) — отклонено: лишний overhead на каждый вызов (env lookup + int parse), несовместимость с существующим API_URL module-level паттерном. Module-level + importlib.reload в тестах — trade-off: простой код, хрупкие reload-тесты.
  • Дропать empty files из индекса (вместо 1 записи с пустым чанком) — отклонено: меняет существующее поведение (раньше empty files попадали в индекс с 1 embedding). Empty embedding даёт score 0, файл остаётся в index для completeness.
  • Параметризовать _chunk_text через argparse вместо env — отклонено: env-параметры позволяют настраивать без правки CLI invocation, совместимо с setup-memory.sh wrapper (env передаётся через process env, не флаги).