own-rag-cli 0.0.3-snapshot → 0.0.5-snapshot

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,15 +1,20 @@
1
+ # MCP binary checksum (SHA-256, payload without shebang): `1413af4d4c7d01d57ec5195ea0c5f704f9fefabeb641d2f216a042ec638c2b59`
2
+
1
3
  # own-rag
2
4
 
3
- Local RAG for codebases with ChromaDB + MCP, focused on simple setup and local control.
5
+ Local RAG for codebases with ChromaDB + MCP, focused on practical setup and lower LLM token waste.
4
6
 
5
7
  Language: English (default) | Portuguese: `README.pt-br.md`
6
8
 
7
9
  ## Why use own-rag
8
10
 
9
- - Search your codebase semantically from MCP clients (Claude/Cursor).
10
- - Keep data local by default.
11
- - Run on CPU (no GPU required).
12
- - Choose between safer memory usage (`autotune`) or higher throughput (`max-performance`).
11
+ Without local retrieval, an AI assistant often scans many files or asks you to paste large chunks of code, which increases token usage and slows iteration. With `own-rag`, your repository is indexed once and exposed via MCP tools, so the assistant can fetch only the most relevant files/chunks.
12
+
13
+ Example:
14
+ - Without RAG: asking "where is `MedicationController` used?" may trigger broad project reads.
15
+ - With MCP + own-rag: retrieval points directly to likely files first, so the assistant reads less and answers faster.
16
+
17
+ Immediate MCP targets are Claude Code and Cursor, but the server can be used by any MCP-capable client.
13
18
 
14
19
  ## What gets installed
15
20
 
@@ -56,6 +61,30 @@ rag remove # full local uninstall (double confirmation)
56
61
  rag remove --force # uninstall without confirmation prompts
57
62
  ```
58
63
 
64
+ ## Indexing from URL (HTTP/HTTPS)
65
+
66
+ `rag run` now accepts remote URLs in addition to local folders.
67
+
68
+ How it works:
69
+ - If you pass `http://` or `https://`, the wrapper downloads content to a temporary folder.
70
+ - If the downloaded file is a ZIP, it is extracted and the extracted folder is indexed.
71
+ - Text files (`.txt`, `.md`, and other non-binary text files) are indexable.
72
+ - Binary files are skipped by the indexer.
73
+ - After indexing finishes, temporary downloaded/extracted files are removed automatically.
74
+
75
+ Download tool behavior:
76
+ - Uses `curl` when available.
77
+ - If `curl` is missing, the wrapper attempts automatic installation:
78
+ - Linux: package manager (`apt`, `dnf`, `yum`, `pacman`, `zypper`, `apk`)
79
+ - macOS: Homebrew (`brew`)
80
+
81
+ Examples:
82
+
83
+ ```bash
84
+ rag run https://example.com/docs/guide.md
85
+ rag run https://example.com/snapshots/project-docs.zip
86
+ ```
87
+
59
88
  ## Configuration files and paths
60
89
 
61
90
  ### 1) Runtime config (CLI-level)
@@ -63,8 +92,8 @@ rag remove --force # uninstall without confirmation prompts
63
92
  Path: `~/.own-rag-cli.json`
64
93
 
65
94
  Purpose:
66
- - Stores Chroma endpoint (`scheme`, `host`, `port`).
67
- - Stores latest indexing batch fields (`indexing.embedding_batch_size`, `indexing.batch_count`).
95
+ - Stores Chroma endpoint and last known indexing batch values.
96
+ - Used as the simplest user-facing config to move between machines or switch endpoint.
68
97
 
69
98
  Example:
70
99
 
@@ -82,6 +111,18 @@ Example:
82
111
  }
83
112
  ```
84
113
 
114
+ Field meaning:
115
+ - `chroma.scheme`: `http` or `https`.
116
+ - `chroma.host`: Chroma host (`localhost`, IP, or DNS).
117
+ - `chroma.port`: Chroma TCP port.
118
+ - `indexing.embedding_batch_size`: batch size used by embedding encode.
119
+ - `indexing.batch_count`: mirrored batch value for explicit readability.
120
+
121
+ If you edit this file manually:
122
+ 1. Save the file.
123
+ 2. Restart tools that consume MCP (Claude/Cursor).
124
+ 3. If you changed model/chunk/batch strategy, run reindex (`rag run <project>` or `--only-index`) so stored vectors match new settings.
125
+
85
126
  ### 2) Indexer tuning config
86
127
 
87
128
  Path: `~/.cache/own-rag-cli/indexer_tuning.json`
@@ -100,11 +141,11 @@ Purpose:
100
141
 
101
142
  - Claude Code: `~/.claude.json`
102
143
  - Cursor: `~/.cursor/mcp.json`
103
- - Cursor (alt, Linux/macOS variants): `~/.config/Cursor/User/mcp.json` or `~/Library/Application Support/Cursor/User/mcp.json`
144
+ - Cursor (alt paths): `~/.config/Cursor/User/mcp.json` or `~/Library/Application Support/Cursor/User/mcp.json`
104
145
 
105
146
  ## Choosing the embedding model (practical guidance)
106
147
 
107
- These are practical memory guidelines for CPU usage. Real usage depends on project size, file size, and other running processes.
148
+ These are practical CPU-memory guidelines. Real usage depends on project size and concurrent processes.
108
149
 
109
150
  | Choice | Use case | Approx RAM target |
110
151
  |---|---|---|
@@ -113,70 +154,122 @@ These are practical memory guidelines for CPU usage. Real usage depends on proje
113
154
  | `hybrid` (`jina v2 + bge`) | two collections, broader retrieval strategy | 24-48 GiB |
114
155
 
115
156
  Notes:
116
- - Jina on CPU is heavy; with low free RAM/swap, you can see slowdowns or OOM (`exit 137`).
117
- - If your machine has limited memory, start with `bge`.
157
+ - Jina on CPU is heavy; with low free RAM/swap, slowdowns or OOM (`exit 137`) can happen.
158
+ - If your machine is memory-limited, start with `bge`.
118
159
 
119
160
  ## Performance profile
120
161
 
121
- During setup/indexing, choose one profile:
162
+ During setup/indexing:
122
163
 
123
164
  - `autotune` (recommended):
124
165
  - Runs a short local benchmark (`model.encode`) with `psutil` metrics.
125
- - Tries to keep memory in a safer range (roughly cost-benefit target).
166
+ - Targets cost-benefit (not too aggressive, not too conservative).
126
167
  - Automatically adjusts `MCP_EMBEDDING_BATCH_SIZE`, `MCP_CHUNK_SIZE`, `MCP_CHUNK_OVERLAP`.
127
168
 
128
169
  - `max-performance`:
129
170
  - Uses more aggressive throughput-oriented parameters.
130
- - Explicit warning is shown because memory can increase considerably.
171
+ - Shows explicit memory-risk warning.
131
172
 
132
173
  ## Chroma endpoint behavior
133
174
 
134
175
  - Default local endpoint is `http://localhost:8000`.
135
- - Setup checks if the selected port is already in use.
136
- - If `host` in `~/.own-rag-cli.json` is remote (not localhost/127.0.0.1/::1), local Docker startup is skipped.
176
+ - Setup checks if selected port is already in use.
177
+ - If `~/.own-rag-cli.json` points to a remote host, local Docker startup is skipped.
178
+
179
+ ## Backup/snapshot before reinstall or machine migration
180
+
181
+ Before running `rag remove`, formatting your machine, or moving to another machine, snapshot these files:
182
+
183
+ ```bash
184
+ mkdir -p "$HOME/own-rag-backup"
185
+ cp "$HOME/.own-rag-cli.json" "$HOME/own-rag-backup/" 2>/dev/null || true
186
+ cp "$HOME/.cache/own-rag-cli/indexer_tuning.json" "$HOME/own-rag-backup/" 2>/dev/null || true
187
+
188
+ tar -czf "$HOME/own-rag-backup/chromadb-ragdb-$(date +%Y%m%d-%H%M%S).tgz" -C "$HOME" .rag_db
189
+ ```
190
+
191
+ Restore on a new machine:
192
+ 1. Install `own-rag-cli`.
193
+ 2. Restore `~/.own-rag-cli.json` and optional tuning file.
194
+ 3. Extract `.rag_db` backup into `$HOME`.
195
+ 4. Start with `rag run /path/to/project --skip-index` and validate search.
196
+ 5. If model/chunk settings changed, reindex.
137
197
 
138
198
  ## Common flows
139
199
 
140
- ### Reindex only
200
+ ### Reindex only (`--only-index`)
141
201
 
142
202
  ```bash
143
203
  ./rag-setup.run /path/to/project --only-index
144
204
  ```
145
205
 
146
- ### Skip index step
206
+ Use when environment is already installed and you only changed project code/docs.
207
+
208
+ ### Skip index step (`--skip-index`)
147
209
 
148
210
  ```bash
149
211
  ./rag-setup.run /path/to/project --skip-index
150
212
  ```
151
213
 
152
- ### Change model later
214
+ Use when you want to refresh infra/config first (venv, Docker, MCP wiring), and index later.
215
+
216
+ ### Change model (`--change-model`)
153
217
 
154
218
  ```bash
155
219
  ./rag-setup.run --change-model /path/to/project
156
220
  ```
157
221
 
158
- This flow warns about resetting Chroma collections and requiring full reindex.
222
+ Use when switching embedding strategy (`jina`, `bge`, `hybrid`). This may reset collections and requires full reindex so embeddings remain consistent.
159
223
 
160
224
  ## Environment overrides
161
225
 
162
- Main overrides:
226
+ Where to override:
227
+ - Temporary for one shell/session: `export VAR=value` before `rag run`.
228
+ - Persistent for MCP runtime: set env in your MCP client config (`~/.claude.json`, Cursor `mcp.json`).
229
+ - Chroma endpoint can also be persisted in `~/.own-rag-cli.json`.
230
+
231
+ What each variable does:
163
232
 
164
233
  - `MCP_EMBEDDING_MODEL=jina|bge|hybrid`
234
+ - Selects embedding strategy.
235
+ - After changing: reindex required.
236
+
165
237
  - `MCP_JINA_QUANTIZATION=default|dynamic-int8`
238
+ - Jina CPU quantization mode.
239
+ - After changing: reindex recommended.
240
+
166
241
  - `MCP_PERF_PROFILE=autotune|max-performance`
242
+ - Chooses tuning strategy for chunk and batch decisions.
243
+ - After changing: rerun index setup to apply new tuning.
244
+
167
245
  - `MCP_EMBEDDING_BATCH_SIZE=<int>`
246
+ - Forces fixed embedding batch size (overrides autotune batch).
247
+ - After changing: restart MCP; reindex recommended for consistent throughput assumptions.
248
+
168
249
  - `MCP_CHUNK_SIZE=<int>`
250
+ - Sets chunk length for indexing.
251
+ - After changing: full reindex required.
252
+
169
253
  - `MCP_CHUNK_OVERLAP=<int>`
254
+ - Sets overlap between chunks.
255
+ - After changing: full reindex required.
256
+
170
257
  - `OWN_RAG_CLI_CONFIG_FILE=<path>`
258
+ - Moves runtime config location from default `~/.own-rag-cli.json`.
259
+ - After changing: restart setup/MCP tools.
260
+
171
261
  - `MCP_INDEXER_CONFIG_FILE=<path>`
262
+ - Moves tuning config location from default `~/.cache/own-rag-cli/indexer_tuning.json`.
263
+ - After changing: restart setup/MCP tools.
264
+
172
265
  - `MCP_CHROMA_SCHEME=http|https`
173
266
  - `MCP_CHROMA_HOST=<host>`
174
267
  - `MCP_CHROMA_PORT=<port>`
268
+ - Overrides Chroma endpoint.
269
+ - After changing: restart MCP clients; reindex only if switching to a fresh/empty database.
175
270
 
176
271
  ## Checksum verification (`.run`)
177
272
 
178
- Current checksum (documented in this repo) refers to payload hash.
179
-
180
273
  Manual verification (Linux/macOS):
181
274
 
182
275
  ```bash
package/README.pt-br.md CHANGED
@@ -1,15 +1,18 @@
1
1
  # own-rag
2
2
 
3
- RAG local para codebases com ChromaDB + MCP, focado em instalação simples e controle local.
3
+ RAG local para codebases com ChromaDB + MCP, focado em setup prático e menor desperdício de tokens no uso de IA.
4
4
 
5
5
  Idioma: Português (PT-BR) | English: `README.md`
6
6
 
7
7
  ## Por que usar o own-rag
8
8
 
9
- - Busca semântica no seu código via clientes MCP (Claude/Cursor).
10
- - Dados locais por padrão.
11
- - Execução em CPU (sem GPU obrigatória).
12
- - Escolha entre perfil mais seguro de memória (`autotune`) ou maior throughput (`max-performance`).
9
+ Sem recuperação local, a IA costuma ler muitos arquivos ou pedir grandes trechos de código, aumentando custo de tokens e tempo de resposta. Com `own-rag`, o repositório é indexado uma vez e exposto por ferramentas MCP, então a IA consulta primeiro os trechos mais relevantes.
10
+
11
+ Exemplo:
12
+ - Sem RAG: ao perguntar "onde `MedicationController` é usado?", a IA pode varrer boa parte do projeto.
13
+ - Com MCP + own-rag: a recuperação aponta rapidamente para os arquivos com maior probabilidade, reduzindo leitura desnecessária.
14
+
15
+ Os alvos imediatos por padrão são Claude Code e Cursor, mas o servidor pode ser usado por qualquer cliente compatível com MCP.
13
16
 
14
17
  ## O que é instalado
15
18
 
@@ -56,6 +59,30 @@ rag remove # desinstalação completa local (dupla confirma
56
59
  rag remove --force # desinstala sem prompts de confirmação
57
60
  ```
58
61
 
62
+ ## Indexacao a partir de URL (HTTP/HTTPS)
63
+
64
+ Agora o `rag run` aceita URL remota alem de pasta local.
65
+
66
+ Como funciona:
67
+ - Se voce passar `http://` ou `https://`, o wrapper baixa o conteudo para uma pasta temporaria.
68
+ - Se o arquivo baixado for ZIP, ele e descompactado e a pasta extraida e indexada.
69
+ - Arquivos de texto (`.txt`, `.md` e outros textos nao-binarios) podem ser indexados.
70
+ - Arquivos binarios sao ignorados pelo indexador.
71
+ - Ao terminar a indexacao, os arquivos temporarios baixados/descompactados sao removidos automaticamente.
72
+
73
+ Comportamento do downloader:
74
+ - Usa `curl` quando disponivel.
75
+ - Se `curl` nao existir, o wrapper tenta instalar automaticamente:
76
+ - Linux: gerenciador de pacotes (`apt`, `dnf`, `yum`, `pacman`, `zypper`, `apk`)
77
+ - macOS: Homebrew (`brew`)
78
+
79
+ Exemplos:
80
+
81
+ ```bash
82
+ rag run https://exemplo.com/docs/guia.md
83
+ rag run https://exemplo.com/snapshots/docs-projeto.zip
84
+ ```
85
+
59
86
  ## Arquivos e caminhos de configuração
60
87
 
61
88
  ### 1) Configuração de runtime (nível CLI)
@@ -63,8 +90,8 @@ rag remove --force # desinstala sem prompts de confirmação
63
90
  Caminho: `~/.own-rag-cli.json`
64
91
 
65
92
  Finalidade:
66
- - Armazena endpoint do Chroma (`scheme`, `host`, `port`).
67
- - Armazena campos de batch da indexação (`indexing.embedding_batch_size`, `indexing.batch_count`).
93
+ - Guarda endpoint do Chroma e os últimos valores de batch de indexação.
94
+ - É o arquivo mais simples para portar configuração entre máquinas.
68
95
 
69
96
  Exemplo:
70
97
 
@@ -82,6 +109,18 @@ Exemplo:
82
109
  }
83
110
  ```
84
111
 
112
+ Significado de cada variável:
113
+ - `chroma.scheme`: protocolo (`http` ou `https`).
114
+ - `chroma.host`: host do Chroma (`localhost`, IP ou DNS).
115
+ - `chroma.port`: porta TCP do Chroma.
116
+ - `indexing.embedding_batch_size`: tamanho do batch usado no encode de embeddings.
117
+ - `indexing.batch_count`: espelho explícito do valor de batch para leitura humana.
118
+
119
+ Se você editar esse arquivo manualmente:
120
+ 1. Salve o arquivo.
121
+ 2. Reinicie as ferramentas que usam MCP (Claude/Cursor).
122
+ 3. Se mudou estratégia de modelo/chunk/batch, rode reindexação (`rag run <projeto>` ou `--only-index`) para manter consistência dos vetores.
123
+
85
124
  ### 2) Configuração de tuning do indexador
86
125
 
87
126
  Caminho: `~/.cache/own-rag-cli/indexer_tuning.json`
@@ -100,11 +139,11 @@ Finalidade:
100
139
 
101
140
  - Claude Code: `~/.claude.json`
102
141
  - Cursor: `~/.cursor/mcp.json`
103
- - Cursor (variações Linux/macOS): `~/.config/Cursor/User/mcp.json` ou `~/Library/Application Support/Cursor/User/mcp.json`
142
+ - Cursor (caminhos alternativos): `~/.config/Cursor/User/mcp.json` ou `~/Library/Application Support/Cursor/User/mcp.json`
104
143
 
105
144
  ## Escolha do modelo de embeddings (guia prático)
106
145
 
107
- Valores abaixo são referência prática para CPU. O consumo real depende do tamanho do projeto, arquivos e processos concorrentes.
146
+ Valores abaixo são referência prática para CPU. O consumo real depende do tamanho do projeto e de processos concorrentes.
108
147
 
109
148
  | Opção | Quando usar | RAM desejada (aprox.) |
110
149
  |---|---|---|
@@ -118,60 +157,114 @@ Notas:
118
157
 
119
158
  ## Perfil de performance
120
159
 
121
- Durante setup/indexação, escolha:
160
+ Durante setup/indexação:
122
161
 
123
162
  - `autotune` (recomendado):
124
163
  - Executa micro-benchmark local (`model.encode`) com métricas `psutil`.
125
- - Busca faixa mais estável de memória (custo-benefício).
164
+ - Busca custo-benefício (nem agressivo, nem conservador).
126
165
  - Ajusta automaticamente `MCP_EMBEDDING_BATCH_SIZE`, `MCP_CHUNK_SIZE`, `MCP_CHUNK_OVERLAP`.
127
166
 
128
167
  - `max-performance`:
129
168
  - Usa parâmetros mais agressivos para throughput.
130
- - Exibe aviso explícito de maior consumo de memória.
169
+ - Exibe aviso explícito de risco de memória.
131
170
 
132
171
  ## Comportamento do endpoint Chroma
133
172
 
134
173
  - Endpoint local padrão: `http://localhost:8000`.
135
174
  - O setup verifica se a porta escolhida já está em uso.
136
- - Se `host` em `~/.own-rag-cli.json` for remoto (não localhost/127.0.0.1/::1), o setup não sobe Docker local.
175
+ - Se `~/.own-rag-cli.json` apontar para host remoto, o setup não sobe Docker local.
176
+
177
+ ## Backup/snapshot antes de remover, formatar ou migrar máquina
178
+
179
+ Antes de executar `rag remove`, formatar o computador, ou migrar para outra máquina, faça snapshot:
180
+
181
+ ```bash
182
+ mkdir -p "$HOME/own-rag-backup"
183
+ cp "$HOME/.own-rag-cli.json" "$HOME/own-rag-backup/" 2>/dev/null || true
184
+ cp "$HOME/.cache/own-rag-cli/indexer_tuning.json" "$HOME/own-rag-backup/" 2>/dev/null || true
185
+
186
+ tar -czf "$HOME/own-rag-backup/chromadb-ragdb-$(date +%Y%m%d-%H%M%S).tgz" -C "$HOME" .rag_db
187
+ ```
188
+
189
+ Restauração em nova máquina:
190
+ 1. Instale `own-rag-cli`.
191
+ 2. Restaure `~/.own-rag-cli.json` e, opcionalmente, o tuning.
192
+ 3. Extraia o backup de `.rag_db` em `$HOME`.
193
+ 4. Execute `rag run /caminho/do/projeto --skip-index` e valide a busca.
194
+ 5. Se mudou modelo/chunks, reindexe.
137
195
 
138
196
  ## Fluxos comuns
139
197
 
140
- ### Reindexar somente
198
+ ### Reindexar somente (`--only-index`)
141
199
 
142
200
  ```bash
143
201
  ./rag-setup.run /caminho/do/projeto --only-index
144
202
  ```
145
203
 
146
- ### Pular indexação
204
+ Use quando o ambiente já está instalado e você só alterou código/documentação do projeto.
205
+
206
+ ### Pular indexação (`--skip-index`)
147
207
 
148
208
  ```bash
149
209
  ./rag-setup.run /caminho/do/projeto --skip-index
150
210
  ```
151
211
 
152
- ### Trocar modelo depois
212
+ Use para atualizar infraestrutura/configuração primeiro (venv, Docker, MCP), deixando a indexação para depois.
213
+
214
+ ### Trocar modelo (`--change-model`)
153
215
 
154
216
  ```bash
155
217
  ./rag-setup.run --change-model /caminho/do/projeto
156
218
  ```
157
219
 
158
- Esse fluxo alerta sobre reset das coleções Chroma e reindexação completa.
220
+ Use ao mudar estratégia de embeddings (`jina`, `bge`, `hybrid`). Esse fluxo pode resetar coleções e exige reindexação completa para consistência.
159
221
 
160
- ## Variáveis de ambiente (override)
222
+ ## Environment overrides
161
223
 
162
- Principais variáveis:
224
+ Onde sobrescrever:
225
+ - Temporário por sessão: `export VAR=valor` antes do `rag run`.
226
+ - Persistente no runtime MCP: definir `env` no arquivo de configuração do MCP (`~/.claude.json`, `mcp.json` do Cursor).
227
+ - Endpoint Chroma também pode ser persistido em `~/.own-rag-cli.json`.
228
+
229
+ Para que serve cada variável:
163
230
 
164
231
  - `MCP_EMBEDDING_MODEL=jina|bge|hybrid`
232
+ - Seleciona estratégia de embedding.
233
+ - Ao alterar: reindexação obrigatória.
234
+
165
235
  - `MCP_JINA_QUANTIZATION=default|dynamic-int8`
236
+ - Define quantização do Jina em CPU.
237
+ - Ao alterar: reindexação recomendada.
238
+
166
239
  - `MCP_PERF_PROFILE=autotune|max-performance`
240
+ - Define estratégia de tuning para chunks e batch.
241
+ - Ao alterar: rode setup/indexador novamente para aplicar novo tuning.
242
+
167
243
  - `MCP_EMBEDDING_BATCH_SIZE=<int>`
244
+ - Força batch fixo (sobrescreve batch autotunado).
245
+ - Ao alterar: reinicie MCP; reindexação recomendada para consistência operacional.
246
+
168
247
  - `MCP_CHUNK_SIZE=<int>`
248
+ - Define tamanho dos chunks na indexação.
249
+ - Ao alterar: reindexação completa obrigatória.
250
+
169
251
  - `MCP_CHUNK_OVERLAP=<int>`
252
+ - Define sobreposição entre chunks.
253
+ - Ao alterar: reindexação completa obrigatória.
254
+
170
255
  - `OWN_RAG_CLI_CONFIG_FILE=<path>`
256
+ - Muda localização do arquivo runtime (padrão `~/.own-rag-cli.json`).
257
+ - Ao alterar: reinicie setup/ferramentas MCP.
258
+
171
259
  - `MCP_INDEXER_CONFIG_FILE=<path>`
260
+ - Muda localização do arquivo de tuning (padrão `~/.cache/own-rag-cli/indexer_tuning.json`).
261
+ - Ao alterar: reinicie setup/ferramentas MCP.
262
+
172
263
  - `MCP_CHROMA_SCHEME=http|https`
173
264
  - `MCP_CHROMA_HOST=<host>`
174
265
  - `MCP_CHROMA_PORT=<port>`
266
+ - Sobrescrevem endpoint do Chroma.
267
+ - Ao alterar: reinicie clientes MCP; reindexe somente se trocar para base vazia/nova.
175
268
 
176
269
  ## Verificação de checksum (`.run`)
177
270
 
@@ -194,7 +194,7 @@ IGNORED_EXTENSIONS = {
194
194
  ".mp4", ".mp3", ".wav", ".ogg", ".avi", ".mov",
195
195
  # Pacotes e compilados
196
196
  ".zip", ".tar", ".gz", ".rar", ".7z", ".jar", ".war", ".ear",
197
- ".pyc", ".pyo", ".so", ".dll", ".exe", ".bin",
197
+ ".pyc", ".pyo", ".so", ".dll", ".exe", ".bin", ".run",
198
198
  # Lockfiles e gerados
199
199
  ".lock", ".sum",
200
200
  # Banco de dados
@@ -1113,17 +1113,44 @@ def make_chunk_id(file_path: str, chunk_index: int) -> str:
1113
1113
  return hashlib.md5(raw.encode()).hexdigest()
1114
1114
 
1115
1115
 
1116
+ def _looks_binary_content(raw: bytes) -> bool:
1117
+ """Detecta conteúdo binário por heurística em amostra de bytes."""
1118
+ if not raw:
1119
+ return False
1120
+
1121
+ sample = raw[:4096]
1122
+ if b"\x00" in sample:
1123
+ return True
1124
+
1125
+ non_text_bytes = 0
1126
+ for byte in sample:
1127
+ if byte in (9, 10, 13): # \t \n \r
1128
+ continue
1129
+ if 32 <= byte <= 126: # ASCII imprimível
1130
+ continue
1131
+ if 160 <= byte <= 255: # Latin-1 estendido comum em texto
1132
+ continue
1133
+ non_text_bytes += 1
1134
+
1135
+ return (non_text_bytes / len(sample)) > 0.30
1136
+
1137
+
1116
1138
  def read_file_safe(filepath: Path) -> str | None:
1117
- """Lê um arquivo de texto, tentando múltiplos encodings."""
1139
+ """Lê um arquivo de texto, evitando binários e tentando múltiplos encodings."""
1140
+ try:
1141
+ raw = filepath.read_bytes()
1142
+ except OSError as e:
1143
+ print(f" [AVISO] Não foi possível ler {filepath}: {e}")
1144
+ return None
1145
+
1146
+ if _looks_binary_content(raw):
1147
+ return None
1148
+
1118
1149
  for encoding in ("utf-8", "latin-1", "cp1252"):
1119
1150
  try:
1120
- return filepath.read_text(encoding=encoding)
1151
+ return raw.decode(encoding)
1121
1152
  except UnicodeDecodeError:
1122
1153
  continue
1123
- except OSError as e:
1124
- print(f" [AVISO] Não foi possível ler {filepath}: {e}")
1125
- return None
1126
- # Se nenhum encoding funcionou, é provavelmente binário disfarçado
1127
1154
  return None
1128
1155
 
1129
1156
 
@@ -1170,34 +1197,110 @@ def index_file(
1170
1197
 
1171
1198
  relative_path = str(filepath.relative_to(root_path))
1172
1199
  inserted_chunks = 0
1200
+ skipped_chunks = 0
1201
+ stop_iteration_warnings = 0
1173
1202
  batch_ids: list[str] = []
1174
1203
  batch_docs: list[str] = []
1175
1204
  batch_metadatas: list[dict[str, object]] = []
1176
1205
 
1206
+ def _warn_stop_iteration(message: str) -> None:
1207
+ nonlocal stop_iteration_warnings
1208
+ if stop_iteration_warnings < 3:
1209
+ tqdm.write(message)
1210
+ stop_iteration_warnings += 1
1211
+
1212
+ def _to_embedding_rows(encoded_embeddings: object) -> list[list[float]]:
1213
+ if hasattr(encoded_embeddings, "tolist"):
1214
+ rows = encoded_embeddings.tolist()
1215
+ if isinstance(rows, list):
1216
+ if rows and isinstance(rows[0], (int, float)):
1217
+ return [list(rows)]
1218
+ return rows
1219
+ return [list(row) for row in encoded_embeddings] # type: ignore[arg-type]
1220
+
1177
1221
  def _flush_batch() -> None:
1178
- nonlocal inserted_chunks
1222
+ nonlocal inserted_chunks, skipped_chunks
1179
1223
  if not batch_ids:
1180
1224
  return
1181
1225
 
1182
- embeddings = model.encode(
1183
- batch_docs,
1184
- show_progress_bar=False,
1185
- batch_size=embedding_batch_size,
1186
- ).tolist()
1187
- collection.upsert(
1188
- ids=batch_ids,
1189
- embeddings=embeddings,
1190
- documents=batch_docs,
1191
- metadatas=batch_metadatas,
1192
- )
1193
- inserted_chunks += len(batch_ids)
1194
- del embeddings
1226
+ pending_ids = list(batch_ids)
1227
+ pending_docs = list(batch_docs)
1228
+ pending_metadatas = list(batch_metadatas)
1229
+
1230
+ try:
1231
+ encoded = model.encode(
1232
+ pending_docs,
1233
+ show_progress_bar=False,
1234
+ batch_size=embedding_batch_size,
1235
+ )
1236
+ embeddings = _to_embedding_rows(encoded)
1237
+ collection.upsert(
1238
+ ids=pending_ids,
1239
+ embeddings=embeddings,
1240
+ documents=pending_docs,
1241
+ metadatas=pending_metadatas,
1242
+ )
1243
+ inserted_chunks += len(pending_ids)
1244
+ del embeddings
1245
+ except StopIteration:
1246
+ _warn_stop_iteration(
1247
+ f" [AVISO] {filepath.name}: StopIteration no batch de embeddings; tentando fallback por chunk."
1248
+ )
1249
+ for chunk_id, chunk_doc, chunk_metadata in zip(pending_ids, pending_docs, pending_metadatas):
1250
+ candidate_doc = chunk_doc.strip()
1251
+ if not candidate_doc:
1252
+ skipped_chunks += 1
1253
+ continue
1254
+
1255
+ try:
1256
+ encoded_single = model.encode(
1257
+ [candidate_doc],
1258
+ show_progress_bar=False,
1259
+ batch_size=1,
1260
+ )
1261
+ single_embeddings = _to_embedding_rows(encoded_single)
1262
+ collection.upsert(
1263
+ ids=[chunk_id],
1264
+ embeddings=single_embeddings,
1265
+ documents=[candidate_doc],
1266
+ metadatas=[chunk_metadata],
1267
+ )
1268
+ inserted_chunks += 1
1269
+ del single_embeddings
1270
+ except StopIteration:
1271
+ compact_doc = " ".join(candidate_doc.split())
1272
+ if not compact_doc:
1273
+ skipped_chunks += 1
1274
+ continue
1275
+ try:
1276
+ encoded_single = model.encode(
1277
+ [compact_doc],
1278
+ show_progress_bar=False,
1279
+ batch_size=1,
1280
+ )
1281
+ single_embeddings = _to_embedding_rows(encoded_single)
1282
+ collection.upsert(
1283
+ ids=[chunk_id],
1284
+ embeddings=single_embeddings,
1285
+ documents=[compact_doc],
1286
+ metadatas=[chunk_metadata],
1287
+ )
1288
+ inserted_chunks += 1
1289
+ del single_embeddings
1290
+ except StopIteration:
1291
+ skipped_chunks += 1
1292
+ _warn_stop_iteration(
1293
+ f" [AVISO] {filepath.name}: chunk ignorado após StopIteration repetido."
1294
+ )
1195
1295
  batch_ids.clear()
1196
1296
  batch_docs.clear()
1197
1297
  batch_metadatas.clear()
1198
1298
  gc.collect()
1199
1299
 
1200
1300
  for i, chunk in enumerate(chunks):
1301
+ if not chunk or not chunk.strip():
1302
+ skipped_chunks += 1
1303
+ continue
1201
1304
  batch_ids.append(make_chunk_id(abs_path, i))
1202
1305
  batch_docs.append(chunk)
1203
1306
  batch_metadatas.append(
@@ -1213,6 +1316,10 @@ def index_file(
1213
1316
  _flush_batch()
1214
1317
 
1215
1318
  _flush_batch()
1319
+ if skipped_chunks:
1320
+ _warn_stop_iteration(
1321
+ f" [AVISO] {filepath.name}: {skipped_chunks} chunk(s) vazio(s)/inválido(s) foram ignorados."
1322
+ )
1216
1323
  return inserted_chunks
1217
1324
 
1218
1325