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,58 @@
|
|
|
1
|
+
"""OpenAI-compatible embedding provider (OpenAI, OpenRouter or any compatible base_url)."""
|
|
2
|
+
from .base import BaseEmbedding
|
|
3
|
+
from ...config import get_api_key, get_proxy_url
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class OpenAIEmbedding(BaseEmbedding):
|
|
7
|
+
|
|
8
|
+
def __init__(self, model: str = "openai/text-embedding-3-large", dimension: int = 3072,
|
|
9
|
+
api_key_env: str = "OPENROUTER_API_KEY", base_url: str = "https://openrouter.ai/api/v1",
|
|
10
|
+
timeout: float = 30.0):
|
|
11
|
+
from openai import OpenAI
|
|
12
|
+
import httpx
|
|
13
|
+
|
|
14
|
+
self._model = model
|
|
15
|
+
self._dimension = dimension
|
|
16
|
+
|
|
17
|
+
proxy = get_proxy_url()
|
|
18
|
+
self._http_client = httpx.Client(proxy=proxy) if proxy else None
|
|
19
|
+
|
|
20
|
+
# One retry and a short timeout: a bad key or a down endpoint must not
|
|
21
|
+
# stall a query for long; the retriever falls back to keyword search.
|
|
22
|
+
self._client = OpenAI(
|
|
23
|
+
api_key=get_api_key(api_key_env),
|
|
24
|
+
base_url=base_url,
|
|
25
|
+
http_client=self._http_client,
|
|
26
|
+
max_retries=1,
|
|
27
|
+
timeout=timeout,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def close(self) -> None:
|
|
31
|
+
"""Close the underlying httpx.Client, if any."""
|
|
32
|
+
if self._http_client is not None:
|
|
33
|
+
try:
|
|
34
|
+
self._http_client.close()
|
|
35
|
+
except Exception:
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
def __enter__(self) -> "OpenAIEmbedding":
|
|
39
|
+
return self
|
|
40
|
+
|
|
41
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
42
|
+
self.close()
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def model_name(self) -> str:
|
|
46
|
+
return self._model
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def dimension(self) -> int:
|
|
50
|
+
return self._dimension
|
|
51
|
+
|
|
52
|
+
def embed_texts(self, texts: list[str]) -> list[list[float]]:
|
|
53
|
+
response = self._client.embeddings.create(model=self._model, input=texts)
|
|
54
|
+
return [item.embedding for item in response.data]
|
|
55
|
+
|
|
56
|
+
def embed_query(self, query: str) -> list[float]:
|
|
57
|
+
response = self._client.embeddings.create(model=self._model, input=[query])
|
|
58
|
+
return response.data[0].embedding
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"""Minimal chat-completions client (OpenAI-compatible, used through OpenRouter).
|
|
2
|
+
|
|
3
|
+
Used by the offline pipeline tools (SDK summarisation, quality agents) and by
|
|
4
|
+
the optional query rewriting in the retriever. Configuration comes from the
|
|
5
|
+
`llm` section of the config; the key from the environment variable named in
|
|
6
|
+
`api_key_env`. The MCP server never needs it.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
from typing import Any, Generator
|
|
14
|
+
|
|
15
|
+
import time
|
|
16
|
+
|
|
17
|
+
import httpx
|
|
18
|
+
|
|
19
|
+
# SDK summarisation calls run long; use a generous timeout.
|
|
20
|
+
_DEFAULT_TIMEOUT = 120.0
|
|
21
|
+
|
|
22
|
+
# Transient statuses (OpenRouter / upstream hiccups) that are retried.
|
|
23
|
+
# 403 has been observed as a transient refusal from Anthropic via OpenRouter.
|
|
24
|
+
_RETRYABLE_STATUS = frozenset({403, 408, 409, 429, 500, 502, 503, 504, 529})
|
|
25
|
+
_MAX_RETRIES = 4 # total attempts = 1 + retries
|
|
26
|
+
_BACKOFF_BASE = 1.5 # backoff base in seconds: 1.5, 3.0, 4.5 ...
|
|
27
|
+
_MAX_403_RETRIES = 1 # 403 is usually permissions/quota: retry once, then give up
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class LLMClient:
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
base_url: str,
|
|
34
|
+
api_key: str,
|
|
35
|
+
model: str,
|
|
36
|
+
temperature: float = 0.3,
|
|
37
|
+
max_tokens: int = 4096,
|
|
38
|
+
timeout: float = _DEFAULT_TIMEOUT,
|
|
39
|
+
proxy: str | None = None,
|
|
40
|
+
):
|
|
41
|
+
self.base_url = base_url.rstrip("/")
|
|
42
|
+
self.api_key = api_key
|
|
43
|
+
self.model = model
|
|
44
|
+
self.temperature = temperature
|
|
45
|
+
self.max_tokens = max_tokens
|
|
46
|
+
|
|
47
|
+
# Configure HTTP proxy if provided (helps with Gemini routing in restricted networks)
|
|
48
|
+
if proxy:
|
|
49
|
+
self._client = httpx.Client(
|
|
50
|
+
timeout=timeout,
|
|
51
|
+
proxy=proxy,
|
|
52
|
+
)
|
|
53
|
+
else:
|
|
54
|
+
self._client = httpx.Client(timeout=timeout)
|
|
55
|
+
|
|
56
|
+
def close(self) -> None:
|
|
57
|
+
"""Close the underlying httpx.Client and its connection pool."""
|
|
58
|
+
try:
|
|
59
|
+
self._client.close()
|
|
60
|
+
except Exception:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
def __enter__(self) -> "LLMClient":
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
67
|
+
self.close()
|
|
68
|
+
|
|
69
|
+
def _build_request(
|
|
70
|
+
self,
|
|
71
|
+
prompt: str,
|
|
72
|
+
system_prompt: str | None = None,
|
|
73
|
+
stream: bool = False,
|
|
74
|
+
messages: list[dict] | None = None,
|
|
75
|
+
) -> tuple[str, dict, dict]:
|
|
76
|
+
"""Build request URL, headers, and payload.
|
|
77
|
+
|
|
78
|
+
If `messages` is provided it is used verbatim as the chat messages array
|
|
79
|
+
(structured multi-turn: system + per-role turns). Otherwise a simple
|
|
80
|
+
[system, user] pair is built from `system_prompt` / `prompt`.
|
|
81
|
+
"""
|
|
82
|
+
url = f"{self.base_url}/chat/completions"
|
|
83
|
+
headers = {
|
|
84
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
85
|
+
"Content-Type": "application/json",
|
|
86
|
+
"X-Title": "revit-api-docs",
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if messages is not None:
|
|
90
|
+
msgs = messages
|
|
91
|
+
else:
|
|
92
|
+
_system = system_prompt or (
|
|
93
|
+
"You are an expert assistant for summarizing Revit SDK C# sample code. "
|
|
94
|
+
"Always respond in English."
|
|
95
|
+
)
|
|
96
|
+
msgs = [
|
|
97
|
+
{"role": "system", "content": _system},
|
|
98
|
+
{"role": "user", "content": prompt},
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
payload: dict[str, Any] = {
|
|
102
|
+
"model": self.model,
|
|
103
|
+
"messages": msgs,
|
|
104
|
+
"temperature": self.temperature,
|
|
105
|
+
"max_tokens": self.max_tokens,
|
|
106
|
+
"stream": stream,
|
|
107
|
+
}
|
|
108
|
+
return url, headers, payload
|
|
109
|
+
|
|
110
|
+
def generate_text(self, prompt: str, system_prompt: str | None = None) -> str:
|
|
111
|
+
"""Chat-completions call; returns the first message's content.
|
|
112
|
+
|
|
113
|
+
Retries transient statuses with a short backoff (see _RETRYABLE_STATUS).
|
|
114
|
+
"""
|
|
115
|
+
url, headers, payload = self._build_request(prompt, system_prompt, stream=False)
|
|
116
|
+
|
|
117
|
+
import logging
|
|
118
|
+
_llm_log = logging.getLogger("revit_api_docs.llm_client")
|
|
119
|
+
|
|
120
|
+
resp = None
|
|
121
|
+
last_err: Exception | None = None
|
|
122
|
+
consecutive_403 = 0
|
|
123
|
+
for attempt in range(1, _MAX_RETRIES + 1):
|
|
124
|
+
try:
|
|
125
|
+
resp = self._client.post(url, headers=headers, json=payload)
|
|
126
|
+
if resp.status_code in _RETRYABLE_STATUS and attempt < _MAX_RETRIES:
|
|
127
|
+
# 403 circuit breaker: stop after _MAX_403_RETRIES so a lasting
|
|
128
|
+
# permission/quota error is not retried as if transient.
|
|
129
|
+
if resp.status_code == 403:
|
|
130
|
+
consecutive_403 += 1
|
|
131
|
+
if consecutive_403 > _MAX_403_RETRIES:
|
|
132
|
+
_llm_log.warning(
|
|
133
|
+
f"[generate_text] {self.model} HTTP 403 x{consecutive_403} "
|
|
134
|
+
"— circuit break, no more retries."
|
|
135
|
+
)
|
|
136
|
+
resp.raise_for_status()
|
|
137
|
+
else:
|
|
138
|
+
consecutive_403 = 0
|
|
139
|
+
body = resp.text[:300].replace("\n", " ")
|
|
140
|
+
wait = _BACKOFF_BASE * attempt
|
|
141
|
+
_llm_log.warning(
|
|
142
|
+
f"[generate_text] {self.model} HTTP {resp.status_code} "
|
|
143
|
+
f"(attempt {attempt}/{_MAX_RETRIES}) — retrying in {wait:.1f}s. Body: {body}"
|
|
144
|
+
)
|
|
145
|
+
time.sleep(wait)
|
|
146
|
+
continue
|
|
147
|
+
resp.raise_for_status()
|
|
148
|
+
break
|
|
149
|
+
except (httpx.TransportError, httpx.TimeoutException) as e:
|
|
150
|
+
# transport-level hiccup: retry as well
|
|
151
|
+
last_err = e
|
|
152
|
+
if attempt < _MAX_RETRIES:
|
|
153
|
+
wait = _BACKOFF_BASE * attempt
|
|
154
|
+
_llm_log.warning(
|
|
155
|
+
f"[generate_text] {self.model} transport error "
|
|
156
|
+
f"(attempt {attempt}/{_MAX_RETRIES}): {e} — retrying in {wait:.1f}s"
|
|
157
|
+
)
|
|
158
|
+
time.sleep(wait)
|
|
159
|
+
continue
|
|
160
|
+
raise
|
|
161
|
+
except httpx.HTTPStatusError as e:
|
|
162
|
+
# non-retryable status or retries exhausted: raise with the body for diagnosis
|
|
163
|
+
body = e.response.text[:500] if e.response is not None else ""
|
|
164
|
+
raise httpx.HTTPStatusError(
|
|
165
|
+
f"{e} | OpenRouter response: {body}",
|
|
166
|
+
request=e.request, response=e.response,
|
|
167
|
+
) from None
|
|
168
|
+
|
|
169
|
+
if resp is None: # unreachable in practice
|
|
170
|
+
raise last_err or RuntimeError("generate_text: no response")
|
|
171
|
+
|
|
172
|
+
data = resp.json()
|
|
173
|
+
|
|
174
|
+
choices = data.get("choices") or []
|
|
175
|
+
if not choices:
|
|
176
|
+
return ""
|
|
177
|
+
|
|
178
|
+
choice = choices[0]
|
|
179
|
+
finish_reason = choice.get("finish_reason", "unknown")
|
|
180
|
+
message = choice.get("message") or {}
|
|
181
|
+
content = message.get("content") or ""
|
|
182
|
+
|
|
183
|
+
_llm_log.info(
|
|
184
|
+
f"[generate_text] finish_reason={finish_reason} "
|
|
185
|
+
f"content_len={len(content)} max_tokens={self.max_tokens}"
|
|
186
|
+
)
|
|
187
|
+
if finish_reason == "length":
|
|
188
|
+
_llm_log.warning(
|
|
189
|
+
f"[generate_text] RESPONSE TRUNCATED — hit max_tokens={self.max_tokens}. "
|
|
190
|
+
f"Increase max_tokens in config."
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return str(content)
|
|
194
|
+
|
|
195
|
+
def generate_stream(
|
|
196
|
+
self, prompt: str, system_prompt: str | None = None,
|
|
197
|
+
messages: list[dict] | None = None,
|
|
198
|
+
) -> Generator[str, None, None]:
|
|
199
|
+
"""Stream tokens over SSE. `messages` may carry a structured multi-turn
|
|
200
|
+
conversation (system + per-role entries) instead of `prompt`."""
|
|
201
|
+
url, headers, payload = self._build_request(
|
|
202
|
+
prompt, system_prompt, stream=True, messages=messages,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
with self._client.stream("POST", url, headers=headers, json=payload) as resp:
|
|
206
|
+
resp.raise_for_status()
|
|
207
|
+
for line in resp.iter_lines():
|
|
208
|
+
if not line or not line.startswith("data: "):
|
|
209
|
+
continue
|
|
210
|
+
data_str = line[6:] # strip "data: "
|
|
211
|
+
if data_str.strip() == "[DONE]":
|
|
212
|
+
break
|
|
213
|
+
try:
|
|
214
|
+
chunk = json.loads(data_str)
|
|
215
|
+
delta = chunk.get("choices", [{}])[0].get("delta", {})
|
|
216
|
+
token = delta.get("content", "")
|
|
217
|
+
if token:
|
|
218
|
+
yield token
|
|
219
|
+
except (json.JSONDecodeError, IndexError):
|
|
220
|
+
continue
|
|
221
|
+
|
|
222
|
+
def stream_print(
|
|
223
|
+
self, prompt: str, system_prompt: str | None = None
|
|
224
|
+
) -> str:
|
|
225
|
+
"""Stream to stdout as tokens arrive; return the full text."""
|
|
226
|
+
full_text = []
|
|
227
|
+
for token in self.generate_stream(prompt, system_prompt):
|
|
228
|
+
print(token, end="", flush=True)
|
|
229
|
+
full_text.append(token)
|
|
230
|
+
print() # final newline
|
|
231
|
+
return "".join(full_text)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def create_llm_client(config: dict[str, Any], provider_override: str | None = None) -> LLMClient:
|
|
235
|
+
"""Build an LLMClient from config["llm"].
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
config: the loaded config dict
|
|
239
|
+
provider_override: use this llm.models entry instead of llm.provider
|
|
240
|
+
"""
|
|
241
|
+
llm_cfg = config.get("llm", {})
|
|
242
|
+
provider = provider_override or llm_cfg.get("provider", "claude")
|
|
243
|
+
models_cfg = llm_cfg.get("models", {})
|
|
244
|
+
model_cfg = models_cfg.get(provider, {})
|
|
245
|
+
|
|
246
|
+
if not model_cfg:
|
|
247
|
+
available = list(models_cfg.keys())
|
|
248
|
+
raise RuntimeError(
|
|
249
|
+
f"no llm.models entry for provider={provider!r} in the config; "
|
|
250
|
+
f"available: {available}"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
model = model_cfg.get("model")
|
|
254
|
+
base_url = model_cfg.get("base_url") or config.get("openrouter", {}).get("base_url")
|
|
255
|
+
api_key_env = (
|
|
256
|
+
model_cfg.get("api_key_env")
|
|
257
|
+
or config.get("openrouter", {}).get("api_key_env", "OPENROUTER_API_KEY")
|
|
258
|
+
)
|
|
259
|
+
api_key = os.getenv(api_key_env, "")
|
|
260
|
+
|
|
261
|
+
if not model:
|
|
262
|
+
raise RuntimeError(f"llm.models.{provider}.model is not set in the config")
|
|
263
|
+
if not base_url:
|
|
264
|
+
raise RuntimeError(f"llm.models.{provider}.base_url is not set in the config")
|
|
265
|
+
if not api_key:
|
|
266
|
+
raise RuntimeError(
|
|
267
|
+
f"environment variable {api_key_env} is not set; it must hold the API key "
|
|
268
|
+
f"for the {provider} model"
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
temperature = llm_cfg.get("temperature", 0.3)
|
|
272
|
+
max_tokens = llm_cfg.get("max_tokens", 4096)
|
|
273
|
+
|
|
274
|
+
# Proxy: model-level override > global proxy config
|
|
275
|
+
proxy_cfg = config.get("proxy", {})
|
|
276
|
+
proxy_url: str | None = None
|
|
277
|
+
if proxy_cfg.get("enabled", False):
|
|
278
|
+
proxy_url = proxy_cfg.get("https") or proxy_cfg.get("http")
|
|
279
|
+
|
|
280
|
+
return LLMClient(
|
|
281
|
+
base_url=base_url,
|
|
282
|
+
api_key=api_key,
|
|
283
|
+
model=model,
|
|
284
|
+
temperature=temperature,
|
|
285
|
+
max_tokens=max_tokens,
|
|
286
|
+
proxy=proxy_url,
|
|
287
|
+
)
|
|
288
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Prompt template loader.
|
|
2
|
+
|
|
3
|
+
Prompt text lives in this package so prompts can be reviewed and tuned without
|
|
4
|
+
digging through runtime code.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from functools import lru_cache
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
_PROMPT_DIR = Path(__file__).resolve().parent
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@lru_cache(maxsize=None)
|
|
15
|
+
def load_prompt(name: str) -> str:
|
|
16
|
+
"""Load a prompt template by filename from the prompts directory."""
|
|
17
|
+
if "/" in name or "\\" in name or name.startswith("."):
|
|
18
|
+
raise ValueError(f"Invalid prompt name: {name!r}")
|
|
19
|
+
path = _PROMPT_DIR / name
|
|
20
|
+
return path.read_text(encoding="utf-8")
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Audit the following parsed Revit API record for data quality.
|
|
2
|
+
|
|
3
|
+
No raw HTML is available. Be conservative: without HTML you cannot know if missing fields are parser failures or absent source documentation. Only deduct for clearly detectable issues.
|
|
4
|
+
|
|
5
|
+
## Score Criteria
|
|
6
|
+
- -0.5 name/full_id indicates a constructor (`#ctor` or `Constructor`) -> noise.
|
|
7
|
+
- -0.3 summary or info is clearly generic boilerplate.
|
|
8
|
+
- -0.25 parameters field is empty but syntax shows the method takes arguments with actual parameter names.
|
|
9
|
+
- -0.1 any field contains raw HTML tags, garbled text, or excessive whitespace.
|
|
10
|
+
|
|
11
|
+
Do not deduct for empty summary/info, missing syntax, or missing parameters when source absence is plausible.
|
|
12
|
+
|
|
13
|
+
Parsed record:
|
|
14
|
+
name : {name}
|
|
15
|
+
full_id : {full_id}
|
|
16
|
+
syntax : {syntax}
|
|
17
|
+
summary : {summary}
|
|
18
|
+
info : {info}
|
|
19
|
+
parameters : {parameters}
|
|
20
|
+
|
|
21
|
+
Return ONLY a JSON object:
|
|
22
|
+
{{
|
|
23
|
+
"quality_score": 0.8,
|
|
24
|
+
"issues": [],
|
|
25
|
+
"needs_rewrite": false
|
|
26
|
+
}}
|
|
27
|
+
|
|
28
|
+
Use threshold {threshold}. JSON only. No markdown. No explanation.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
You are a data-quality auditor for Revit API documentation records. You measure parse quality: how well the parser captured information that exists in the HTML. Always reply with a single valid JSON object and nothing else.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Audit the following parsed Revit API record by comparing it against the raw HTML source.
|
|
2
|
+
|
|
3
|
+
## Principle
|
|
4
|
+
Measure parse quality, not documentation completeness. Only deduct points when the HTML source contains information that the parsed record is missing or captured incorrectly. If the HTML lacks a field, an empty parsed field is correct.
|
|
5
|
+
|
|
6
|
+
## Score Criteria
|
|
7
|
+
Cumulative deductions from 1.0, only when HTML evidence exists:
|
|
8
|
+
- -0.5 name/full_id indicates a constructor (`#ctor` or `Constructor`) -> noise record.
|
|
9
|
+
- -0.4 summary and info are both empty but HTML contains a real description.
|
|
10
|
+
- -0.3 summary/info is generic boilerplate while HTML has a real description.
|
|
11
|
+
- -0.25 parameters field is empty but HTML shows method parameters with descriptions.
|
|
12
|
+
- -0.2 syntax/C# signature is missing but HTML contains a public C# signature.
|
|
13
|
+
- -0.2 important HTML content such as description or return value is absent from parsed fields.
|
|
14
|
+
- -0.15 all parameter types are `Unknown Type` but HTML has actual type information.
|
|
15
|
+
- -0.1 any field contains raw HTML tags, garbled unicode, or excessive whitespace.
|
|
16
|
+
|
|
17
|
+
Parsed record:
|
|
18
|
+
name : {name}
|
|
19
|
+
full_id : {full_id}
|
|
20
|
+
syntax : {syntax}
|
|
21
|
+
summary : {summary}
|
|
22
|
+
info : {info}
|
|
23
|
+
parameters : {parameters}
|
|
24
|
+
|
|
25
|
+
Raw HTML excerpt:
|
|
26
|
+
{html_excerpt}
|
|
27
|
+
|
|
28
|
+
Return ONLY a JSON object:
|
|
29
|
+
{{
|
|
30
|
+
"quality_score": 0.0,
|
|
31
|
+
"issues": ["short English issue strings"],
|
|
32
|
+
"needs_rewrite": true
|
|
33
|
+
}}
|
|
34
|
+
|
|
35
|
+
Use threshold {threshold}. JSON only. No markdown. No explanation.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
The following Revit API record has quality issues. Use the raw HTML as ground truth to produce corrected fields.
|
|
2
|
+
|
|
3
|
+
Identified issues:
|
|
4
|
+
{issues}
|
|
5
|
+
|
|
6
|
+
Original parsed record:
|
|
7
|
+
name : {name}
|
|
8
|
+
full_id : {full_id}
|
|
9
|
+
syntax : {syntax}
|
|
10
|
+
summary : {summary}
|
|
11
|
+
info : {info}
|
|
12
|
+
parameters : {parameters}
|
|
13
|
+
remark : {remark}
|
|
14
|
+
|
|
15
|
+
Raw HTML excerpt:
|
|
16
|
+
{html_excerpt}
|
|
17
|
+
|
|
18
|
+
## Rules
|
|
19
|
+
- Only include fields where you can make a genuine improvement based on HTML evidence.
|
|
20
|
+
- If the original field already matches the HTML, do not include it.
|
|
21
|
+
- If a field is empty in both the record and the HTML, omit it.
|
|
22
|
+
- Do not fabricate content.
|
|
23
|
+
- Keep output concise.
|
|
24
|
+
|
|
25
|
+
Return ONLY a JSON object with improved fields:
|
|
26
|
+
{{
|
|
27
|
+
"summary": "clear one-sentence English description",
|
|
28
|
+
"parameters": "one parameter per line, or empty string if none",
|
|
29
|
+
"syntax": "correct C# public signature from HTML",
|
|
30
|
+
"info": "concise description if summary is missing"
|
|
31
|
+
}}
|
|
32
|
+
|
|
33
|
+
Omit unchanged fields. JSON only. No markdown. No explanation.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
You are an expert Revit API documentation writer. Repair and improve incomplete or garbled API records using raw HTML as the authoritative reference. Always reply with a single valid JSON object and nothing else.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
You are a Revit API expert. Given a user query, possibly in Chinese, extract all relevant Revit API class names, method names, properties, enums, and English technical keywords.
|
|
2
|
+
|
|
3
|
+
## Rules
|
|
4
|
+
1. Translate non-English domain terms to exact Revit API equivalents.
|
|
5
|
+
2. Entity nouns are primary search targets. Action verbs are secondary qualifiers.
|
|
6
|
+
3. Strip natural-language filler words and focus on API entities.
|
|
7
|
+
4. Be exhaustive: include parent classes, related interfaces, relevant enum types, BuiltInParameter, and BuiltInCategory names.
|
|
8
|
+
5. Include full namespace paths when useful, for example `Autodesk.Revit.DB.Wall`.
|
|
9
|
+
6. Include method signatures, property names, and likely overload terms.
|
|
10
|
+
7. Think about what a developer would search for when implementing this feature.
|
|
11
|
+
8. Output ONLY a JSON object.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
- "结构柱" -> {{"keywords": "structural column FamilyInstance BuiltInCategory.OST_StructuralColumns NewFamilyInstance StructuralType", "api_terms": ["FamilyInstance", "FamilySymbol", "NewFamilyInstance", "StructuralType", "BuiltInCategory.OST_StructuralColumns", "Level", "XYZ"]}}
|
|
15
|
+
- "创建墙体" -> {{"keywords": "create wall Wall.Create WallType Level Line CurveLoop", "api_terms": ["Wall", "Wall.Create", "WallType", "WallUtils", "CurtainGrid", "Line.CreateBound", "Level"]}}
|
|
16
|
+
- "i want get wall created api" -> {{"keywords": "Wall Wall.Create WallType Level Line", "api_terms": ["Wall", "Wall.Create", "WallType", "WallUtils", "Line.CreateBound", "Level", "FilteredElementCollector"]}}
|
|
17
|
+
- "获取房间面积" -> {{"keywords": "room area Room get_Area SpatialElement BoundarySegment", "api_terms": ["Room", "SpatialElement", "Area", "Room.Area", "Room.get_BoundarySegments", "SpatialElementBoundaryOptions"]}}
|
|
18
|
+
- "修改墙类型" -> {{"keywords": "change wall type WallType ChangeTypeId Element.ChangeTypeId GetTypeId", "api_terms": ["Element.ChangeTypeId", "Element.GetTypeId", "WallType", "FilteredElementCollector", "Wall"]}}
|
|
19
|
+
- "Part" -> {{"keywords": "Part PartUtils PartMaker CreateParts DivideParts PartType", "api_terms": ["Part", "PartUtils", "PartMaker", "PartUtils.CreateParts", "PartUtils.AreElementsValidForCreateParts"]}}
|
|
20
|
+
|
|
21
|
+
User query: {query}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
You are a Revit API keyword extractor. Output JSON only.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
You are an AI assistant specializing in codebase analysis and extracting structured data from technical documentation.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Role
|
|
2
|
+
You are an AI assistant specializing in codebase analysis and extracting structured data from technical documentation.
|
|
3
|
+
|
|
4
|
+
# Goal
|
|
5
|
+
Parse the provided ReadMe text to extract key identifiers for code. Output will be used programmatically by an automated code retrieval system.
|
|
6
|
+
|
|
7
|
+
# Instructions
|
|
8
|
+
1. Carefully analyze the text provided.
|
|
9
|
+
2. Extract:
|
|
10
|
+
- `target_files`: all project source filenames explicitly mentioned.
|
|
11
|
+
- `key_classes_and_methods`: custom class or method names responsible for core functionality.
|
|
12
|
+
- `mentioned_apis`: key API classes from external frameworks such as `Autodesk.Revit.DB.View`.
|
|
13
|
+
3. Return a single strict JSON object.
|
|
14
|
+
4. If no information is found for a field, use an empty list `[]`.
|
|
15
|
+
5. Output only raw JSON. No markdown. No explanation.
|
|
16
|
+
|
|
17
|
+
# Example
|
|
18
|
+
Input: "This tool is in `Processor.cs`. Core logic is in `DataParser` using `Autodesk.Revit.DB.Transaction`."
|
|
19
|
+
Output: {{"target_files": ["Processor.cs"], "key_classes_and_methods": ["DataParser"], "mentioned_apis": ["Autodesk.Revit.DB.Transaction"]}}
|
|
20
|
+
|
|
21
|
+
# README Content
|
|
22
|
+
{readme_text}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Role
|
|
2
|
+
You are an expert C# software architect and technical writer specializing in the Autodesk Revit API.
|
|
3
|
+
|
|
4
|
+
# Goal
|
|
5
|
+
Analyze the ReadMe context and raw C# code details. Identify the single most relevant code block representing the core functionality described in the ReadMe, then synthesize it into a clean, reusable, documented Golden Code Snippet for a RAG knowledge base.
|
|
6
|
+
|
|
7
|
+
# Context
|
|
8
|
+
<ReadMeContext>
|
|
9
|
+
{readme_summary}
|
|
10
|
+
</ReadMeContext>
|
|
11
|
+
|
|
12
|
+
<RawCodeDetails>
|
|
13
|
+
{detail_code_analysis}
|
|
14
|
+
</RawCodeDetails>
|
|
15
|
+
|
|
16
|
+
# Instructions
|
|
17
|
+
1. Read `<ReadMeContext>` to understand the project's main purpose and key APIs.
|
|
18
|
+
2. Examine `<RawCodeDetails>` and select the one method or block that most directly implements the core functionality.
|
|
19
|
+
3. Remove UI interaction code, logging/debugging, generic file I/O unless core, and `IExternalCommand.Execute` boilerplate.
|
|
20
|
+
4. Refactor for reuse with a clear method signature, typed parameters, and hardcoded values converted to parameters.
|
|
21
|
+
5. Keep Transaction handling appropriate for standalone SDK examples.
|
|
22
|
+
6. Generate XML documentation comments.
|
|
23
|
+
7. Output one complete syntactically correct C# method block.
|
|
24
|
+
|
|
25
|
+
# Output
|
|
26
|
+
Return ONLY a JSON object:
|
|
27
|
+
{{
|
|
28
|
+
"summary": "One sentence English description of what this code demonstrates and which Revit APIs it uses.",
|
|
29
|
+
"content": "The complete clean C# method with XML documentation comments."
|
|
30
|
+
}}
|
|
31
|
+
|
|
32
|
+
JSON only. No markdown. No explanation outside JSON.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
You are an expert C# software architect and technical writer specializing in the Autodesk Revit API. You identify core logic and refactor it into clear, concise, educational code examples. Always reply with a single valid JSON object and nothing else.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Analyze these C# files from the Revit SDK project "{project_name}".
|
|
2
|
+
|
|
3
|
+
Project context: {project_summary}
|
|
4
|
+
|
|
5
|
+
{file_sections}
|
|
6
|
+
|
|
7
|
+
Return a JSON array with one object per file in the same order:
|
|
8
|
+
[
|
|
9
|
+
{{
|
|
10
|
+
"filename": "exact filename",
|
|
11
|
+
"file_purpose": "1-sentence English description of what this file does",
|
|
12
|
+
"key_classes": ["ClassName1", "ClassName2"],
|
|
13
|
+
"key_methods": ["MethodName1 - brief description", "MethodName2 - brief description"]
|
|
14
|
+
}}
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
JSON array only. No markdown. No explanation.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
You are a Revit SDK code analyst. Analyze C# source files and extract metadata. Always reply with a single valid JSON array and nothing else.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Analyze this Revit SDK sample project.
|
|
2
|
+
|
|
3
|
+
Project name: {project_name}
|
|
4
|
+
Number of C# files: {file_count}
|
|
5
|
+
|
|
6
|
+
README content:
|
|
7
|
+
{readme}
|
|
8
|
+
|
|
9
|
+
File listing with code previews:
|
|
10
|
+
{file_previews}
|
|
11
|
+
|
|
12
|
+
Return a JSON object with:
|
|
13
|
+
{{
|
|
14
|
+
"project_summary": "2-3 sentence English description of what this project demonstrates",
|
|
15
|
+
"api_classes_used": ["main Revit API classes/types used"],
|
|
16
|
+
"key_patterns": ["design patterns demonstrated"],
|
|
17
|
+
"use_case_category": "geometry|family|view|structure|mep|annotation|export|utility|ui|analysis|database|other"
|
|
18
|
+
}}
|
|
19
|
+
|
|
20
|
+
JSON only. No markdown. No explanation.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
You are a Revit SDK code analyst. Analyze C# sample projects that demonstrate Revit API usage patterns. Always reply with a single valid JSON object and nothing else.
|