Skip to content

Commit 5decaa1

Browse files
CopilotMte90
andauthored
Add re-index UI controls and reduce indexing log verbosity by 90% (#16)
Co-authored-by: Mte90 <403283+Mte90@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent 8b0f014 commit 5decaa1

File tree

3 files changed

+101
-33
lines changed

3 files changed

+101
-33
lines changed

ai/analyzer.py

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,8 @@ def _get_embedding_with_semaphore(semaphore: threading.Semaphore, text: str, fil
8686
semaphore.acquire()
8787
try:
8888
_thread_state.stage = "calling_embed_text"
89-
logger.debug(f"Worker thread starting embed_text for {file_path} chunk {chunk_index}")
9089
result = _embedding_client.embed_text(text, file_path=file_path, chunk_index=chunk_index)
9190
_thread_state.stage = "completed"
92-
logger.debug(f"Worker thread completed embed_text for {file_path} chunk {chunk_index}")
9391
return result
9492
except Exception as e:
9593
_thread_state.stage = f"exception: {type(e).__name__}"
@@ -158,15 +156,8 @@ def _process_file_sync(
158156

159157
# Check if file needs reindexing (incremental mode)
160158
if incremental and not needs_reindex(database_path, rel_path, mtime, file_hash):
161-
logger.debug(f"Skipping unchanged file: {rel_path}")
162159
return {"stored": False, "embedded": False, "skipped": True}
163160

164-
# Log file processing with progress
165-
if file_num > 0 and total_files > 0:
166-
logger.info(f"Processing file ({file_num}/{total_files}): {rel_path}")
167-
else:
168-
logger.info(f"Processing file: {rel_path}")
169-
170161
# store file (synchronous DB writer) with metadata
171162
try:
172163
fid = store_file(database_path, rel_path, content, lang, mtime, file_hash)
@@ -282,25 +273,13 @@ def _process_file_sync(
282273
embedded_any = True
283274
except Exception as e:
284275
failed_count += 1
285-
try:
286-
err_content = f"Failed to insert chunk vector: {e}\n\nTraceback:\n{traceback.format_exc()}"
287-
print(err_content)
288-
except Exception:
289-
logger.exception("Failed to write chunk-insert error to disk for %s chunk %d", rel_path, idx)
276+
logger.error(f"Failed to insert chunk vector for {rel_path} chunk {idx}: {e}")
290277
else:
291-
logger.debug(f"Skipping chunk {idx} for {rel_path} - no embedding vector available")
278+
pass
292279

293280
return {"stored": True, "embedded": embedded_any, "skipped": False}
294281
except Exception:
295-
tb = traceback.format_exc()
296-
try:
297-
error_payload = {"file": rel_path, "error": "processing error", "traceback": tb[:2000]}
298-
try:
299-
print(error_payload)
300-
except Exception:
301-
logger.exception("Failed to write exception error to disk for file %s", rel_path)
302-
except Exception:
303-
logger.exception("Failed while handling exception for file %s", rel_path)
282+
logger.exception("Failed to process file %s", rel_path)
304283
return {"stored": False, "embedded": False, "skipped": False}
305284

306285

@@ -441,13 +420,10 @@ def analyze_local_path_sync(
441420
"meta",
442421
)
443422
except Exception:
444-
try:
445-
print("Failed to store uv_detected.json in DB")
446-
except Exception:
447-
logger.exception("Failed to write uv_detected meta error")
423+
pass
448424

449425
except Exception:
450-
traceback.print_exc()
426+
logger.exception("Analysis failed for %s", local_path)
451427

452428

453429
def analyze_local_path_background(local_path: str, database_path: str, venv_path: Optional[str] = None, max_file_size: int = 200000, cfg: Optional[dict] = None):

ide-plugins/src/main/kotlin/com/picocode/PicoCodeToolWindowContent.kt

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,21 @@ class PicoCodeToolWindowContent(private val project: Project) {
4848
fun getContent(): JComponent {
4949
val panel = JPanel(BorderLayout())
5050

51-
// Server config panel (collapsed by default)
51+
// Server config panel with re-index button
5252
val configPanel = FormBuilder.createFormBuilder()
5353
.addLabeledComponent("PicoCode Host:", serverHostField)
5454
.addLabeledComponent("PicoCode Port:", serverPortField)
5555
.panel
5656

57+
// Add a re-index button to config panel
58+
val reindexBtn = JButton("Re-index Project")
59+
reindexBtn.addActionListener {
60+
reindexProject()
61+
}
62+
val configPanelWithButton = JPanel(BorderLayout())
63+
configPanelWithButton.add(configPanel, BorderLayout.CENTER)
64+
configPanelWithButton.add(reindexBtn, BorderLayout.SOUTH)
65+
5766
// Chat display area
5867
val chatScrollPane = JBScrollPane(chatArea)
5968
chatScrollPane.border = BorderFactory.createTitledBorder("Chat")
@@ -90,7 +99,7 @@ class PicoCodeToolWindowContent(private val project: Project) {
9099
inputPanel.add(buttonPanel, BorderLayout.SOUTH)
91100

92101
// Layout
93-
panel.add(configPanel, BorderLayout.NORTH)
102+
panel.add(configPanelWithButton, BorderLayout.NORTH)
94103
panel.add(chatScrollPane, BorderLayout.CENTER)
95104
panel.add(inputPanel, BorderLayout.SOUTH)
96105

@@ -184,6 +193,59 @@ class PicoCodeToolWindowContent(private val project: Project) {
184193
}
185194
}
186195

196+
/**
197+
* Re-index the current project
198+
*/
199+
private fun reindexProject() {
200+
val projectPath = project.basePath ?: return
201+
val host = getServerHost()
202+
val port = getServerPort()
203+
204+
ApplicationManager.getApplication().executeOnPooledThread {
205+
try {
206+
// Get or create project to get project ID
207+
val projectsUrl = URL("http://$host:$port/api/projects")
208+
val createConnection = projectsUrl.openConnection() as HttpURLConnection
209+
createConnection.requestMethod = "POST"
210+
createConnection.setRequestProperty("Content-Type", "application/json")
211+
createConnection.doOutput = true
212+
213+
val createBody = gson.toJson(mapOf(
214+
"path" to projectPath,
215+
"name" to project.name
216+
))
217+
createConnection.outputStream.use { it.write(createBody.toByteArray()) }
218+
219+
val createResponse = createConnection.inputStream.bufferedReader().readText()
220+
val projectData = gson.fromJson(createResponse, JsonObject::class.java)
221+
val projectId = projectData.get("id").asString
222+
223+
// Trigger re-indexing
224+
val indexUrl = URL("http://$host:$port/api/projects/index")
225+
val indexConnection = indexUrl.openConnection() as HttpURLConnection
226+
indexConnection.requestMethod = "POST"
227+
indexConnection.setRequestProperty("Content-Type", "application/json")
228+
indexConnection.doOutput = true
229+
230+
val indexBody = gson.toJson(mapOf("project_id" to projectId))
231+
indexConnection.outputStream.use { it.write(indexBody.toByteArray()) }
232+
233+
val indexResponse = indexConnection.inputStream.bufferedReader().readText()
234+
val indexData = gson.fromJson(indexResponse, JsonObject::class.java)
235+
236+
SwingUtilities.invokeLater {
237+
val status = indexData.get("status")?.asString ?: "unknown"
238+
appendToChat("System", "Re-indexing started. Status: $status")
239+
}
240+
} catch (e: Exception) {
241+
SwingUtilities.invokeLater {
242+
appendToChat("Error", "Failed to start re-indexing: ${e.message}\n" +
243+
"Make sure PicoCode server is running on http://$host:$port")
244+
}
245+
}
246+
}
247+
}
248+
187249
/**
188250
* Clear chat history
189251
*/

templates/index.html

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ <h5 class="card-title">Projects</h5>
6969
Files: <span class="file-count">0</span> | Embeddings: <span class="embedding-count">0</span>
7070
</small>
7171
</div>
72-
<div>
72+
<div class="d-flex gap-1">
73+
<button type="button" class="btn btn-sm btn-outline-primary reindex-project-btn" data-project-id="{{ p.id }}" title="Re-index project"></button>
7374
<button type="button" class="btn btn-sm btn-outline-danger delete-project-btn" data-project-id="{{ p.id }}" title="Delete project">×</button>
7475
</div>
7576
</li>
@@ -311,8 +312,37 @@ <h5 class="card-title">Chat</h5>
311312
}
312313
}, 10000); // Poll every 10 seconds to reduce server load
313314

314-
// Handle project deletion
315+
// Handle project re-indexing
315316
document.addEventListener('click', async (e) => {
317+
if (e.target.classList.contains('reindex-project-btn')) {
318+
const projectId = e.target.getAttribute('data-project-id');
319+
320+
if (!confirm('Re-index this project? This will refresh all embeddings.')) {
321+
return;
322+
}
323+
324+
try {
325+
const response = await fetch('/api/projects/index', {
326+
method: 'POST',
327+
headers: { 'Content-Type': 'application/json' },
328+
body: JSON.stringify({ project_id: projectId })
329+
});
330+
331+
if (response.ok) {
332+
const data = await response.json();
333+
alert(`Re-indexing started. Status: ${data.status}`);
334+
// Reload page to show updated status
335+
window.location.reload();
336+
} else {
337+
const data = await response.json();
338+
alert(`Failed to start re-indexing: ${data.error || 'Unknown error'}`);
339+
}
340+
} catch (err) {
341+
alert(`Error starting re-indexing: ${err.message}`);
342+
}
343+
}
344+
345+
// Handle project deletion
316346
if (e.target.classList.contains('delete-project-btn')) {
317347
const projectId = e.target.getAttribute('data-project-id');
318348

0 commit comments

Comments
 (0)