cocoindex-code 0.2.34__tar.gz → 0.2.35__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.
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/PKG-INFO +1 -1
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/_version.py +2 -2
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/cli.py +140 -47
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/client.py +28 -14
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/.gitignore +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/LICENSE +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/README.md +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/pyproject.toml +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/__init__.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/__main__.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/_daemon_paths.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/chunking.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/daemon.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/embedder_defaults.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/embedder_params.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/indexer.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/litellm_embedder.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/project.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/protocol.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/query.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/schema.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/server.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/src/cocoindex_code/settings.py +0 -0
- {cocoindex_code-0.2.34 → cocoindex_code-0.2.35}/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.
|
|
3
|
+
Version: 0.2.35
|
|
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.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
21
|
+
__version__ = version = '0.2.35'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 35)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -321,12 +321,35 @@ def remove_from_gitignore(project_root: Path) -> None:
|
|
|
321
321
|
_LITELLM_MODELS_URL = "https://docs.litellm.ai/docs/embedding/supported_embedding"
|
|
322
322
|
|
|
323
323
|
|
|
324
|
+
def _st_model_rejection_reason(model: str) -> str | None:
|
|
325
|
+
"""Why ``model`` can't be a sentence-transformers model, or None if it's fine.
|
|
326
|
+
|
|
327
|
+
sentence-transformers loads HuggingFace model ids. An ``ollama/`` prefix is a
|
|
328
|
+
LiteLLM/Ollama route that ST tries (and fails) to resolve as a HuggingFace
|
|
329
|
+
repo — the user wants the litellm provider instead (issue #181). Real
|
|
330
|
+
HuggingFace ids that contain an ``org/`` slash (``Snowflake/...``,
|
|
331
|
+
``openai/...``) are left alone.
|
|
332
|
+
"""
|
|
333
|
+
if model.strip().lower().startswith("ollama/"):
|
|
334
|
+
return (
|
|
335
|
+
"ollama/… models run via litellm, not sentence-transformers — "
|
|
336
|
+
"go back and pick the litellm provider instead."
|
|
337
|
+
)
|
|
338
|
+
return None
|
|
339
|
+
|
|
340
|
+
|
|
324
341
|
def _resolve_embedding_choice(
|
|
325
342
|
litellm_model_flag: str | None,
|
|
326
343
|
st_installed: bool,
|
|
327
344
|
tty: bool,
|
|
345
|
+
previous: EmbeddingSettings | None = None,
|
|
328
346
|
) -> EmbeddingSettings:
|
|
329
|
-
"""Resolve the embedding settings per the init control-flow diagram.
|
|
347
|
+
"""Resolve the embedding settings per the init control-flow diagram.
|
|
348
|
+
|
|
349
|
+
On a retry, ``previous`` holds the choice from the last attempt; its
|
|
350
|
+
provider and model become the prompt defaults so the user only edits
|
|
351
|
+
what was wrong instead of retyping everything.
|
|
352
|
+
"""
|
|
330
353
|
if litellm_model_flag is not None:
|
|
331
354
|
return EmbeddingSettings(provider="litellm", model=litellm_model_flag)
|
|
332
355
|
|
|
@@ -349,14 +372,15 @@ def _resolve_embedding_choice(
|
|
|
349
372
|
"Embedding provider",
|
|
350
373
|
choices=[
|
|
351
374
|
questionary.Choice(
|
|
352
|
-
title="sentence-transformers (local, free)",
|
|
375
|
+
title="sentence-transformers (local, free — built-in HuggingFace models)",
|
|
353
376
|
value="sentence-transformers",
|
|
354
377
|
),
|
|
355
378
|
questionary.Choice(
|
|
356
|
-
title="litellm (
|
|
379
|
+
title="litellm (100+ providers — cloud APIs & local Ollama)",
|
|
357
380
|
value="litellm",
|
|
358
381
|
),
|
|
359
382
|
],
|
|
383
|
+
default=previous.provider if previous is not None else None,
|
|
360
384
|
).ask()
|
|
361
385
|
else:
|
|
362
386
|
_typer.echo(
|
|
@@ -369,10 +393,16 @@ def _resolve_embedding_choice(
|
|
|
369
393
|
raise _typer.Exit(code=1)
|
|
370
394
|
|
|
371
395
|
if provider == "sentence-transformers":
|
|
372
|
-
|
|
396
|
+
default_model = previous.model if previous is not None else DEFAULT_ST_MODEL
|
|
397
|
+
model = questionary.text(
|
|
398
|
+
"Model name",
|
|
399
|
+
default=default_model,
|
|
400
|
+
validate=lambda m: _st_model_rejection_reason(m) or True,
|
|
401
|
+
).ask()
|
|
373
402
|
elif provider == "litellm":
|
|
374
403
|
_typer.echo(f"See supported LiteLLM embedding models: {_LITELLM_MODELS_URL}")
|
|
375
|
-
|
|
404
|
+
default_model = previous.model if previous is not None else ""
|
|
405
|
+
model = questionary.text("Model name", default=default_model).ask()
|
|
376
406
|
else:
|
|
377
407
|
_typer.echo(f"Error: unknown provider {provider!r}", err=True)
|
|
378
408
|
raise _typer.Exit(code=1)
|
|
@@ -392,13 +422,14 @@ def _ok_fail_tag(ok: bool) -> str:
|
|
|
392
422
|
return _click.style("[FAIL]", fg="red", bold=True)
|
|
393
423
|
|
|
394
424
|
|
|
395
|
-
def _run_init_model_check(
|
|
396
|
-
"""Ask the daemon to test the embedding model; print results
|
|
425
|
+
def _run_init_model_check() -> bool:
|
|
426
|
+
"""Ask the daemon to test the embedding model; print results. Return True if all pass.
|
|
397
427
|
|
|
398
428
|
Drives the check via `DoctorRequest(project_root=None)`. The daemon loads
|
|
399
429
|
the model once and stays running, so the user's next `ccc index` starts
|
|
400
430
|
warm. Both DaemonStartError and generic exceptions are rendered as a
|
|
401
|
-
synthetic failed DoctorCheckResult — uniform failure-output shape.
|
|
431
|
+
synthetic failed DoctorCheckResult — uniform failure-output shape. The
|
|
432
|
+
caller decides what to show on failure (retry prompt / next-steps block).
|
|
402
433
|
"""
|
|
403
434
|
from rich.console import Console as _Console
|
|
404
435
|
from rich.live import Live as _Live
|
|
@@ -426,55 +457,101 @@ def _run_init_model_check(settings_path: Path) -> None:
|
|
|
426
457
|
)
|
|
427
458
|
]
|
|
428
459
|
|
|
429
|
-
|
|
460
|
+
ok = True
|
|
430
461
|
for r in results:
|
|
431
462
|
if r.name == "done":
|
|
432
463
|
continue
|
|
433
|
-
_print_doctor_result(r)
|
|
464
|
+
_print_doctor_result(r, verbose=False)
|
|
434
465
|
if not r.ok:
|
|
435
|
-
|
|
466
|
+
ok = False
|
|
467
|
+
return ok
|
|
436
468
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
469
|
+
|
|
470
|
+
def _print_init_next_steps(settings_path: Path) -> None:
|
|
471
|
+
"""Prominent recovery block shown after a failed init model check."""
|
|
472
|
+
import click as _click
|
|
473
|
+
|
|
474
|
+
display_path = format_path_for_display(settings_path)
|
|
475
|
+
_typer.echo(err=True)
|
|
476
|
+
_typer.echo(_click.style(" Next steps", bold=True), err=True)
|
|
477
|
+
_typer.echo(_click.style(f" {'─' * 38}", fg="bright_black"), err=True)
|
|
478
|
+
_typer.echo(
|
|
479
|
+
f" 1. Edit {_click.style(display_path, fg='cyan', bold=True)}\n"
|
|
480
|
+
" to change the model or add API keys under `envs:`.",
|
|
481
|
+
err=True,
|
|
482
|
+
)
|
|
483
|
+
_typer.echo(" 2. Run `ccc doctor` to verify.", err=True)
|
|
484
|
+
_typer.echo() # trailing blank before whatever init prints next
|
|
444
485
|
|
|
445
486
|
|
|
446
487
|
def _setup_user_settings_interactive(litellm_model_flag: str | None) -> None:
|
|
447
|
-
"""Interactive global-settings setup — only runs when settings are missing.
|
|
488
|
+
"""Interactive global-settings setup — only runs when settings are missing.
|
|
489
|
+
|
|
490
|
+
Loops until the configured model passes its check or the user chooses to
|
|
491
|
+
keep the current settings. On failure we offer a retry, but only when we
|
|
492
|
+
can actually re-prompt for a different model — i.e. interactive and not
|
|
493
|
+
pinned by ``--litellm-model``; otherwise we just print the next steps.
|
|
494
|
+
"""
|
|
448
495
|
from .embedder_defaults import lookup_defaults
|
|
449
496
|
from .shared import is_sentence_transformers_installed
|
|
450
497
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
tty=sys.stdin.isatty(),
|
|
455
|
-
)
|
|
498
|
+
st_installed = is_sentence_transformers_installed()
|
|
499
|
+
interactive = sys.stdin.isatty()
|
|
500
|
+
previous: EmbeddingSettings | None = None
|
|
456
501
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
502
|
+
while True:
|
|
503
|
+
embedding = _resolve_embedding_choice(
|
|
504
|
+
litellm_model_flag=litellm_model_flag,
|
|
505
|
+
st_installed=st_installed,
|
|
506
|
+
tty=interactive,
|
|
507
|
+
previous=previous,
|
|
508
|
+
)
|
|
509
|
+
previous = embedding # remembered as the defaults for a potential retry
|
|
463
510
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
511
|
+
# Apply curated defaults if the model is in our table.
|
|
512
|
+
indexing_defaults, query_defaults = lookup_defaults(embedding.provider, embedding.model)
|
|
513
|
+
defaults_applied = indexing_defaults is not None or query_defaults is not None
|
|
514
|
+
if defaults_applied:
|
|
515
|
+
embedding.indexing_params = indexing_defaults or {}
|
|
516
|
+
embedding.query_params = query_defaults or {}
|
|
467
517
|
|
|
468
|
-
|
|
518
|
+
path = save_initial_user_settings(embedding, defaults_applied=defaults_applied)
|
|
469
519
|
_typer.echo()
|
|
470
|
-
_typer.echo(f"
|
|
471
|
-
_typer.echo(f" indexing_params: {embedding.indexing_params}")
|
|
472
|
-
_typer.echo(f" query_params: {embedding.query_params}")
|
|
520
|
+
_typer.echo(f"Created user settings: {format_path_for_display(path)}")
|
|
473
521
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
522
|
+
if defaults_applied:
|
|
523
|
+
_typer.echo()
|
|
524
|
+
_typer.echo(f"Applied recommended defaults for {embedding.model}:")
|
|
525
|
+
_typer.echo(f" indexing_params: {embedding.indexing_params}")
|
|
526
|
+
_typer.echo(f" query_params: {embedding.query_params}")
|
|
527
|
+
|
|
528
|
+
_typer.echo()
|
|
529
|
+
_typer.echo(f"Testing embedding model: {embedding.provider} / {embedding.model}")
|
|
530
|
+
if _run_init_model_check():
|
|
531
|
+
_typer.echo()
|
|
532
|
+
return
|
|
533
|
+
|
|
534
|
+
# Model check failed. Retry only makes sense if we can re-prompt.
|
|
535
|
+
if interactive and litellm_model_flag is None:
|
|
536
|
+
import questionary
|
|
537
|
+
|
|
538
|
+
_typer.echo() # separate the failure output from the prompt below
|
|
539
|
+
choice = questionary.select(
|
|
540
|
+
"The embedding model couldn't be loaded. What would you like to do?",
|
|
541
|
+
choices=[
|
|
542
|
+
questionary.Choice(title="Try a different provider/model", value="retry"),
|
|
543
|
+
questionary.Choice(
|
|
544
|
+
title="Keep these settings and finish — I'll edit the file myself",
|
|
545
|
+
value="keep",
|
|
546
|
+
),
|
|
547
|
+
],
|
|
548
|
+
).ask()
|
|
549
|
+
if choice == "retry":
|
|
550
|
+
continue
|
|
551
|
+
# "keep" or None (cancelled) falls through to the next-steps block.
|
|
552
|
+
|
|
553
|
+
_print_init_next_steps(path)
|
|
554
|
+
return
|
|
478
555
|
|
|
479
556
|
|
|
480
557
|
@app.command()
|
|
@@ -692,7 +769,7 @@ def _print_error(msg: str) -> None:
|
|
|
692
769
|
_typer.echo(_click.style(f" ERROR: {msg}", fg="red"), err=True)
|
|
693
770
|
|
|
694
771
|
|
|
695
|
-
def _print_doctor_result(result: DoctorCheckResult) -> None:
|
|
772
|
+
def _print_doctor_result(result: DoctorCheckResult, *, verbose: bool = False) -> None:
|
|
696
773
|
import click as _click
|
|
697
774
|
|
|
698
775
|
if result.name == "done":
|
|
@@ -704,13 +781,26 @@ def _print_doctor_result(result: DoctorCheckResult) -> None:
|
|
|
704
781
|
for err in result.errors:
|
|
705
782
|
_typer.echo(_click.style(f" ERROR: {err}", fg="red"), err=True)
|
|
706
783
|
if result.traceback:
|
|
707
|
-
|
|
708
|
-
|
|
784
|
+
if verbose:
|
|
785
|
+
for line in result.traceback.splitlines():
|
|
786
|
+
_typer.echo(_click.style(f" {line}", fg="bright_black"), err=True)
|
|
787
|
+
else:
|
|
788
|
+
_typer.echo(
|
|
789
|
+
_click.style(" Run `ccc doctor -v` for the full traceback.", fg="bright_black"),
|
|
790
|
+
err=True,
|
|
791
|
+
)
|
|
709
792
|
|
|
710
793
|
|
|
711
794
|
@app.command()
|
|
712
795
|
@_catch_daemon_start_error
|
|
713
|
-
def doctor(
|
|
796
|
+
def doctor(
|
|
797
|
+
verbose: bool = _typer.Option(
|
|
798
|
+
False,
|
|
799
|
+
"-v",
|
|
800
|
+
"--verbose",
|
|
801
|
+
help="Show full exception tracebacks for failed checks.",
|
|
802
|
+
),
|
|
803
|
+
) -> None:
|
|
714
804
|
"""Check system health and report issues."""
|
|
715
805
|
from . import client as _client
|
|
716
806
|
from .settings import (
|
|
@@ -720,6 +810,9 @@ def doctor() -> None:
|
|
|
720
810
|
load_user_settings as _load_user_settings,
|
|
721
811
|
)
|
|
722
812
|
|
|
813
|
+
def _on_result(result: DoctorCheckResult) -> None:
|
|
814
|
+
_print_doctor_result(result, verbose=verbose)
|
|
815
|
+
|
|
723
816
|
# --- 1. Global settings (local, no daemon needed) ---
|
|
724
817
|
_print_section("Global Settings")
|
|
725
818
|
settings_path = user_settings_path()
|
|
@@ -773,7 +866,7 @@ def doctor() -> None:
|
|
|
773
866
|
try:
|
|
774
867
|
_client.doctor(
|
|
775
868
|
project_root=None,
|
|
776
|
-
on_result=
|
|
869
|
+
on_result=_on_result,
|
|
777
870
|
)
|
|
778
871
|
except Exception as e:
|
|
779
872
|
_print_error(f"Model check failed: {e}")
|
|
@@ -804,7 +897,7 @@ def doctor() -> None:
|
|
|
804
897
|
try:
|
|
805
898
|
_client.doctor(
|
|
806
899
|
project_root=str(project_root),
|
|
807
|
-
on_result=
|
|
900
|
+
on_result=_on_result,
|
|
808
901
|
)
|
|
809
902
|
except Exception as e:
|
|
810
903
|
_print_error(f"Project checks failed: {e}")
|
|
@@ -111,25 +111,33 @@ def _connect_and_handshake() -> Connection:
|
|
|
111
111
|
|
|
112
112
|
Returns the open connection for the caller to send exactly one request.
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
``
|
|
117
|
-
|
|
114
|
+
Automatically starts or restarts the daemon when it is absent or running
|
|
115
|
+
with stale global settings (e.g. a ``ccc init`` retry rewrote
|
|
116
|
+
``global_settings.yml`` after the daemon loaded it). A genuine *version*
|
|
117
|
+
mismatch after we have already reached a matching daemon means the binary
|
|
118
|
+
was replaced under us mid-session — that fails fast instead of looping on
|
|
119
|
+
restarts.
|
|
118
120
|
"""
|
|
119
121
|
global _daemon_ensured # noqa: PLW0603
|
|
120
122
|
|
|
121
|
-
if _daemon_ensured:
|
|
122
|
-
return _raw_connect_and_handshake()
|
|
123
|
-
|
|
124
|
-
# First connection — auto-start/restart as needed.
|
|
125
123
|
try:
|
|
126
124
|
conn = _raw_connect_and_handshake()
|
|
127
125
|
_daemon_ensured = True
|
|
128
126
|
return conn
|
|
129
|
-
except DaemonVersionError:
|
|
127
|
+
except DaemonVersionError as e:
|
|
128
|
+
# `resp.ok` is False only for a real version mismatch. Once we have
|
|
129
|
+
# ensured a matching daemon, a fresh version mismatch means the binary
|
|
130
|
+
# was swapped under us — fail fast. A settings-only restart request
|
|
131
|
+
# (resp.ok True, but the loaded settings mtime moved) is expected;
|
|
132
|
+
# restart the daemon below so it reloads them.
|
|
133
|
+
if _daemon_ensured and not e.resp.ok:
|
|
134
|
+
raise
|
|
130
135
|
stop_daemon()
|
|
131
136
|
except (ConnectionRefusedError, OSError):
|
|
132
|
-
|
|
137
|
+
# No daemon answered. Normal on the first call (start one below); if we
|
|
138
|
+
# had already ensured one it vanished mid-session — surface that.
|
|
139
|
+
if _daemon_ensured:
|
|
140
|
+
raise
|
|
133
141
|
|
|
134
142
|
if _is_daemon_supervised():
|
|
135
143
|
# Supervisor is responsible for (re)starting the daemon — just wait
|
|
@@ -192,10 +200,16 @@ class DaemonVersionError(RuntimeError):
|
|
|
192
200
|
|
|
193
201
|
def __init__(self, resp: HandshakeResponse) -> None:
|
|
194
202
|
self.resp = resp
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
203
|
+
if not resp.ok:
|
|
204
|
+
message = (
|
|
205
|
+
f"Daemon version mismatch (daemon={resp.daemon_version}, "
|
|
206
|
+
f"client={__version__}). Please retry — the daemon may need a restart."
|
|
207
|
+
)
|
|
208
|
+
else:
|
|
209
|
+
message = (
|
|
210
|
+
"Daemon is running with stale global settings and needs a restart. Please retry."
|
|
211
|
+
)
|
|
212
|
+
super().__init__(message)
|
|
199
213
|
|
|
200
214
|
|
|
201
215
|
class DaemonStartError(RuntimeError):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|