revit-api-docs 0.1.0__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.
Files changed (36) hide show
  1. revit_api_docs/__init__.py +18 -0
  2. revit_api_docs/__main__.py +8 -0
  3. revit_api_docs/config.py +115 -0
  4. revit_api_docs/data.py +320 -0
  5. revit_api_docs/default_config.yaml +85 -0
  6. revit_api_docs/embedder/__init__.py +1 -0
  7. revit_api_docs/embedder/providers/__init__.py +25 -0
  8. revit_api_docs/embedder/providers/base.py +28 -0
  9. revit_api_docs/embedder/providers/google.py +36 -0
  10. revit_api_docs/embedder/providers/openai.py +58 -0
  11. revit_api_docs/llm_client.py +288 -0
  12. revit_api_docs/prompts/__init__.py +5 -0
  13. revit_api_docs/prompts/loader.py +20 -0
  14. revit_api_docs/prompts/pipeline.api_quality_stage1_no_html.md +28 -0
  15. revit_api_docs/prompts/pipeline.api_quality_stage1_system.md +1 -0
  16. revit_api_docs/prompts/pipeline.api_quality_stage1_with_html.md +35 -0
  17. revit_api_docs/prompts/pipeline.api_quality_stage2.md +33 -0
  18. revit_api_docs/prompts/pipeline.api_quality_stage2_system.md +1 -0
  19. revit_api_docs/prompts/pipeline.rewrite_query.md +21 -0
  20. revit_api_docs/prompts/pipeline.rewrite_query_system.md +1 -0
  21. revit_api_docs/prompts/pipeline.sdk_extract_readme_system.md +1 -0
  22. revit_api_docs/prompts/pipeline.sdk_extract_readme_user.md +22 -0
  23. revit_api_docs/prompts/pipeline.sdk_golden_code.md +32 -0
  24. revit_api_docs/prompts/pipeline.sdk_golden_code_system.md +1 -0
  25. revit_api_docs/prompts/pipeline.sdk_quality_file_batch.md +17 -0
  26. revit_api_docs/prompts/pipeline.sdk_quality_file_system.md +1 -0
  27. revit_api_docs/prompts/pipeline.sdk_quality_project.md +20 -0
  28. revit_api_docs/prompts/pipeline.sdk_quality_project_system.md +1 -0
  29. revit_api_docs/reranker.py +177 -0
  30. revit_api_docs/retriever.py +901 -0
  31. revit_api_docs/server.py +244 -0
  32. revit_api_docs-0.1.0.dist-info/METADATA +139 -0
  33. revit_api_docs-0.1.0.dist-info/RECORD +36 -0
  34. revit_api_docs-0.1.0.dist-info/WHEEL +4 -0
  35. revit_api_docs-0.1.0.dist-info/entry_points.txt +2 -0
  36. revit_api_docs-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,177 @@
1
+ """
2
+ Reranker — post-retrieval reranking using Cohere native API.
3
+
4
+ Flow:
5
+ 1. RAG retrieves top_k candidates from ChromaDB (broad recall)
6
+ 2. Reranker scores each candidate against the query (precision)
7
+ 3. Returns top_n best results sorted by relevance score
8
+
9
+ NOTE: Cohere rerank uses their native API (https://api.cohere.com/v2/rerank),
10
+ NOT OpenRouter. Set COHERE_API_KEY in environment or config.
11
+
12
+ Configured in config.yaml:
13
+ rerank:
14
+ enabled: true
15
+ provider: "cohere"
16
+ models:
17
+ cohere:
18
+ model: "rerank-v3.5"
19
+ api_key_env: "COHERE_API_KEY"
20
+ """
21
+ from __future__ import annotations
22
+
23
+ import logging
24
+ import os
25
+ from dataclasses import dataclass
26
+ from typing import Any
27
+
28
+ import httpx
29
+
30
+ _log = logging.getLogger("revit_api_docs.reranker")
31
+
32
+ # Cohere native rerank endpoint
33
+ _COHERE_RERANK_URL = "https://api.cohere.com/v2/rerank"
34
+
35
+
36
+ @dataclass
37
+ class RerankResult:
38
+ """A single reranked item with its relevance score."""
39
+ index: int # original index in the input list
40
+ score: float # relevance score (higher = better)
41
+ text: str # the document text that was scored
42
+
43
+
44
+ class Reranker:
45
+ """Reranker using Cohere's native rerank API (fast, ~200ms)."""
46
+
47
+ def __init__(
48
+ self,
49
+ model: str = "rerank-v3.5",
50
+ api_key_env: str = "COHERE_API_KEY",
51
+ proxy: str | None = None,
52
+ ):
53
+ self._model = model.replace("cohere/", "") # strip provider prefix
54
+ self._api_key = os.getenv(api_key_env, "")
55
+ self._proxy = proxy
56
+
57
+ if self._api_key:
58
+ _log.info(f"[Reranker] initialized model={self._model}")
59
+ else:
60
+ _log.warning(f"[Reranker] no API key ({api_key_env}), rerank will be skipped")
61
+
62
+ def rerank(
63
+ self,
64
+ query: str,
65
+ documents: list[str],
66
+ top_n: int = 15,
67
+ ) -> list[RerankResult]:
68
+ """Rerank documents against query, return top_n sorted by relevance.
69
+
70
+ Uses Cohere's native v2 rerank endpoint — typically <1s for 30 docs.
71
+ """
72
+ if not documents:
73
+ return []
74
+ if not self._api_key:
75
+ _log.debug("[rerank] no API key, skipping rerank")
76
+ return [
77
+ RerankResult(index=i, score=1.0 - i * 0.01, text=doc)
78
+ for i, doc in enumerate(documents[:top_n])
79
+ ]
80
+
81
+ import time as _time
82
+ t0 = _time.monotonic()
83
+
84
+ try:
85
+ headers = {
86
+ "Authorization": f"Bearer {self._api_key}",
87
+ "Content-Type": "application/json",
88
+ }
89
+ payload = {
90
+ "model": self._model,
91
+ "query": query,
92
+ "documents": [{"text": d} for d in documents],
93
+ "top_n": min(top_n, len(documents)),
94
+ "return_documents": False,
95
+ }
96
+
97
+ client_kwargs: dict[str, Any] = {"timeout": 10.0}
98
+ if self._proxy:
99
+ client_kwargs["proxy"] = self._proxy
100
+
101
+ with httpx.Client(**client_kwargs) as client:
102
+ resp = client.post(_COHERE_RERANK_URL, headers=headers,
103
+ json=payload)
104
+ resp.raise_for_status()
105
+ data = resp.json()
106
+
107
+ results = []
108
+ for item in data.get("results", []):
109
+ idx = item.get("index", 0)
110
+ score = item.get("relevance_score", 0.0)
111
+ text = documents[idx] if idx < len(documents) else ""
112
+ results.append(RerankResult(index=idx, score=score, text=text))
113
+
114
+ results.sort(key=lambda r: r.score, reverse=True)
115
+ elapsed = _time.monotonic() - t0
116
+ _log.info(f"[rerank] {len(documents)} docs -> top {len(results)} "
117
+ f"in {elapsed:.2f}s "
118
+ f"scores=[{', '.join(f'{r.score:.3f}' for r in results[:3])}...]")
119
+ return results
120
+
121
+ except httpx.HTTPStatusError as e:
122
+ _log.warning(f"[rerank] HTTP {e.response.status_code}: "
123
+ f"{e.response.text[:200]}")
124
+ return self._fallback(documents, top_n)
125
+ except Exception as e:
126
+ _log.warning(f"[rerank] failed ({e}), using fallback order")
127
+ return self._fallback(documents, top_n)
128
+
129
+ @staticmethod
130
+ def _fallback(documents: list[str], top_n: int) -> list[RerankResult]:
131
+ """Return documents in original (vector similarity) order."""
132
+ return [
133
+ RerankResult(index=i, score=1.0 - i * 0.01, text=doc)
134
+ for i, doc in enumerate(documents[:top_n])
135
+ ]
136
+
137
+
138
+ def create_reranker(config: dict) -> Reranker | None:
139
+ """Create a Reranker from config.yaml, or None if disabled.
140
+
141
+ Config format:
142
+ rerank:
143
+ enabled: true
144
+ provider: "cohere"
145
+ models:
146
+ cohere:
147
+ model: "rerank-v3.5"
148
+ api_key_env: "COHERE_API_KEY" # Cohere native key
149
+ """
150
+ rerank_cfg = config.get("rerank", {})
151
+ if not rerank_cfg.get("enabled", False):
152
+ _log.info("[create_reranker] rerank disabled in config")
153
+ return None
154
+
155
+ provider = rerank_cfg.get("provider", "cohere")
156
+ model_cfg = rerank_cfg.get("models", {}).get(provider, {})
157
+
158
+ if not model_cfg:
159
+ _log.warning(f"[create_reranker] no config for provider={provider}")
160
+ return None
161
+
162
+ api_key_env = model_cfg.get("api_key_env", "COHERE_API_KEY")
163
+ if not os.getenv(api_key_env, ""):
164
+ _log.warning(f"[create_reranker] env var {api_key_env} not set, "
165
+ f"rerank will be disabled")
166
+ return None
167
+
168
+ proxy_cfg = config.get("proxy", {})
169
+ proxy_url = None
170
+ if proxy_cfg.get("enabled", False):
171
+ proxy_url = proxy_cfg.get("https") or proxy_cfg.get("http")
172
+
173
+ return Reranker(
174
+ model=model_cfg.get("model", "rerank-v3.5"),
175
+ api_key_env=api_key_env,
176
+ proxy=proxy_url,
177
+ )