lean-explore 1.1.1__py3-none-any.whl → 1.2.1__py3-none-any.whl

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.
@@ -8,6 +8,7 @@ import logging
8
8
  import os
9
9
  import shutil
10
10
  import subprocess
11
+ import time
11
12
  from pathlib import Path
12
13
 
13
14
  from lean_explore.extract.github import extract_lean_version
@@ -80,6 +81,56 @@ def _setup_workspace(package_config: PackageConfig) -> tuple[str, str]:
80
81
  return lean_toolchain, git_ref
81
82
 
82
83
 
84
+ def _run_lake_update_with_retry(
85
+ workspace_path: Path,
86
+ package_name: str,
87
+ env: dict[str, str],
88
+ verbose: bool = False,
89
+ max_retries: int = 3,
90
+ base_delay: float = 30.0,
91
+ ) -> None:
92
+ """Run ``lake update`` with retries for transient network failures.
93
+
94
+ Large repositories like mathlib4 require cloning several gigabytes of git
95
+ data. Transient network issues (DNS blips, connection resets, GitHub
96
+ throttling) can cause the clone to fail with git exit code 128. Retrying
97
+ with exponential backoff handles these cases.
98
+
99
+ Args:
100
+ workspace_path: Path to the Lake workspace directory.
101
+ package_name: Name of the package (for log messages).
102
+ env: Environment variables to pass to the subprocess.
103
+ verbose: Log stdout from ``lake update``.
104
+ max_retries: Maximum number of retry attempts after the initial try.
105
+ base_delay: Seconds to wait before the first retry. Doubles each retry.
106
+ """
107
+ for attempt in range(1, max_retries + 2):
108
+ logger.info(f"[{package_name}] Running lake update (attempt {attempt})...")
109
+ result = subprocess.run(
110
+ ["lake", "update"],
111
+ cwd=workspace_path,
112
+ capture_output=True,
113
+ text=True,
114
+ env=env,
115
+ )
116
+ if verbose and result.stdout:
117
+ logger.info(result.stdout)
118
+ if result.returncode == 0:
119
+ return
120
+
121
+ if attempt <= max_retries:
122
+ delay = base_delay * (2 ** (attempt - 1))
123
+ logger.warning(
124
+ f"[{package_name}] lake update failed (attempt {attempt}), "
125
+ f"retrying in {delay:.0f}s..."
126
+ )
127
+ logger.warning(f"[{package_name}] stderr: {result.stderr.strip()}")
128
+ time.sleep(delay)
129
+ else:
130
+ logger.error(result.stderr)
131
+ raise RuntimeError(f"lake update failed for {package_name}")
132
+
133
+
83
134
  def _run_lake_for_package(package_name: str, verbose: bool = False) -> None:
84
135
  """Run lake update, cache get, and doc-gen4 for a package."""
85
136
  workspace_path = Path("lean") / package_name
@@ -87,19 +138,7 @@ def _run_lake_for_package(package_name: str, verbose: bool = False) -> None:
87
138
  env = os.environ.copy()
88
139
  env["MATHLIB_NO_CACHE_ON_UPDATE"] = "1"
89
140
 
90
- logger.info(f"[{package_name}] Running lake update...")
91
- result = subprocess.run(
92
- ["lake", "update"],
93
- cwd=workspace_path,
94
- capture_output=True,
95
- text=True,
96
- env=env,
97
- )
98
- if verbose and result.stdout:
99
- logger.info(result.stdout)
100
- if result.returncode != 0:
101
- logger.error(result.stderr)
102
- raise RuntimeError(f"lake update failed for {package_name}")
141
+ _run_lake_update_with_retry(workspace_path, package_name, env, verbose)
103
142
 
104
143
  # Fetch mathlib cache for packages that depend on mathlib
105
144
  if "mathlib" in package_config.depends_on or package_name == "mathlib":
lean_explore/mcp/app.py CHANGED
@@ -69,7 +69,24 @@ async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
69
69
  mcp_app = FastMCP(
70
70
  name="LeanExploreMCPServer",
71
71
  instructions=(
72
- "MCP Server for Lean Explore, providing tools to search Lean declarations."
72
+ "MCP Server for searching Lean 4 mathematical declarations (theorems, "
73
+ "definitions, lemmas, instances, etc.) from Mathlib and other Lean "
74
+ "packages.\n\n"
75
+ "The search engine is hybrid: it matches by declaration name (e.g., "
76
+ "'List.map', 'Nat.add') AND by informal natural language meaning (e.g., "
77
+ "'a continuous function on a compact set', 'prime number divisibility'). "
78
+ "You can use either style of query.\n\n"
79
+ "Recommended workflow:\n"
80
+ "1. Use search_summary to browse results (low token cost).\n"
81
+ "2. Use per-field tools to fetch only what you need:\n"
82
+ " - get_source_code: Lean source code\n"
83
+ " - get_source_link: GitHub link to source\n"
84
+ " - get_docstring: documentation string\n"
85
+ " - get_description: natural language description\n"
86
+ " - get_module: module path in the package\n"
87
+ " - get_dependencies: declarations this depends on\n"
88
+ "3. Use search only when you need full details for all results "
89
+ "at once."
73
90
  ),
74
91
  lifespan=app_lifespan,
75
92
  )
lean_explore/mcp/tools.py CHANGED
@@ -54,6 +54,54 @@ class SearchResponseDict(TypedDict, total=False):
54
54
  processing_time_ms: int | None
55
55
 
56
56
 
57
+ class SourceCodeResultDict(TypedDict):
58
+ """Result containing declaration id, name, and source code."""
59
+
60
+ id: int
61
+ name: str
62
+ source_text: str
63
+
64
+
65
+ class SourceLinkResultDict(TypedDict):
66
+ """Result containing declaration id, name, and GitHub source link."""
67
+
68
+ id: int
69
+ name: str
70
+ source_link: str
71
+
72
+
73
+ class DocstringResultDict(TypedDict):
74
+ """Result containing declaration id, name, and docstring."""
75
+
76
+ id: int
77
+ name: str
78
+ docstring: str | None
79
+
80
+
81
+ class DescriptionResultDict(TypedDict):
82
+ """Result containing declaration id, name, and informalization."""
83
+
84
+ id: int
85
+ name: str
86
+ informalization: str | None
87
+
88
+
89
+ class ModuleResultDict(TypedDict):
90
+ """Result containing declaration id, name, and module path."""
91
+
92
+ id: int
93
+ name: str
94
+ module: str
95
+
96
+
97
+ class DependenciesResultDict(TypedDict):
98
+ """Result containing declaration id, name, and dependencies."""
99
+
100
+ id: int
101
+ name: str
102
+ dependencies: str | None
103
+
104
+
57
105
  logger = logging.getLogger(__name__)
58
106
 
59
107
 
@@ -112,6 +160,33 @@ async def _execute_backend_search(
112
160
  )
113
161
 
114
162
 
163
+ async def _execute_backend_get_by_id(
164
+ backend: BackendServiceType,
165
+ declaration_id: int,
166
+ ) -> SearchResult | None:
167
+ """Execute get_by_id on the backend, handling both async and sync backends.
168
+
169
+ Args:
170
+ backend: The backend service (ApiClient or Service).
171
+ declaration_id: The numeric id of the declaration to retrieve.
172
+
173
+ Returns:
174
+ The SearchResult from the backend, or None if not found.
175
+
176
+ Raises:
177
+ RuntimeError: If the backend does not support get_by_id.
178
+ """
179
+ if not hasattr(backend, "get_by_id"):
180
+ logger.error("Backend service does not have a 'get_by_id' method.")
181
+ raise RuntimeError(
182
+ "Get by ID functionality not available on configured backend."
183
+ )
184
+
185
+ if asyncio.iscoroutinefunction(backend.get_by_id):
186
+ return await backend.get_by_id(declaration_id=declaration_id)
187
+ return backend.get_by_id(declaration_id=declaration_id)
188
+
189
+
115
190
  @mcp_app.tool()
116
191
  async def search(
117
192
  ctx: MCPContext,
@@ -120,11 +195,29 @@ async def search(
120
195
  rerank_top: int | None = 50,
121
196
  packages: list[str] | None = None,
122
197
  ) -> SearchResponseDict:
123
- """Searches Lean declarations by a query string.
198
+ """Search Lean 4 declarations and return full results including source code.
199
+
200
+ Accepts two kinds of queries:
201
+ - By name: a full or partial Lean declaration name, e.g., "List.map",
202
+ "Nat.Prime", "CategoryTheory.Functor.map".
203
+ - By meaning: an informal natural language description, e.g.,
204
+ "continuous function on a compact set", "sum of a geometric series",
205
+ "a group homomorphism preserving multiplication".
206
+
207
+ The search engine handles both styles simultaneously via hybrid retrieval
208
+ (lexical name matching + semantic similarity), so you do not need to
209
+ specify which kind of query you are making.
210
+
211
+ Returns full results including source code, module, dependencies, and
212
+ informalization for every hit. If you only need names and short
213
+ descriptions, prefer search_summary to save tokens, then use the
214
+ per-field tools (get_source_code, get_docstring, get_description,
215
+ get_module, get_dependencies) for the entries you care about.
124
216
 
125
217
  Args:
126
218
  ctx: The MCP context, providing access to the backend service.
127
- query: A search query string, e.g., "continuous function".
219
+ query: A Lean declaration name (e.g., "List.filter") or an informal
220
+ natural language description (e.g., "prime number divisibility").
128
221
  limit: The maximum number of search results to return. Defaults to 10.
129
222
  rerank_top: Number of candidates to rerank with cross-encoder. Set to 0 or
130
223
  None to skip reranking. Defaults to 50. Only used with local backend.
@@ -155,15 +248,29 @@ async def search_summary(
155
248
  rerank_top: int | None = 50,
156
249
  packages: list[str] | None = None,
157
250
  ) -> SearchSummaryResponseDict:
158
- """Searches Lean declarations and returns concise results.
251
+ """Search Lean 4 declarations and return concise results (recommended first step).
159
252
 
160
- Returns slim results (id, name, short description) to minimize token usage.
161
- Use get_by_id to retrieve full details for specific declarations, or
162
- search to get all fields upfront.
253
+ This is the preferred starting point for search. Returns only id, name,
254
+ and a short natural language description for each hit, keeping token
255
+ usage low. After reviewing these summaries, use the per-field tools
256
+ (get_source_code, get_docstring, get_description, get_module,
257
+ get_dependencies) for the entries you need details on.
258
+
259
+ Accepts two kinds of queries:
260
+ - By name: a full or partial Lean declaration name, e.g., "List.map",
261
+ "Nat.Prime", "CategoryTheory.Functor.map".
262
+ - By meaning: an informal natural language description, e.g.,
263
+ "continuous function on a compact set", "sum of a geometric series",
264
+ "a group homomorphism preserving multiplication".
265
+
266
+ The search engine handles both styles simultaneously via hybrid retrieval
267
+ (lexical name matching + semantic similarity), so you do not need to
268
+ specify which kind of query you are making.
163
269
 
164
270
  Args:
165
271
  ctx: The MCP context, providing access to the backend service.
166
- query: A search query string, e.g., "continuous function".
272
+ query: A Lean declaration name (e.g., "List.filter") or an informal
273
+ natural language description (e.g., "prime number divisibility").
167
274
  limit: The maximum number of search results to return. Defaults to 10.
168
275
  rerank_top: Number of candidates to rerank with cross-encoder. Set to 0 or
169
276
  None to skip reranking. Defaults to 50. Only used with local backend.
@@ -203,38 +310,219 @@ async def search_summary(
203
310
 
204
311
 
205
312
  @mcp_app.tool()
206
- async def get_by_id(
313
+ async def get_source_code(
207
314
  ctx: MCPContext,
208
315
  declaration_id: int,
209
- ) -> SearchResultDict | None:
210
- """Retrieves a specific declaration by its unique identifier.
316
+ ) -> SourceCodeResultDict | None:
317
+ """Retrieve the Lean source code for a declaration by id.
318
+
319
+ Returns the declaration name and its Lean 4 source code. Use this after
320
+ calling search_summary to inspect the actual implementation.
211
321
 
212
- Returns the full declaration including source code, dependencies, module
213
- info, and informalization. Use this to expand results from the search tool.
322
+ The id values come from the search or search_summary result lists.
214
323
 
215
324
  Args:
216
325
  ctx: The MCP context, providing access to the backend service.
217
- declaration_id: The unique integer identifier of the declaration.
326
+ declaration_id: The numeric id from a search or search_summary result.
218
327
 
219
328
  Returns:
220
- A dictionary representing the SearchResult, or None if not found.
329
+ A dictionary with id, name, and source_text, or None if the id
330
+ does not exist.
221
331
  """
222
332
  backend = await _get_backend_from_context(ctx)
223
- logger.info(f"MCP Tool 'get_by_id' called for declaration_id: {declaration_id}")
333
+ logger.info(
334
+ f"MCP Tool 'get_source_code' called for declaration_id: {declaration_id}"
335
+ )
224
336
 
225
- if not hasattr(backend, "get_by_id"):
226
- logger.error("Backend service does not have a 'get_by_id' method.")
227
- raise RuntimeError(
228
- "Get by ID functionality not available on configured backend."
229
- )
337
+ result = await _execute_backend_get_by_id(backend, declaration_id)
338
+ if result is None:
339
+ return None
230
340
 
231
- # Call backend get_by_id (handle both async and sync)
232
- if asyncio.iscoroutinefunction(backend.get_by_id):
233
- result: SearchResult | None = await backend.get_by_id(
234
- declaration_id=declaration_id
235
- )
236
- else:
237
- result: SearchResult | None = backend.get_by_id(declaration_id=declaration_id)
341
+ return SourceCodeResultDict(
342
+ id=result.id,
343
+ name=result.name,
344
+ source_text=result.source_text,
345
+ )
346
+
347
+
348
+ @mcp_app.tool()
349
+ async def get_source_link(
350
+ ctx: MCPContext,
351
+ declaration_id: int,
352
+ ) -> SourceLinkResultDict | None:
353
+ """Retrieve the GitHub source link for a declaration by id.
354
+
355
+ Returns the declaration name and a URL to the source code on GitHub.
356
+ Use this when you need to reference or link to the original source.
357
+
358
+ The id values come from the search or search_summary result lists.
238
359
 
239
- # Return as dict for MCP, or None
240
- return result.model_dump(exclude_none=True) if result else None
360
+ Args:
361
+ ctx: The MCP context, providing access to the backend service.
362
+ declaration_id: The numeric id from a search or search_summary result.
363
+
364
+ Returns:
365
+ A dictionary with id, name, and source_link, or None if the id
366
+ does not exist.
367
+ """
368
+ backend = await _get_backend_from_context(ctx)
369
+ logger.info(
370
+ f"MCP Tool 'get_source_link' called for declaration_id: {declaration_id}"
371
+ )
372
+
373
+ result = await _execute_backend_get_by_id(backend, declaration_id)
374
+ if result is None:
375
+ return None
376
+
377
+ return SourceLinkResultDict(
378
+ id=result.id,
379
+ name=result.name,
380
+ source_link=result.source_link,
381
+ )
382
+
383
+
384
+ @mcp_app.tool()
385
+ async def get_docstring(
386
+ ctx: MCPContext,
387
+ declaration_id: int,
388
+ ) -> DocstringResultDict | None:
389
+ """Retrieve the docstring for a declaration by id.
390
+
391
+ Returns the declaration name and its documentation string from the Lean
392
+ source code. Use this to check what documentation exists without
393
+ fetching the full source code.
394
+
395
+ The id values come from the search or search_summary result lists.
396
+
397
+ Args:
398
+ ctx: The MCP context, providing access to the backend service.
399
+ declaration_id: The numeric id from a search or search_summary result.
400
+
401
+ Returns:
402
+ A dictionary with id, name, and docstring, or None if the id
403
+ does not exist.
404
+ """
405
+ backend = await _get_backend_from_context(ctx)
406
+ logger.info(
407
+ f"MCP Tool 'get_docstring' called for declaration_id: {declaration_id}"
408
+ )
409
+
410
+ result = await _execute_backend_get_by_id(backend, declaration_id)
411
+ if result is None:
412
+ return None
413
+
414
+ return DocstringResultDict(
415
+ id=result.id,
416
+ name=result.name,
417
+ docstring=result.docstring,
418
+ )
419
+
420
+
421
+ @mcp_app.tool()
422
+ async def get_description(
423
+ ctx: MCPContext,
424
+ declaration_id: int,
425
+ ) -> DescriptionResultDict | None:
426
+ """Retrieve the natural language description for a declaration by id.
427
+
428
+ Returns the declaration name and its informalization, an AI-generated
429
+ plain-English explanation of what the declaration states or does.
430
+
431
+ The id values come from the search or search_summary result lists.
432
+
433
+ Args:
434
+ ctx: The MCP context, providing access to the backend service.
435
+ declaration_id: The numeric id from a search or search_summary result.
436
+
437
+ Returns:
438
+ A dictionary with id, name, and informalization, or None if the id
439
+ does not exist.
440
+ """
441
+ backend = await _get_backend_from_context(ctx)
442
+ logger.info(
443
+ f"MCP Tool 'get_description' called for declaration_id: {declaration_id}"
444
+ )
445
+
446
+ result = await _execute_backend_get_by_id(backend, declaration_id)
447
+ if result is None:
448
+ return None
449
+
450
+ return DescriptionResultDict(
451
+ id=result.id,
452
+ name=result.name,
453
+ informalization=result.informalization,
454
+ )
455
+
456
+
457
+ @mcp_app.tool()
458
+ async def get_module(
459
+ ctx: MCPContext,
460
+ declaration_id: int,
461
+ ) -> ModuleResultDict | None:
462
+ """Retrieve the module path for a declaration by id.
463
+
464
+ Returns the declaration name and the Lean module it belongs to
465
+ (e.g., 'Mathlib.Data.List.Basic'). Use this to find where a
466
+ declaration lives in the package structure.
467
+
468
+ The id values come from the search or search_summary result lists.
469
+
470
+ Args:
471
+ ctx: The MCP context, providing access to the backend service.
472
+ declaration_id: The numeric id from a search or search_summary result.
473
+
474
+ Returns:
475
+ A dictionary with id, name, and module, or None if the id does
476
+ not exist.
477
+ """
478
+ backend = await _get_backend_from_context(ctx)
479
+ logger.info(
480
+ f"MCP Tool 'get_module' called for declaration_id: {declaration_id}"
481
+ )
482
+
483
+ result = await _execute_backend_get_by_id(backend, declaration_id)
484
+ if result is None:
485
+ return None
486
+
487
+ return ModuleResultDict(
488
+ id=result.id,
489
+ name=result.name,
490
+ module=result.module,
491
+ )
492
+
493
+
494
+ @mcp_app.tool()
495
+ async def get_dependencies(
496
+ ctx: MCPContext,
497
+ declaration_id: int,
498
+ ) -> DependenciesResultDict | None:
499
+ """Retrieve the dependencies for a declaration by id.
500
+
501
+ Returns the declaration name and a JSON array of other declaration
502
+ names that this declaration depends on. Use this to understand what
503
+ a declaration builds upon.
504
+
505
+ The id values come from the search or search_summary result lists.
506
+
507
+ Args:
508
+ ctx: The MCP context, providing access to the backend service.
509
+ declaration_id: The numeric id from a search or search_summary result.
510
+
511
+ Returns:
512
+ A dictionary with id, name, and dependencies, or None if the id
513
+ does not exist.
514
+ """
515
+ backend = await _get_backend_from_context(ctx)
516
+ logger.info(
517
+ f"MCP Tool 'get_dependencies' called for declaration_id: {declaration_id}"
518
+ )
519
+
520
+ result = await _execute_backend_get_by_id(backend, declaration_id)
521
+ if result is None:
522
+ return None
523
+
524
+ return DependenciesResultDict(
525
+ id=result.id,
526
+ name=result.name,
527
+ dependencies=result.dependencies,
528
+ )
@@ -28,7 +28,8 @@ class SearchResultSummary(BaseModel):
28
28
  """A slim search result containing only identification and description.
29
29
 
30
30
  Used by the MCP search tool to return concise results that minimize
31
- token usage. Consumers can use the id to fetch full details via get_by_id.
31
+ token usage. Consumers can use the id to fetch specific fields via the
32
+ per-field tools (get_source_code, get_docstring, etc.).
32
33
  """
33
34
 
34
35
  id: int
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lean-explore
3
- Version: 1.1.1
3
+ Version: 1.2.1
4
4
  Summary: A search engine for Lean 4 declarations.
5
5
  Author-email: Justin Asher <justinchadwickasher@gmail.com>
6
6
  License: Apache License
@@ -241,10 +241,13 @@ Requires-Dist: rich>=13.0.0
241
241
  Requires-Dist: requests>=2.25.0
242
242
  Requires-Dist: tenacity>=8.0.0
243
243
  Requires-Dist: pooch>=1.8.0
244
+ Provides-Extra: local
245
+ Requires-Dist: sentence-transformers>=2.2.0; extra == "local"
246
+ Requires-Dist: torch>=2.0.0; extra == "local"
244
247
  Provides-Extra: extract
245
248
  Requires-Dist: sentence-transformers>=2.2.0; extra == "extract"
246
- Requires-Dist: networkx>=3.0; extra == "extract"
247
249
  Requires-Dist: torch>=2.0.0; extra == "extract"
250
+ Requires-Dist: networkx>=3.0; extra == "extract"
248
251
  Provides-Extra: dev
249
252
  Requires-Dist: pytest>=7.0; extra == "dev"
250
253
  Requires-Dist: pytest-cov>=4.0; extra == "dev"
@@ -283,6 +286,27 @@ A search engine for Lean 4 declarations. This project provides tools and resourc
283
286
 
284
287
  **For full documentation, please visit: [https://www.leanexplore.com/docs](https://www.leanexplore.com/docs)**
285
288
 
289
+ ## Installation
290
+
291
+ The base package connects to the remote API and does not require heavy ML dependencies:
292
+
293
+ ```bash
294
+ pip install lean-explore
295
+ ```
296
+
297
+ To run the **local** search backend (which uses on-device embedding and reranking models), install the extra ML dependencies:
298
+
299
+ ```bash
300
+ pip install lean-explore[local]
301
+ ```
302
+
303
+ Then fetch the data files and start the local MCP server:
304
+
305
+ ```bash
306
+ lean-explore data fetch
307
+ lean-explore mcp serve --backend local
308
+ ```
309
+
286
310
  The current indexed projects include:
287
311
 
288
312
  * Batteries
@@ -8,7 +8,7 @@ lean_explore/cli/display.py,sha256=t7qfP8Cdw05KM-kfYmFEeWSHNzyYuBOcvx4aCb5lFv0,5
8
8
  lean_explore/cli/main.py,sha256=feVxTmzH0FMZGP7Urb6X43atlESJIYXgC6DJylgiKp0,3937
9
9
  lean_explore/extract/__init__.py,sha256=ZpCPPHjFCRxyHwYxQNhGpbpd_XuZOkgB9qkxa9zSUD0,184
10
10
  lean_explore/extract/__main__.py,sha256=Aquy2g-WIU7YO39UZRq0EoHgJWW-uM5G0FSK2OwDuR8,12010
11
- lean_explore/extract/doc_gen4.py,sha256=5pvw-EdfDxg1NPxLpa6kQuoVUe1_TtKL1_hO13I0t78,6734
11
+ lean_explore/extract/doc_gen4.py,sha256=FG-P4S6dGU55IRhJyYtEp3XUIMhKMad6_IZpAA5hWwM,8305
12
12
  lean_explore/extract/doc_parser.py,sha256=tb3y7409mn5DhPjR2prPh2Cm7QMf3VJy8qfir_-Y4z8,17511
13
13
  lean_explore/extract/embeddings.py,sha256=atZzaM2wZBSZflL2sTbStGUSd1WKjyex218fMxG_CWc,11875
14
14
  lean_explore/extract/github.py,sha256=1Zyyl2u1esULJ3KFHHL8F80zcxLDx1Fh8u_gxCmBTi8,3499
@@ -19,12 +19,12 @@ lean_explore/extract/package_registry.py,sha256=N-M3WYry77P80pjt12lpFcCuPW1M9Sgx
19
19
  lean_explore/extract/package_utils.py,sha256=SBD4gbCA5enldm2m3AoL4Dti1FsSs-ShQYeYMjyOADg,3400
20
20
  lean_explore/extract/types.py,sha256=Sp6sYuioTE_Gs0Z0lbq4h7OMyAnaZafdLV8UGtKn-zs,583
21
21
  lean_explore/mcp/__init__.py,sha256=YO0RM466ik2jQk8YMDITzkm3AHPtjEhn7Wm7rOusUXo,462
22
- lean_explore/mcp/app.py,sha256=PJ2HwX6VyTqKejuI1G8Ld4aO9XWp9hT5H8loaA5g0Lc,2173
22
+ lean_explore/mcp/app.py,sha256=7Se0sqvyzigxwkqti19leRWNEe4CAK1VrCW2Br6TZbw,3165
23
23
  lean_explore/mcp/server.py,sha256=Lf3SCn8ghPNkZ3BybHh3VCXn91F-yX6RSRke1rvC7Pk,8234
24
- lean_explore/mcp/tools.py,sha256=3MLeePzHS9mMkr97Lx5JM2YxF4QiHBWYZvAh6yBh-9g,7837
24
+ lean_explore/mcp/tools.py,sha256=WlH73HOt81a6nYGOK_wkuQ_d2x739TWik9pZon2ow9w,16757
25
25
  lean_explore/models/__init__.py,sha256=k4tDULTDIp61iLuP-CBXLJzjCaEBgNVgO_x9JpdrjgY,523
26
26
  lean_explore/models/search_db.py,sha256=_a6B6FpqUevyHvW4KmNLeziiznIuxftpMUy0AtSDBJE,2673
27
- lean_explore/models/search_types.py,sha256=3r0eql--zoTGRtwosbvZpTMK7tdBdPSfWPBFTU_pupE,3001
27
+ lean_explore/models/search_types.py,sha256=UgwB-J5nN30PpVdKmlSe1ewjJ3DsmeYtDR4Er8AAmUc,3057
28
28
  lean_explore/search/__init__.py,sha256=0k_iHe5xrurepznk7NzMYz10QFbK10ydMlpFlsuyFSc,1216
29
29
  lean_explore/search/engine.py,sha256=oAsqiltBEXsbur0t77-zG8ATgcZ-8vKX8vvdoPNZsv0,23660
30
30
  lean_explore/search/scoring.py,sha256=VkH-kpGheX14_tf8uJYBOp0nrG05_JJLmv7_0QdfAQk,4168
@@ -35,9 +35,9 @@ lean_explore/util/embedding_client.py,sha256=6XJGJrGTXAiefDr-E1j_SPHTTZMIJYi62Pw
35
35
  lean_explore/util/logging.py,sha256=hF8YPi-1I6DdC1B_yROXA6u5GG14IIhD0Nym2FfqgRA,649
36
36
  lean_explore/util/openrouter_client.py,sha256=C_0HLO5o1seYjGl2zn6897i2onK7CdI6XxtE3cWb3Os,1926
37
37
  lean_explore/util/reranker_client.py,sha256=kLCTGPMQuphjwAj0PPi9KXpSzDP7o9JRQJpTbmWGiMs,6074
38
- lean_explore-1.1.1.dist-info/licenses/LICENSE,sha256=l4QLw1kIvEOjUktmmKm4dycK1E249Qs2s2AQTYbMXpY,11354
39
- lean_explore-1.1.1.dist-info/METADATA,sha256=q0RKNHMMwkLvPzToLOuUbNoumHaCILKaecT6ypKGmnQ,17084
40
- lean_explore-1.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
41
- lean_explore-1.1.1.dist-info/entry_points.txt,sha256=FuKSRE7GmI9B_kM-xoiWEJj2dQ4upqhHnw8qH1vcjW8,59
42
- lean_explore-1.1.1.dist-info/top_level.txt,sha256=h51BKWrFvB7iym-IlaNAAHX5MZfA8Gmg-aDuXGo0fQ8,13
43
- lean_explore-1.1.1.dist-info/RECORD,,
38
+ lean_explore-1.2.1.dist-info/licenses/LICENSE,sha256=l4QLw1kIvEOjUktmmKm4dycK1E249Qs2s2AQTYbMXpY,11354
39
+ lean_explore-1.2.1.dist-info/METADATA,sha256=oC9qaZmqz9DSc0oCK4sSuFCxm8caZ5XW8PwFLykUHgw,17665
40
+ lean_explore-1.2.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
41
+ lean_explore-1.2.1.dist-info/entry_points.txt,sha256=FuKSRE7GmI9B_kM-xoiWEJj2dQ4upqhHnw8qH1vcjW8,59
42
+ lean_explore-1.2.1.dist-info/top_level.txt,sha256=h51BKWrFvB7iym-IlaNAAHX5MZfA8Gmg-aDuXGo0fQ8,13
43
+ lean_explore-1.2.1.dist-info/RECORD,,