Semantic search over a Markdown knowledge base, served over MCP.
この記録までは、1 つの定数 MAX_RAW_BINARY_BYTES (50 MiB、grooveseek/src/parser/mod.rs)
が 6 つのことを同時に決めていた: indexer がバイナリファイルを読む前に skip するサイズ、
読む handle での同じ判定、.xlsx の展開 pre-flight の budget、.docx / .pptx のパート
読み出しの累積 budget、PDF 1 本のテキスト budget、そして get_document / resources/read
のバイナリ上限である。MAX_RAW_TEXT_BYTES は最初の 2 つを Markdown とプレーンテキストに
ついて持っていた。
300 MB の workbook は普通の業務に存在し、warning 付きで skip されていた。上限を全員に
対して上げるだけでは済まない: rebuild_index はクライアントがいつでも呼べる MCP ツールで、
ファイルは parse の間まるごとメモリに持たれ、calamine は .xlsx の共有文字列表も丸ごと
メモリに載せ、しかも確保に失敗すると process が abort する。abort は catch_unwind でも
別スレッドでの parse でも止まらない。
問いは、設定で求めていない構成には何も変えずに、運用者がそうしたファイルを分かって 受け入れられるようにする方法である。
0。却下: 製品ごとに意味が違う。Recoll は filtermaxseconds では無制限、
compressedfilemaxkbs では「全拒否」と読み、Zoekt は「既定を使う」と読み、Open WebUI は
未設定または 0 の RAG_FILE_MAX_SIZE を、同じ製品の中でアップロードでは無制限、
アーカイブ展開では 100 MB として扱う。この repository は既に
[embedding].max_input_chars = 0 を拒否している。-1。最も多い選択 (Elasticsearch indexed_chars、Apache Tika、Solr)。却下: TOML 上で
負のバイト数は意図として読めず、「では -2 は?」を招く。"unlimited"。 採用。0 と負値はエラーにし、メッセージでこれを案内する。max_binary_file_size = "300 MiB" でも 300 MB の workbook は展開段で skip され、
通す方法が "unlimited" しか残らず、それは全ファイルから zip-bomb の検査を外す。"unlimited" の時だけ連動させる。却下: 「無制限」が 2 つ目の防御を黙って外す。max_decompressed_size、既定 50 MiB、raw の上限と非連動。 採用。get_document / resources/read を索引の上限に連動させる。却下: MCP の 1 リクエストが
300 MB のファイルをメモリに持つことになる。uri を持たない。キーの置き場所。 [limits] の新設は却下した。[index] が既に groove index と
rebuild_index の設定を持ち、上限は索引の性質そのものである。
.txt 1 本が
巨大な 1 chunk になり得る。[index] に max_binary_file_size / max_text_file_size / max_decompressed_size を
置く。値は 1 以上のバイト数、単位付きの大きさ (B / KB / MB / GB / TB は 1000 進、
KiB / MiB / GiB / TiB は 1024 進、大文字小文字を区別しない、小数は不可)、または
"unlimited" (大文字小文字を区別しない)。既定はどれも 50 MiB。0、負値、小数、未知の
単位、それ以外の語は、キー名を含むエラーになる。Registry::limits)。indexer はすべての読み出し経路
(フル run、watcher の作成・変更、rename) で raw の上限をそこから読み、バイナリ parser は
構築時に展開 budget を受け取り (with_budget)、最も深い読み出しまで渡す。上限固定の
helper は test 専用として残すので、本番の経路が 50 MiB でもう一度判定することは無い。max_decompressed_size を名指しする。.docx / .pptx の
パート 1 つが budget を超えて読み飛ばされる時の警告 (以前は何も言わずに捨てていた) も
同じく名指しする。raw の上限による skip はこれまでどおり file too large と言う。
stderr でどちらのキーかが区別できる。GET_DOCUMENT_BINARY_MAX_BYTES で持ち、同じ数が別の
問いに答えていることを、索引の既定値と取り違えさせない。読み出しは展開 budget も
max_decompressed_size に関わらず組み込みの値のまま: get_document と resources/read は
parse_bytes_for_read を通して parse し、4 つのバイナリ parser はそこで構築時の budget では
なく既定の budget を使うので、MCP の 1 リクエストはこれまでどおり bound される。"unlimited" ではメモリ
使用量は KB に置かれたファイルだけで決まる。--config で名指しすれば受け入れる。max_binary_file_size がファイルを、
max_decompressed_size がその展開後を受け入れる。2 つ目に書く値は運用者の workbook で
測るもので、ここでは分からない。groove doctor が黄色のままになる。読み出し上限を超えて
索引された文書は larger-than-a-read-returns (Warning、exit 1) として報告される — 1 MiB を
超えるテキストが既に受けているのと同じ扱い。max_decompressed_size を上げると、バイナリ文書はすべて提示から外れる。 読み出しは
組み込みの budget で parse し、索引が記録するのは raw の大きさだけで、どのバイナリ文書が
budget を超えて展開するかは分からない。そのためキーが既定を超えている間、バイナリの hit は
uri を持たず、resources/list も提示しない。検索には出続け、get_document は budget を
超えて展開する文書を展開の文言で拒否するか、単独で budget を超えるパートを欠いて返す。
テキストには影響しない。外れたバイナリ文書は groove doctor が binary-uris-withheld
(Warning、exit 1) として名指しするので、こちらも上げた設定では黄色のままになる。展開後の大きさを索引時に記録すれば、収まるバイナリ文書だけを
提示できる — これは未着手。groove index --force を打つか、
ファイルが上限の下まで縮むまで。scan の size skip は既存の行を消さずに保護するため。
上限を超えたままの編集では古いテキストが検索に出続ける: watcher は新しい大きさだけを記録し、
次の scan もそのファイルをまた skip する。[index] に
加えられる)、ファイルを buffer ではなく handle から parse するようになった時 (警告の
「まるごとメモリに持つ」が嘘になる)、展開 budget を比率に移す時。grooveseek/src/parser/mod.rs (値の文法と警告)、4 つの parser module と
registry.rs (budget がそれぞれの読み出しに届く)、grooveseek/src/config.rs (キー、
registry の両分岐、信頼しない設定の規則)、grooveseek/src/indexer.rs と
grooveseek/tests/index_size_caps.rs (3 つの読み出し経路)、grooveseek/src/server.rs
(読み出し側は動かない。raw の上限も展開 budget も)。