Skip to content

Commit 29c97e7

Browse files
committed
feat: add RTL support and extend TTS to 55+ languages
RTL (Right-to-Left) Support: - New rtl_support.py module for EPUB RTL layout management - Automatic detection of RTL languages (Arabic, Hebrew, Persian, Urdu, etc.) - CSS injection for proper text direction and layout - Handles RTL<->LTR transitions (e.g., Arabic->French, French->Arabic) - Protects technical content (code, URLs) from direction reversal - Updates OPF metadata with page-progression-direction TTS Expansion (55+ languages): - Asian: Chinese, Japanese, Korean, Hindi, Vietnamese, Thai, Indonesian, Malay, Filipino, Bengali, Tamil, Telugu - European: English, French, German, Spanish, Italian, Portuguese, Russian, Dutch, Polish, Swedish, Norwegian, Danish, Finnish, Greek, Czech, Hungarian, Romanian, Turkish, Ukrainian, Bulgarian, Croatian, Slovak, Slovenian, Lithuanian, Latvian, Estonian - Semitic/RTL: Arabic, Hebrew, Persian/Farsi, Urdu - Premium neural voices from Edge-TTS UI Updates: - Extended language dropdowns in TTS modals with native script labels
1 parent 389613c commit 29c97e7

File tree

14 files changed

+46494
-82
lines changed

14 files changed

+46494
-82
lines changed

benchmark/cli.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import sys
1313
from typing import Optional
1414

15-
from benchmark.config import BenchmarkConfig, DEFAULT_EVALUATOR_MODEL
15+
from benchmark.config import BenchmarkConfig, DEFAULT_EVALUATOR_MODEL, DEFAULT_EVALUATOR_PROVIDER, DEFAULT_POE_EVALUATOR_MODEL
1616
from benchmark.runner import BenchmarkRunner, quick_benchmark, full_benchmark
1717
from benchmark.results.storage import ResultsStorage
1818
from benchmark.wiki.generator import WikiGenerator
@@ -72,11 +72,14 @@ def cmd_run(args: argparse.Namespace) -> int:
7272
provider = getattr(args, 'provider', 'ollama') or 'ollama'
7373

7474
# Build configuration
75+
evaluator_provider = getattr(args, 'evaluator_provider', DEFAULT_EVALUATOR_PROVIDER)
7576
config = BenchmarkConfig.from_cli_args(
7677
openrouter_key=args.openrouter_key,
78+
poe_key=args.poe_key,
7779
evaluator_model=args.evaluator,
7880
ollama_endpoint=args.ollama_endpoint,
7981
translation_provider=provider,
82+
evaluator_provider=evaluator_provider,
8083
)
8184

8285
# Validate configuration
@@ -615,10 +618,22 @@ def create_parser() -> argparse.ArgumentParser:
615618
help="OpenRouter API key (for evaluation, and translation if using --provider openrouter). "
616619
"Can also be set via OPENROUTER_API_KEY env var."
617620
)
621+
run_parser.add_argument(
622+
"--evaluator-provider",
623+
choices=["openrouter", "poe"],
624+
default=DEFAULT_EVALUATOR_PROVIDER,
625+
help=f"Provider for evaluation (default: {DEFAULT_EVALUATOR_PROVIDER})"
626+
)
618627
run_parser.add_argument(
619628
"--evaluator",
620-
default=DEFAULT_EVALUATOR_MODEL,
621-
help=f"OpenRouter model for evaluation (default: {DEFAULT_EVALUATOR_MODEL})"
629+
default=None,
630+
help=f"Model for evaluation (default: {DEFAULT_EVALUATOR_MODEL} for OpenRouter, "
631+
f"{DEFAULT_POE_EVALUATOR_MODEL} for Poe)"
632+
)
633+
run_parser.add_argument(
634+
"--poe-key",
635+
help="Poe API key (for evaluation if using --evaluator-provider poe). "
636+
"Can also be set via POE_API_KEY env var."
622637
)
623638
run_parser.add_argument(
624639
"--ollama-endpoint",

benchmark/config.py

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@
4848
# Default evaluator model
4949
DEFAULT_EVALUATOR_MODEL = "google/gemini-3-flash-preview"
5050

51+
# Default evaluator provider ("openrouter" or "poe")
52+
DEFAULT_EVALUATOR_PROVIDER = "poe"
53+
54+
# Default POE model for evaluation
55+
# Available models: gemini-3.1-flash-lite, gemini-3.1-pro, Claude-Sonnet-4, GPT-4o, etc.
56+
DEFAULT_POE_EVALUATOR_MODEL = "gemini-3.1-flash-lite"
57+
5158
# Score thresholds for visual indicators
5259
SCORE_THRESHOLDS = {
5360
"excellent": 9, # 🟢 9-10
@@ -92,6 +99,18 @@ class OpenRouterConfig:
9299
site_name: str = "TranslateBookWithLLM Benchmark"
93100

94101

102+
@dataclass
103+
class PoeConfig:
104+
"""Configuration for Poe evaluation provider."""
105+
106+
api_key: Optional[str] = field(
107+
default_factory=lambda: os.getenv("POE_API_KEY")
108+
)
109+
endpoint: str = "https://api.poe.com/v1/chat/completions"
110+
default_model: str = DEFAULT_POE_EVALUATOR_MODEL
111+
timeout: int = 120
112+
113+
95114
@dataclass
96115
class PathConfig:
97116
"""Configuration for file paths."""
@@ -137,6 +156,7 @@ class BenchmarkConfig:
137156

138157
ollama: OllamaConfig = field(default_factory=OllamaConfig)
139158
openrouter: OpenRouterConfig = field(default_factory=OpenRouterConfig)
159+
poe: PoeConfig = field(default_factory=PoeConfig)
140160
paths: PathConfig = field(default_factory=PathConfig)
141161

142162
# Benchmark settings
@@ -146,6 +166,9 @@ class BenchmarkConfig:
146166
# Translation provider ("ollama" or "openrouter")
147167
translation_provider: str = "ollama"
148168

169+
# Evaluator provider ("openrouter" or "poe")
170+
evaluator_provider: str = DEFAULT_EVALUATOR_PROVIDER
171+
149172
# Retry settings
150173
max_retries: int = 3
151174
retry_delay: float = 2.0
@@ -162,6 +185,8 @@ def from_cli_args(
162185
evaluator_model: Optional[str] = None,
163186
ollama_endpoint: Optional[str] = None,
164187
translation_provider: Optional[str] = None,
188+
evaluator_provider: Optional[str] = None,
189+
poe_key: Optional[str] = None,
165190
**kwargs
166191
) -> "BenchmarkConfig":
167192
"""Create configuration from CLI arguments with env fallbacks."""
@@ -170,35 +195,56 @@ def from_cli_args(
170195
if openrouter_key:
171196
config.openrouter.api_key = openrouter_key
172197

198+
if poe_key:
199+
config.poe.api_key = poe_key
200+
173201
if evaluator_model:
174202
config.openrouter.default_model = evaluator_model
203+
config.poe.default_model = evaluator_model
175204

176205
if ollama_endpoint:
177206
config.ollama.endpoint = ollama_endpoint
178207

179208
if translation_provider:
180209
config.translation_provider = translation_provider.lower()
181210

211+
if evaluator_provider:
212+
config.evaluator_provider = evaluator_provider.lower()
213+
182214
return config
183215

184216
def validate(self) -> list[str]:
185217
"""Validate configuration and return list of errors."""
186218
errors = []
187219

188-
# OpenRouter API key is required for evaluation (always)
189-
# and for translation if using OpenRouter provider
190-
if not self.openrouter.api_key:
191-
if self.translation_provider == "openrouter":
220+
# Validate evaluator provider
221+
if self.evaluator_provider not in ("openrouter", "poe"):
222+
errors.append(
223+
f"Invalid evaluator provider: {self.evaluator_provider}. "
224+
"Must be 'openrouter' or 'poe'"
225+
)
226+
227+
# Check API key for evaluation provider
228+
if self.evaluator_provider == "poe":
229+
if not self.poe.api_key:
192230
errors.append(
193-
"OpenRouter API key not configured. Required for both translation and evaluation. "
194-
"Set OPENROUTER_API_KEY in .env or use --openrouter-key"
231+
"Poe API key not configured. Required for evaluation. "
232+
"Set POE_API_KEY in .env or use --poe-key"
195233
)
196-
else:
234+
else: # openrouter
235+
if not self.openrouter.api_key:
197236
errors.append(
198237
"OpenRouter API key not configured. Required for evaluation. "
199238
"Set OPENROUTER_API_KEY in .env or use --openrouter-key"
200239
)
201240

241+
# Check translation provider API key if needed
242+
if self.translation_provider == "openrouter" and not self.openrouter.api_key:
243+
errors.append(
244+
"OpenRouter API key not configured. Required for translation. "
245+
"Set OPENROUTER_API_KEY in .env or use --openrouter-key"
246+
)
247+
202248
if not self.paths.languages_file.exists():
203249
errors.append(f"Languages file not found: {self.paths.languages_file}")
204250

@@ -207,7 +253,10 @@ def validate(self) -> list[str]:
207253

208254
# Validate translation provider
209255
if self.translation_provider not in ("ollama", "openrouter"):
210-
errors.append(f"Invalid translation provider: {self.translation_provider}. Must be 'ollama' or 'openrouter'")
256+
errors.append(
257+
f"Invalid translation provider: {self.translation_provider}. "
258+
"Must be 'ollama' or 'openrouter'"
259+
)
211260

212261
return errors
213262

0 commit comments

Comments
 (0)