42e3aea6
tangwang
tidy
|
1
2
3
4
|
"""
Services configuration - single source for translation, embedding, rerank providers.
All provider selection and endpoint resolution is centralized here.
|
26b910bd
tangwang
refactor service ...
|
5
|
Priority: env vars > config.yaml.
|
42e3aea6
tangwang
tidy
|
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
|
"""
from __future__ import annotations
import os
from dataclasses import dataclass, field
from functools import lru_cache
from pathlib import Path
from typing import Any, Dict, Optional
import yaml
@dataclass
class ServiceConfig:
"""Config for one capability (translation/embedding/rerank)."""
provider: str
providers: Dict[str, Any] = field(default_factory=dict)
def get_provider_cfg(self) -> Dict[str, Any]:
"""Get config for current provider."""
p = (self.provider or "").strip().lower()
return self.providers.get(p, {}) if isinstance(self.providers, dict) else {}
def _load_services_raw(config_path: Optional[Path] = None) -> Dict[str, Any]:
"""Load services block from config.yaml."""
if config_path is None:
config_path = Path(__file__).parent / "config.yaml"
path = Path(config_path)
if not path.exists():
|
26b910bd
tangwang
refactor service ...
|
37
|
raise FileNotFoundError(f"services config file not found: {path}")
|
42e3aea6
tangwang
tidy
|
38
39
40
|
try:
with open(path, "r", encoding="utf-8") as f:
data = yaml.safe_load(f)
|
26b910bd
tangwang
refactor service ...
|
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
except Exception as exc:
raise RuntimeError(f"failed to parse services config from {path}: {exc}") from exc
if not isinstance(data, dict):
raise RuntimeError(f"invalid config format in {path}: expected mapping root")
services = data.get("services")
if not isinstance(services, dict):
raise RuntimeError("config.yaml must contain a valid 'services' mapping")
return services
def _resolve_provider_name(
env_name: str,
config_provider: Any,
capability: str,
) -> str:
provider = os.getenv(env_name) or config_provider
if not provider:
raise ValueError(
f"services.{capability}.provider is required "
f"(or set env override {env_name})"
)
return str(provider).strip().lower()
|
42e3aea6
tangwang
tidy
|
63
64
65
66
67
68
69
|
def _resolve_translation() -> ServiceConfig:
raw = _load_services_raw()
cfg = raw.get("translation", {}) if isinstance(raw.get("translation"), dict) else {}
providers = cfg.get("providers", {}) if isinstance(cfg.get("providers"), dict) else {}
|
26b910bd
tangwang
refactor service ...
|
70
71
72
73
|
provider = _resolve_provider_name(
env_name="TRANSLATION_PROVIDER",
config_provider=cfg.get("provider"),
capability="translation",
|
42e3aea6
tangwang
tidy
|
74
|
)
|
d4cadc13
tangwang
翻译重构
|
75
|
if provider not in ("qwen-mt", "deepl", "direct", "local", "inprocess", "http", "service", "llm"):
|
26b910bd
tangwang
refactor service ...
|
76
|
raise ValueError(f"Unsupported translation provider: {provider}")
|
42e3aea6
tangwang
tidy
|
77
78
79
|
# Env override for http base_url
env_url = os.getenv("TRANSLATION_SERVICE_URL")
|
d4cadc13
tangwang
翻译重构
|
80
|
if env_url and provider in ("http", "service"):
|
42e3aea6
tangwang
tidy
|
81
82
83
84
85
86
87
88
89
90
91
92
|
providers = dict(providers)
providers["http"] = dict(providers.get("http", {}))
providers["http"]["base_url"] = env_url.rstrip("/")
return ServiceConfig(provider=provider, providers=providers)
def _resolve_embedding() -> ServiceConfig:
raw = _load_services_raw()
cfg = raw.get("embedding", {}) if isinstance(raw.get("embedding"), dict) else {}
providers = cfg.get("providers", {}) if isinstance(cfg.get("providers"), dict) else {}
|
26b910bd
tangwang
refactor service ...
|
93
94
95
96
|
provider = _resolve_provider_name(
env_name="EMBEDDING_PROVIDER",
config_provider=cfg.get("provider"),
capability="embedding",
|
42e3aea6
tangwang
tidy
|
97
|
)
|
26b910bd
tangwang
refactor service ...
|
98
99
|
if provider != "http":
raise ValueError(f"Unsupported embedding provider: {provider}")
|
42e3aea6
tangwang
tidy
|
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
env_url = os.getenv("EMBEDDING_SERVICE_URL")
if env_url and provider == "http":
providers = dict(providers)
providers["http"] = dict(providers.get("http", {}))
providers["http"]["base_url"] = env_url.rstrip("/")
return ServiceConfig(provider=provider, providers=providers)
def _resolve_rerank() -> ServiceConfig:
raw = _load_services_raw()
cfg = raw.get("rerank", {}) if isinstance(raw.get("rerank"), dict) else {}
providers = cfg.get("providers", {}) if isinstance(cfg.get("providers"), dict) else {}
|
26b910bd
tangwang
refactor service ...
|
115
116
117
118
|
provider = _resolve_provider_name(
env_name="RERANK_PROVIDER",
config_provider=cfg.get("provider"),
capability="rerank",
|
42e3aea6
tangwang
tidy
|
119
|
)
|
26b910bd
tangwang
refactor service ...
|
120
121
|
if provider != "http":
raise ValueError(f"Unsupported rerank provider: {provider}")
|
42e3aea6
tangwang
tidy
|
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
env_url = os.getenv("RERANKER_SERVICE_URL")
if env_url:
url = env_url.rstrip("/")
if not url.endswith("/rerank"):
url = f"{url}/rerank" if "/rerank" not in url else url
providers = dict(providers)
providers["http"] = dict(providers.get("http", {}))
providers["http"]["base_url"] = url.replace("/rerank", "")
providers["http"]["service_url"] = url
return ServiceConfig(provider=provider, providers=providers)
|
701ae503
tangwang
docs
|
136
137
138
139
140
141
142
143
144
145
146
147
|
def get_rerank_backend_config() -> tuple[str, dict]:
"""
Resolve reranker backend name and config for the reranker service process.
Returns (backend_name, backend_cfg).
Env RERANK_BACKEND overrides config.
"""
raw = _load_services_raw()
cfg = raw.get("rerank", {}) if isinstance(raw.get("rerank"), dict) else {}
backends = cfg.get("backends", {}) if isinstance(cfg.get("backends"), dict) else {}
name = (
os.getenv("RERANK_BACKEND")
or cfg.get("backend")
|
07cf5a93
tangwang
START_EMBEDDING=...
|
148
|
)
|
26b910bd
tangwang
refactor service ...
|
149
150
|
if not name:
raise ValueError("services.rerank.backend is required (or env RERANK_BACKEND)")
|
07cf5a93
tangwang
START_EMBEDDING=...
|
151
152
|
name = str(name).strip().lower()
backend_cfg = backends.get(name, {}) if isinstance(backends.get(name), dict) else {}
|
26b910bd
tangwang
refactor service ...
|
153
154
|
if not backend_cfg:
raise ValueError(f"services.rerank.backends.{name} is required")
|
07cf5a93
tangwang
START_EMBEDDING=...
|
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
return name, backend_cfg
def get_embedding_backend_config() -> tuple[str, dict]:
"""
Resolve embedding backend name and config for the embedding service process.
Returns (backend_name, backend_cfg).
Env EMBEDDING_BACKEND overrides config.
"""
raw = _load_services_raw()
cfg = raw.get("embedding", {}) if isinstance(raw.get("embedding"), dict) else {}
backends = cfg.get("backends", {}) if isinstance(cfg.get("backends"), dict) else {}
name = (
os.getenv("EMBEDDING_BACKEND")
or cfg.get("backend")
|
701ae503
tangwang
docs
|
170
|
)
|
26b910bd
tangwang
refactor service ...
|
171
172
|
if not name:
raise ValueError("services.embedding.backend is required (or env EMBEDDING_BACKEND)")
|
701ae503
tangwang
docs
|
173
174
|
name = str(name).strip().lower()
backend_cfg = backends.get(name, {}) if isinstance(backends.get(name), dict) else {}
|
26b910bd
tangwang
refactor service ...
|
175
176
|
if not backend_cfg:
raise ValueError(f"services.embedding.backends.{name} is required")
|
701ae503
tangwang
docs
|
177
178
179
|
return name, backend_cfg
|
42e3aea6
tangwang
tidy
|
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
@lru_cache(maxsize=1)
def get_translation_config() -> ServiceConfig:
"""Get translation service config."""
return _resolve_translation()
@lru_cache(maxsize=1)
def get_embedding_config() -> ServiceConfig:
"""Get embedding service config."""
return _resolve_embedding()
@lru_cache(maxsize=1)
def get_rerank_config() -> ServiceConfig:
"""Get rerank service config."""
return _resolve_rerank()
def get_translation_base_url() -> str:
"""Resolve translation HTTP base URL (for http provider)."""
base = (
os.getenv("TRANSLATION_SERVICE_URL")
or get_translation_config().providers.get("http", {}).get("base_url")
|
42e3aea6
tangwang
tidy
|
203
|
)
|
26b910bd
tangwang
refactor service ...
|
204
205
|
if not base:
raise ValueError("Translation HTTP base_url is not configured")
|
42e3aea6
tangwang
tidy
|
206
207
208
|
return str(base).rstrip("/")
|
d4cadc13
tangwang
翻译重构
|
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
|
def get_translation_cache_config() -> Dict[str, Any]:
"""
Resolve translation cache policy from services.translation.cache.
All translation cache key/TTL behavior should be configured in config.yaml,
not hardcoded in code.
"""
raw = _load_services_raw()
cfg = raw.get("translation", {}) if isinstance(raw.get("translation"), dict) else {}
cache_cfg = cfg.get("cache", {}) if isinstance(cfg.get("cache"), dict) else {}
return {
"enabled": bool(cache_cfg.get("enabled", True)),
"key_prefix": str(cache_cfg.get("key_prefix", "trans:v2")),
"ttl_seconds": int(cache_cfg.get("ttl_seconds", 360 * 24 * 3600)),
"sliding_expiration": bool(cache_cfg.get("sliding_expiration", True)),
"key_include_context": bool(cache_cfg.get("key_include_context", True)),
"key_include_prompt": bool(cache_cfg.get("key_include_prompt", True)),
"key_include_source_lang": bool(cache_cfg.get("key_include_source_lang", True)),
}
|
42e3aea6
tangwang
tidy
|
230
231
232
233
234
|
def get_embedding_base_url() -> str:
"""Resolve embedding HTTP base URL."""
base = (
os.getenv("EMBEDDING_SERVICE_URL")
or get_embedding_config().providers.get("http", {}).get("base_url")
|
42e3aea6
tangwang
tidy
|
235
|
)
|
26b910bd
tangwang
refactor service ...
|
236
237
|
if not base:
raise ValueError("Embedding HTTP base_url is not configured")
|
42e3aea6
tangwang
tidy
|
238
239
240
241
242
243
244
245
246
|
return str(base).rstrip("/")
def get_rerank_service_url() -> str:
"""Resolve rerank service URL (full path including /rerank)."""
base = (
os.getenv("RERANKER_SERVICE_URL")
or get_rerank_config().providers.get("http", {}).get("service_url")
or get_rerank_config().providers.get("http", {}).get("base_url")
|
42e3aea6
tangwang
tidy
|
247
|
)
|
26b910bd
tangwang
refactor service ...
|
248
249
|
if not base:
raise ValueError("Rerank HTTP service_url/base_url is not configured")
|
42e3aea6
tangwang
tidy
|
250
251
252
253
254
255
256
257
258
|
base = str(base).rstrip("/")
return base if base.endswith("/rerank") else f"{base}/rerank"
def clear_services_cache() -> None:
"""Clear cached config (for tests)."""
get_translation_config.cache_clear()
get_embedding_config.cache_clear()
get_rerank_config.cache_clear()
|