-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcore_config.py
More file actions
149 lines (136 loc) · 5.27 KB
/
core_config.py
File metadata and controls
149 lines (136 loc) · 5.27 KB
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
from __future__ import annotations
import os
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Any
def _to_bool(value: str | None, default: bool) -> bool:
if value is None:
return default
value = value.strip().lower()
if value in {"1", "true", "yes", "on"}:
return True
if value in {"0", "false", "no", "off"}:
return False
return default
def _parse_bind(value: str) -> tuple[str, int]:
host, _, port = value.rpartition(":")
if not host:
return "127.0.0.1", 8080
try:
return host, int(port)
except ValueError:
return host, 8080
@dataclass(frozen=True, slots=True)
class Config:
bind_host: str = "127.0.0.1"
bind_port: int = 8080
api_key: str | None = None
storage_dir: str = ".data"
database_path: str = ".data/vra.db"
ollama_base_url: str = "http://127.0.0.1:11434"
model_primary: str = "qwen3.5:4b-q4_K_M"
model_fallback: str = "qwen3.5:2b-q4_K_M"
model_min: str = "qwen3.5:0.8b-q8_0"
auto_pull_models: bool = True
vram_budget_mb: int = 8192
model_size_budget_ratio: float = 0.75
model_selection_reserve_mb: int = 768
context_tokens: int = 3072
max_output_tokens: int = 768
max_frames: int = 5
frame_scene_detection: bool = True
frame_scene_threshold: float = 0.28
frame_scene_min_frames: int = 2
max_transcript_chars: int = 16000
transcript_retention_per_video: int = 3
summary_retention_per_video: int = 5
rss_title: str = "Video RSS Aggregator"
rss_link: str = "http://127.0.0.1:8080/rss"
rss_description: str = "Video summaries"
@property
def bind_address(self) -> str:
return f"{self.bind_host}:{self.bind_port}"
@property
def model_priority(self) -> tuple[str, ...]:
out: list[str] = []
for name in (self.model_primary, self.model_fallback, self.model_min):
item = name.strip()
if item and item not in out:
out.append(item)
return tuple(out)
def as_setup_payload(self) -> dict[str, Any]:
payload = asdict(self)
payload.pop("api_key", None)
payload["bind_address"] = self.bind_address
payload["model_priority"] = list(self.model_priority)
payload["api_key_required"] = self.api_key is not None
payload["quick_commands"] = {
"bootstrap": "python -m vra bootstrap",
"status": "python -m vra status",
"serve": f"python -m vra serve --bind {self.bind_address}",
}
return payload
@classmethod
def from_env(cls) -> Config:
bind = os.environ.get("BIND_ADDRESS", "127.0.0.1:8080")
host, port = _parse_bind(bind)
storage_dir = os.environ.get("VRA_STORAGE_DIR", ".data")
db_path = os.environ.get("VRA_DATABASE_PATH")
if not db_path:
db_path = str(Path(storage_dir) / "vra.db")
return cls(
bind_host=host,
bind_port=port,
api_key=os.environ.get("API_KEY"),
storage_dir=storage_dir,
database_path=db_path,
ollama_base_url=os.environ.get(
"VRA_OLLAMA_BASE_URL",
"http://127.0.0.1:11434",
),
model_primary=os.environ.get("VRA_MODEL_PRIMARY", "qwen3.5:4b-q4_K_M"),
model_fallback=os.environ.get("VRA_MODEL_FALLBACK", "qwen3.5:2b-q4_K_M"),
model_min=os.environ.get("VRA_MODEL_MIN", "qwen3.5:0.8b-q8_0"),
auto_pull_models=_to_bool(
os.environ.get("VRA_AUTO_PULL_MODELS"),
True,
),
vram_budget_mb=int(os.environ.get("VRA_VRAM_BUDGET_MB", "8192")),
model_size_budget_ratio=float(
os.environ.get("VRA_MODEL_SIZE_BUDGET_RATIO", "0.75")
),
model_selection_reserve_mb=int(
os.environ.get("VRA_MODEL_SELECTION_RESERVE_MB", "768")
),
context_tokens=int(os.environ.get("VRA_CONTEXT_TOKENS", "3072")),
max_output_tokens=int(os.environ.get("VRA_MAX_OUTPUT_TOKENS", "768")),
max_frames=int(os.environ.get("VRA_MAX_FRAMES", "5")),
frame_scene_detection=_to_bool(
os.environ.get("VRA_FRAME_SCENE_DETECTION"),
True,
),
frame_scene_threshold=float(
os.environ.get("VRA_FRAME_SCENE_THRESHOLD", "0.28")
),
frame_scene_min_frames=max(
1,
int(os.environ.get("VRA_FRAME_SCENE_MIN_FRAMES", "2")),
),
max_transcript_chars=int(
os.environ.get("VRA_MAX_TRANSCRIPT_CHARS", "16000")
),
transcript_retention_per_video=max(
1,
int(os.environ.get("VRA_TRANSCRIPT_RETENTION_PER_VIDEO", "3")),
),
summary_retention_per_video=max(
1,
int(os.environ.get("VRA_SUMMARY_RETENTION_PER_VIDEO", "5")),
),
rss_title=os.environ.get("VRA_RSS_TITLE", "Video RSS Aggregator"),
rss_link=os.environ.get("VRA_RSS_LINK", "http://127.0.0.1:8080/rss"),
rss_description=os.environ.get(
"VRA_RSS_DESCRIPTION",
"Video summaries",
),
)