Spaces:
Sleeping
Sleeping
File size: 5,674 Bytes
005e9fd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | """Constants and static configuration, shared by the app and the offline scripts."""
from dataclasses import dataclass
from pathlib import Path
# --- Paths -----------------------------------------------------------------
PROJECT_ROOT = Path(__file__).resolve().parent.parent
DATA_DIR = PROJECT_ROOT / "data"
SOURCES_DIR = DATA_DIR / "sources"
MANIFEST_PATH = DATA_DIR / "manifest.json"
CHUNKS_PATH = DATA_DIR / "chunks.jsonl"
EVAL_DIR = PROJECT_ROOT / "eval"
EVAL_DATASET_PATH = EVAL_DIR / "dataset.json"
# --- Corpus ----------------------------------------------------------------
@dataclass(frozen=True)
class BookSpec:
"""One of the source books that make up the corpus.
`repo` and `sha` tell us which repository to clone and at which commit, and
`base_url` is where the published pages live so that citations can link to
them. We keep a base URL per book rather than deriving one, because
async-book is published on rust-lang.github.io while the other four are on
doc.rust-lang.org.
`exclude_parts` lists mdBook part headings whose chapters we skip. async-book
keeps its pre-rewrite chapters under "Old chapters", and since those
contradict the current text we leave them out of the corpus.
`exclude_paths` lists individual chapters to skip. The Reference already
turns off search for three generated index pages in its own `book.toml`, and
they are machine-generated tables rather than prose, so we follow that
decision.
"""
title: str
repo: str
sha: str
base_url: str
license: str
exclude_parts: tuple[str, ...] = ()
exclude_paths: tuple[str, ...] = ()
BOOKS: dict[str, BookSpec] = {
"book": BookSpec(
title="The Rust Programming Language",
repo="rust-lang/book",
sha="917544888a55",
base_url="https://doc.rust-lang.org/book/",
license="MIT OR Apache-2.0",
),
"reference": BookSpec(
title="The Rust Reference",
repo="rust-lang/reference",
sha="bec6b5e6631b",
base_url="https://doc.rust-lang.org/reference/",
license="MIT OR Apache-2.0",
exclude_paths=("grammar.md", "syntax-index.md", "test-summary.md"),
),
"rust-by-example": BookSpec(
title="Rust by Example",
repo="rust-lang/rust-by-example",
sha="15308f3e9518",
base_url="https://doc.rust-lang.org/rust-by-example/",
license="MIT OR Apache-2.0",
),
"nomicon": BookSpec(
title="The Rustonomicon",
repo="rust-lang/nomicon",
sha="5012a37c682b",
base_url="https://doc.rust-lang.org/nomicon/",
license="MIT OR Apache-2.0",
),
"async-book": BookSpec(
title="Asynchronous Programming in Rust",
repo="rust-lang/async-book",
sha="43891cedf954",
base_url="https://rust-lang.github.io/async-book/",
license="MIT",
exclude_parts=("Old chapters",),
),
}
# --- Chunking --------------------------------------------------------------
HEADING_SEPARATOR = " › "
EMBED_MODEL = "Qwen/Qwen3-Embedding-0.6B"
EMBED_DIM = 1024
# Sized to stay cheap to retrieve and read while still holding a useful amount of
# information. Counted with the embedding model's own tokenizer, so changing
# EMBED_MODEL re-chunks the corpus, and the index and the eval dataset have to be
# rebuilt with it.
CHUNK_TARGET_TOKENS = 400
CHUNK_MAX_TOKENS = 512
# Headings at or above this level start a new chunk. We use h3 rather than h1
# because the Book's chapter pages open at h2 with their subsections at h3.
CHUNK_HEADING_LEVEL = 3
# Qwen3-Embedding embeds a query and a document differently: its own
# `config_sentence_transformers.json` sets `prompts.query` to this instruction and
# `prompts.document` to an empty string.
#
# LlamaIndex reports `query_instruction = None` while quietly applying an
# instruction of its own. We pass ours explicitly to override that, because the
# two embed the same query differently and so retrieve different passages.
QUERY_INSTRUCTION = (
"Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery: "
)
# --- Index -----------------------------------------------------------------
CHROMA_DIR = DATA_DIR / "chroma"
CHROMA_COLLECTION = "rust_docs"
CHROMA_DISTANCE = "cosine"
HF_INDEX_REPO_ENV = "HF_INDEX_REPO"
# --- Retrieval -------------------------------------------------------------
RETRIEVAL_TOP_K = 8
# How the retriever combines its parts. One of "vector", "bm25", "hybrid" or "rerank"
RETRIEVAL_MODE = "hybrid"
CANDIDATE_TOP_K = 20
RERANK_MODEL = "cross-encoder/ms-marco-MiniLM-L-6-v2"
MAX_SEARCHES_PER_TURN = 3
MAX_AGENT_ITERATIONS = 6
MAX_ANSWER_TOKENS = 4096
# --- Providers -------------------------------------------------------------
@dataclass(frozen=True)
class ProviderSpec:
label: str
key_label: str
models: tuple[str, ...]
env_var: str
@property
def default_model(self) -> str:
return self.models[0]
PROVIDERS: dict[str, ProviderSpec] = {
"openai": ProviderSpec(
label="OpenAI",
key_label="OpenAI API key",
models=("gpt-5-mini", "gpt-5"),
env_var="OPENAI_API_KEY",
),
"gemini": ProviderSpec(
label="Google Gemini",
key_label="Google Gemini API key",
models=("gemini-2.5-flash", "gemini-2.5-pro"),
env_var="GOOGLE_API_KEY",
),
"anthropic": ProviderSpec(
label="Anthropic Claude",
key_label="Anthropic API key",
models=("claude-haiku-4-5", "claude-sonnet-4-5"),
env_var="ANTHROPIC_API_KEY",
),
}
DEFAULT_PROVIDER = "openai"
|