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.
- revit_api_docs/__init__.py +18 -0
- revit_api_docs/__main__.py +8 -0
- revit_api_docs/config.py +115 -0
- revit_api_docs/data.py +320 -0
- revit_api_docs/default_config.yaml +85 -0
- revit_api_docs/embedder/__init__.py +1 -0
- revit_api_docs/embedder/providers/__init__.py +25 -0
- revit_api_docs/embedder/providers/base.py +28 -0
- revit_api_docs/embedder/providers/google.py +36 -0
- revit_api_docs/embedder/providers/openai.py +58 -0
- revit_api_docs/llm_client.py +288 -0
- revit_api_docs/prompts/__init__.py +5 -0
- revit_api_docs/prompts/loader.py +20 -0
- revit_api_docs/prompts/pipeline.api_quality_stage1_no_html.md +28 -0
- revit_api_docs/prompts/pipeline.api_quality_stage1_system.md +1 -0
- revit_api_docs/prompts/pipeline.api_quality_stage1_with_html.md +35 -0
- revit_api_docs/prompts/pipeline.api_quality_stage2.md +33 -0
- revit_api_docs/prompts/pipeline.api_quality_stage2_system.md +1 -0
- revit_api_docs/prompts/pipeline.rewrite_query.md +21 -0
- revit_api_docs/prompts/pipeline.rewrite_query_system.md +1 -0
- revit_api_docs/prompts/pipeline.sdk_extract_readme_system.md +1 -0
- revit_api_docs/prompts/pipeline.sdk_extract_readme_user.md +22 -0
- revit_api_docs/prompts/pipeline.sdk_golden_code.md +32 -0
- revit_api_docs/prompts/pipeline.sdk_golden_code_system.md +1 -0
- revit_api_docs/prompts/pipeline.sdk_quality_file_batch.md +17 -0
- revit_api_docs/prompts/pipeline.sdk_quality_file_system.md +1 -0
- revit_api_docs/prompts/pipeline.sdk_quality_project.md +20 -0
- revit_api_docs/prompts/pipeline.sdk_quality_project_system.md +1 -0
- revit_api_docs/reranker.py +177 -0
- revit_api_docs/retriever.py +901 -0
- revit_api_docs/server.py +244 -0
- revit_api_docs-0.1.0.dist-info/METADATA +139 -0
- revit_api_docs-0.1.0.dist-info/RECORD +36 -0
- revit_api_docs-0.1.0.dist-info/WHEEL +4 -0
- revit_api_docs-0.1.0.dist-info/entry_points.txt +2 -0
- 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
|
+
)
|