cocoindex-code 0.2.40__tar.gz → 0.2.41__tar.gz

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.
Files changed (26) hide show
  1. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/PKG-INFO +1 -1
  2. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/_version.py +2 -2
  3. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/client.py +18 -8
  4. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/daemon.py +3 -2
  5. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/query.py +46 -13
  6. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/.gitignore +0 -0
  7. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/LICENSE +0 -0
  8. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/README.md +0 -0
  9. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/pyproject.toml +0 -0
  10. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/__init__.py +0 -0
  11. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/__main__.py +0 -0
  12. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/_daemon_paths.py +0 -0
  13. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/chunking.py +0 -0
  14. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/cli.py +0 -0
  15. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/embedder_defaults.py +0 -0
  16. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/embedder_params.py +0 -0
  17. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/file_walk.py +0 -0
  18. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/grep.py +0 -0
  19. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/indexer.py +0 -0
  20. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/litellm_embedder.py +0 -0
  21. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/project.py +0 -0
  22. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/protocol.py +0 -0
  23. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/schema.py +0 -0
  24. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/server.py +0 -0
  25. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/settings.py +0 -0
  26. {cocoindex_code-0.2.40 → cocoindex_code-0.2.41}/src/cocoindex_code/shared.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cocoindex-code
3
- Version: 0.2.40
3
+ Version: 0.2.41
4
4
  Summary: MCP server for indexing and querying codebases using CocoIndex
5
5
  Project-URL: Homepage, https://github.com/cocoindex-io/cocoindex-code
6
6
  Project-URL: Repository, https://github.com/cocoindex-io/cocoindex-code
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.2.40'
22
- __version_tuple__ = version_tuple = (0, 2, 40)
21
+ __version__ = version = '0.2.41'
22
+ __version_tuple__ = version_tuple = (0, 2, 41)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -228,6 +228,19 @@ def _handle_vanished_daemon() -> None:
228
228
  )
229
229
 
230
230
 
231
+ def _daemon_error(resp: ErrorResponse) -> RuntimeError:
232
+ """Build the exception for an ``ErrorResponse`` from the daemon.
233
+
234
+ The daemon-side traceback is appended when present: without it the caller
235
+ only ever sees the client's own re-raise frames, which says nothing about
236
+ where the failure actually happened (issue #270).
237
+ """
238
+ detail = f"Daemon error: {resp.message}"
239
+ if resp.traceback:
240
+ detail += f"\n{resp.traceback}"
241
+ return RuntimeError(detail)
242
+
243
+
231
244
  class _HandshakeResult(NamedTuple):
232
245
  conn: Connection
233
246
  resp: HandshakeResponse
@@ -261,7 +274,7 @@ def _raw_connect_and_handshake() -> _HandshakeResult:
261
274
  raise DaemonProtocolError(f"Undecodable handshake reply from daemon: {e}") from e
262
275
  if isinstance(resp, ErrorResponse):
263
276
  conn.close()
264
- raise RuntimeError(f"Daemon error: {resp.message}")
277
+ raise _daemon_error(resp)
265
278
  if not isinstance(resp, HandshakeResponse):
266
279
  conn.close()
267
280
  raise RuntimeError(f"Unexpected handshake response: {type(resp).__name__}")
@@ -340,7 +353,7 @@ def _send(req: Request) -> Response:
340
353
  conn.close()
341
354
  resp = decode_response(data)
342
355
  if isinstance(resp, ErrorResponse):
343
- raise RuntimeError(f"Daemon error: {resp.message}")
356
+ raise _daemon_error(resp)
344
357
  return resp
345
358
 
346
359
 
@@ -366,7 +379,7 @@ def index(
366
379
  raise RuntimeError("Connection to daemon lost during indexing")
367
380
  resp = decode_response(data)
368
381
  if isinstance(resp, ErrorResponse):
369
- raise RuntimeError(f"Daemon error: {resp.message}")
382
+ raise _daemon_error(resp)
370
383
  if isinstance(resp, IndexWaitingNotice):
371
384
  if on_waiting is not None:
372
385
  on_waiting()
@@ -419,7 +432,7 @@ def search(
419
432
  raise RuntimeError("Connection to daemon lost during search")
420
433
  resp = decode_response(data)
421
434
  if isinstance(resp, ErrorResponse):
422
- raise RuntimeError(f"Daemon error: {resp.message}")
435
+ raise _daemon_error(resp)
423
436
  if isinstance(resp, IndexWaitingNotice):
424
437
  if on_waiting is not None:
425
438
  on_waiting()
@@ -496,10 +509,7 @@ def doctor(
496
509
  raise RuntimeError("Connection to daemon lost during doctor checks")
497
510
  resp = decode_response(data)
498
511
  if isinstance(resp, ErrorResponse):
499
- detail = f"Daemon error: {resp.message}"
500
- if resp.traceback:
501
- detail += f"\n{resp.traceback}"
502
- raise RuntimeError(detail)
512
+ raise _daemon_error(resp)
503
513
  if isinstance(resp, DoctorResponse):
504
514
  results.append(resp.result)
505
515
  if on_result is not None:
@@ -346,7 +346,8 @@ async def _search_with_wait(
346
346
  offset=req.offset,
347
347
  )
348
348
  except Exception as e:
349
- yield ErrorResponse(message=str(e))
349
+ logger.exception("Error handling search request")
350
+ yield ErrorResponse(message=str(e), traceback=traceback.format_exc())
350
351
 
351
352
 
352
353
  async def _handle_doctor(
@@ -605,7 +606,7 @@ async def _dispatch(
605
606
  return ErrorResponse(message=f"Unknown request type: {type(req).__name__}")
606
607
  except Exception as e:
607
608
  logger.exception("Error dispatching request")
608
- return ErrorResponse(message=str(e))
609
+ return ErrorResponse(message=str(e), traceback=traceback.format_exc())
609
610
 
610
611
 
611
612
  # ---------------------------------------------------------------------------
@@ -16,6 +16,33 @@ def _l2_to_score(distance: float) -> float:
16
16
  return 1.0 - distance * distance / 2.0
17
17
 
18
18
 
19
+ def _checked(rows: list[tuple[Any, ...]], query_shape: str) -> list[tuple[Any, ...]]:
20
+ """Return *rows*, failing loudly if any of them carries a NULL distance.
21
+
22
+ ``code_chunks_vec`` is a vec0 virtual table whose ``distance`` is a *hidden*
23
+ column: sqlite-vec populates it only under the KNN query plan and yields
24
+ NULL for it on a plain full scan. A NULL therefore means the plan we asked
25
+ for is not the plan we got, which is a bug worth reporting rather than a
26
+ result worth ranking. Left unchecked, the NULL flows into ``_l2_to_score``
27
+ and surfaces as an unrelated-looking ``TypeError: unsupported operand
28
+ type(s) for *: 'NoneType' and 'NoneType'`` (issue #270).
29
+
30
+ Only KNN queries need this guard: ``_full_scan_query`` computes its own
31
+ ``vec_distance_L2(...)``, which works under any plan and raises (never
32
+ returns NULL) on bad input.
33
+ """
34
+ bad = next((row for row in rows if row[5] is None), None)
35
+ if bad is not None:
36
+ raise RuntimeError(
37
+ f"Vector index returned a row with no distance ({query_shape}, "
38
+ f"file_path={bad[0]!r}) — the sqlite-vec KNN query plan was not "
39
+ "used. Please report this at "
40
+ "https://github.com/cocoindex-io/cocoindex-code/issues along with "
41
+ "the output of `ccc doctor`."
42
+ )
43
+ return rows
44
+
45
+
19
46
  def _knn_query(
20
47
  conn: sqlite3.Connection,
21
48
  embedding_bytes: bytes,
@@ -24,24 +51,30 @@ def _knn_query(
24
51
  ) -> list[tuple[Any, ...]]:
25
52
  """Run a vec0 KNN query, optionally constrained to a language partition."""
26
53
  if language is not None:
27
- return conn.execute(
54
+ return _checked(
55
+ conn.execute(
56
+ """
57
+ SELECT file_path, language, content, start_line, end_line, distance
58
+ FROM code_chunks_vec
59
+ WHERE embedding MATCH ? AND k = ? AND language = ?
60
+ ORDER BY distance
61
+ """,
62
+ (embedding_bytes, k, language),
63
+ ).fetchall(),
64
+ f"knn language={language!r}",
65
+ )
66
+ return _checked(
67
+ conn.execute(
28
68
  """
29
69
  SELECT file_path, language, content, start_line, end_line, distance
30
70
  FROM code_chunks_vec
31
- WHERE embedding MATCH ? AND k = ? AND language = ?
71
+ WHERE embedding MATCH ? AND k = ?
32
72
  ORDER BY distance
33
73
  """,
34
- (embedding_bytes, k, language),
35
- ).fetchall()
36
- return conn.execute(
37
- """
38
- SELECT file_path, language, content, start_line, end_line, distance
39
- FROM code_chunks_vec
40
- WHERE embedding MATCH ? AND k = ?
41
- ORDER BY distance
42
- """,
43
- (embedding_bytes, k),
44
- ).fetchall()
74
+ (embedding_bytes, k),
75
+ ).fetchall(),
76
+ "knn unfiltered",
77
+ )
45
78
 
46
79
 
47
80
  def _full_scan_query(
File without changes