初始化提交
This commit is contained in:
198
app/config/config.py
Normal file
198
app/config/config.py
Normal file
@@ -0,0 +1,198 @@
|
||||
import os
|
||||
import shutil
|
||||
import socket
|
||||
|
||||
import toml
|
||||
from loguru import logger
|
||||
|
||||
root_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
||||
config_file = f"{root_dir}/config.toml"
|
||||
_CONTAINER_CGROUP_MARKERS = ("docker", "containerd", "kubepods", "libpod", "podman")
|
||||
_DOCKER_HOST_GATEWAY_NAME = "host.docker.internal"
|
||||
|
||||
|
||||
def is_running_in_container(
|
||||
dockerenv_path: str = "/.dockerenv",
|
||||
containerenv_path: str = "/run/.containerenv",
|
||||
cgroup_path: str = "/proc/1/cgroup",
|
||||
) -> bool:
|
||||
"""
|
||||
判断当前进程是否运行在容器内。
|
||||
|
||||
这个判断主要用于 Ollama 默认地址选择:
|
||||
- 普通本机运行时,`localhost` 指向用户机器本身;
|
||||
- Docker 容器内,`localhost` 指向容器自己,访问宿主机 Ollama
|
||||
通常需要使用 `host.docker.internal`。
|
||||
|
||||
不能只判断 `/proc/1/cgroup` 是否存在,因为普通 Linux 也会有这个文件。
|
||||
这里只在检测到明确的容器标记时返回 True,避免误伤非 Docker Linux 用户。
|
||||
参数保留为可注入路径,便于单元测试覆盖不同运行环境。
|
||||
"""
|
||||
if os.path.isfile(dockerenv_path) or os.path.isfile(containerenv_path):
|
||||
return True
|
||||
|
||||
try:
|
||||
with open(cgroup_path, mode="r", encoding="utf-8") as fp:
|
||||
cgroup_content = fp.read().lower()
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
return any(marker in cgroup_content for marker in _CONTAINER_CGROUP_MARKERS)
|
||||
|
||||
|
||||
def _can_resolve_hostname(hostname: str) -> bool:
|
||||
try:
|
||||
socket.gethostbyname(hostname)
|
||||
except OSError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _decode_linux_route_gateway(hex_gateway: str) -> str:
|
||||
# /proc/net/route 里的 Gateway 是 16 进制小端序,例如 010011AC 表示
|
||||
# 172.17.0.1。这里单独解析,是为了在原生 Linux Docker 没有
|
||||
# host.docker.internal DNS 记录时,还能尝试访问容器默认网关上的宿主机。
|
||||
if len(hex_gateway) != 8:
|
||||
raise ValueError("invalid gateway length")
|
||||
|
||||
octets = [
|
||||
str(int(hex_gateway[index : index + 2], 16))
|
||||
for index in range(6, -1, -2)
|
||||
]
|
||||
return ".".join(octets)
|
||||
|
||||
|
||||
def get_container_default_gateway_ip(route_path: str = "/proc/net/route") -> str:
|
||||
"""
|
||||
读取 Linux 容器里的默认网关 IP。
|
||||
|
||||
Docker Desktop 通常提供 `host.docker.internal`,但原生 Linux Docker
|
||||
默认不一定提供这个 DNS 名称。默认网关通常可以作为访问宿主机服务的
|
||||
兜底地址;如果用户的 Ollama 只监听 127.0.0.1,则仍需要用户让
|
||||
Ollama 监听宿主机网卡或手动配置 `ollama_base_url`。
|
||||
"""
|
||||
try:
|
||||
with open(route_path, mode="r", encoding="utf-8") as fp:
|
||||
route_lines = fp.readlines()
|
||||
except OSError:
|
||||
return ""
|
||||
|
||||
for line in route_lines[1:]:
|
||||
fields = line.strip().split()
|
||||
if len(fields) < 3:
|
||||
continue
|
||||
|
||||
destination = fields[1]
|
||||
gateway = fields[2]
|
||||
if destination != "00000000" or gateway == "00000000":
|
||||
continue
|
||||
|
||||
try:
|
||||
return _decode_linux_route_gateway(gateway)
|
||||
except ValueError:
|
||||
logger.warning(f"invalid container gateway route entry: {line.strip()}")
|
||||
return ""
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
def get_default_ollama_base_url() -> str:
|
||||
"""
|
||||
返回 Ollama 的默认 OpenAI-compatible base_url。
|
||||
|
||||
用户显式配置 `ollama_base_url` 时不会走这里;这里只处理“未配置时的
|
||||
最佳默认值”。容器内默认指向宿主机,普通本机运行默认指向 localhost。
|
||||
"""
|
||||
if not is_running_in_container():
|
||||
return "http://localhost:11434/v1"
|
||||
|
||||
if _can_resolve_hostname(_DOCKER_HOST_GATEWAY_NAME):
|
||||
return f"http://{_DOCKER_HOST_GATEWAY_NAME}:11434/v1"
|
||||
|
||||
gateway_ip = get_container_default_gateway_ip()
|
||||
if gateway_ip:
|
||||
logger.info(
|
||||
"host.docker.internal is not resolvable, fallback to container "
|
||||
f"default gateway for Ollama: {gateway_ip}"
|
||||
)
|
||||
return f"http://{gateway_ip}:11434/v1"
|
||||
|
||||
logger.warning(
|
||||
"failed to resolve host.docker.internal and container default gateway; "
|
||||
"fallback to host.docker.internal for Ollama"
|
||||
)
|
||||
return f"http://{_DOCKER_HOST_GATEWAY_NAME}:11434/v1"
|
||||
|
||||
|
||||
def load_config():
|
||||
# fix: IsADirectoryError: [Errno 21] Is a directory: '/MoneyPrinterTurbo/config.toml'
|
||||
if os.path.isdir(config_file):
|
||||
shutil.rmtree(config_file)
|
||||
|
||||
if not os.path.isfile(config_file):
|
||||
example_file = f"{root_dir}/config.example.toml"
|
||||
if os.path.isfile(example_file):
|
||||
shutil.copyfile(example_file, config_file)
|
||||
logger.info("copy config.example.toml to config.toml")
|
||||
|
||||
logger.info(f"load config from file: {config_file}")
|
||||
|
||||
try:
|
||||
_config_ = toml.load(config_file)
|
||||
except Exception as e:
|
||||
logger.warning(f"load config failed: {str(e)}, try to load as utf-8-sig")
|
||||
with open(config_file, mode="r", encoding="utf-8-sig") as fp:
|
||||
_cfg_content = fp.read()
|
||||
_config_ = toml.loads(_cfg_content)
|
||||
return _config_
|
||||
|
||||
|
||||
def save_config():
|
||||
with open(config_file, "w", encoding="utf-8") as f:
|
||||
_cfg["app"] = app
|
||||
_cfg["azure"] = azure
|
||||
_cfg["siliconflow"] = siliconflow
|
||||
_cfg["ui"] = ui
|
||||
f.write(toml.dumps(_cfg))
|
||||
|
||||
|
||||
_cfg = load_config()
|
||||
app = _cfg.get("app", {})
|
||||
whisper = _cfg.get("whisper", {})
|
||||
proxy = _cfg.get("proxy", {})
|
||||
azure = _cfg.get("azure", {})
|
||||
siliconflow = _cfg.get("siliconflow", {})
|
||||
ui = _cfg.get(
|
||||
"ui",
|
||||
{
|
||||
"hide_log": False,
|
||||
},
|
||||
)
|
||||
|
||||
hostname = socket.gethostname()
|
||||
|
||||
log_level = _cfg.get("log_level", "DEBUG")
|
||||
listen_host = _cfg.get("listen_host", "0.0.0.0")
|
||||
listen_port = _cfg.get("listen_port", 8099)
|
||||
project_name = _cfg.get("project_name", "MoneyPrinterTurbo")
|
||||
project_description = _cfg.get(
|
||||
"project_description",
|
||||
"<a href='https://github.com/harry0703/MoneyPrinterTurbo'>https://github.com/harry0703/MoneyPrinterTurbo</a>",
|
||||
)
|
||||
project_version = _cfg.get("project_version", "1.2.9")
|
||||
reload_debug = False
|
||||
|
||||
app["redis_host"] = os.getenv(
|
||||
"MPT_APP_REDIS_HOST",
|
||||
os.getenv("REDIS_HOST", app.get("redis_host", "localhost")),
|
||||
)
|
||||
|
||||
imagemagick_path = app.get("imagemagick_path", "")
|
||||
if imagemagick_path and os.path.isfile(imagemagick_path):
|
||||
os.environ["IMAGEMAGICK_BINARY"] = imagemagick_path
|
||||
|
||||
ffmpeg_path = app.get("ffmpeg_path", "")
|
||||
if ffmpeg_path and os.path.isfile(ffmpeg_path):
|
||||
os.environ["IMAGEIO_FFMPEG_EXE"] = ffmpeg_path
|
||||
|
||||
logger.info(f"{project_name} v{project_version}")
|
||||
Reference in New Issue
Block a user