|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
|
|
|
|
|
from pathlib import Path
|
|
from pathlib import Path
|
|
|
from typing import Self
|
|
from typing import Self
|
|
|
|
|
+from urllib.parse import urlsplit
|
|
|
|
|
|
|
|
from pydantic import Field, SecretStr, model_validator
|
|
from pydantic import Field, SecretStr, model_validator
|
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
@@ -29,12 +30,16 @@ class ScriptBuildSettings(BaseSettings):
|
|
|
|
|
|
|
|
environment: str = "production"
|
|
environment: str = "production"
|
|
|
read_database_url: SecretStr = Field(repr=False)
|
|
read_database_url: SecretStr = Field(repr=False)
|
|
|
|
|
+ read_database_socks_proxy: str | None = None
|
|
|
|
|
+ read_database_tunnel_host: str = "127.0.0.1"
|
|
|
|
|
+ read_database_tunnel_port: int = Field(default=13306, ge=1, le=65535)
|
|
|
write_database_url: SecretStr = Field(repr=False)
|
|
write_database_url: SecretStr = Field(repr=False)
|
|
|
final_database_url: SecretStr | None = Field(default=None, repr=False)
|
|
final_database_url: SecretStr | None = Field(default=None, repr=False)
|
|
|
agent_data_root: Path
|
|
agent_data_root: Path
|
|
|
persona_root: Path
|
|
persona_root: Path
|
|
|
section_pattern_root: Path
|
|
section_pattern_root: Path
|
|
|
decode_index_root: Path
|
|
decode_index_root: Path
|
|
|
|
|
+ decode_raw_root: Path | None = None
|
|
|
knowledge_root: Path
|
|
knowledge_root: Path
|
|
|
prompt_fallback_root: Path
|
|
prompt_fallback_root: Path
|
|
|
prompts_from_file: bool = False
|
|
prompts_from_file: bool = False
|
|
@@ -44,9 +49,20 @@ class ScriptBuildSettings(BaseSettings):
|
|
|
decode_endpoint: str | None = None
|
|
decode_endpoint: str | None = None
|
|
|
embedding_endpoint: str | None = None
|
|
embedding_endpoint: str | None = None
|
|
|
external_endpoint: str | None = None
|
|
external_endpoint: str | None = None
|
|
|
|
|
+ xhs_search_endpoint: str | None = None
|
|
|
|
|
+ xhs_detail_endpoint: str | None = None
|
|
|
|
|
+ zhihu_search_endpoint: str | None = None
|
|
|
image_endpoint: str | None = None
|
|
image_endpoint: str | None = None
|
|
|
|
|
+ openrouter_api_key: SecretStr | None = Field(default=None, repr=False)
|
|
|
|
|
+ openrouter_chat_endpoint: str = "https://openrouter.ai/api/v1/chat/completions"
|
|
|
|
|
+ embedding_model: str = "qwen/qwen3-embedding-8b"
|
|
|
|
|
+ embedding_dimension: int = Field(default=512, ge=1, le=8192)
|
|
|
|
|
+ knowledge_model: str = "google/gemini-3-flash-preview"
|
|
|
|
|
+ external_image_model: str = "google/gemini-3-flash-preview"
|
|
|
|
|
+ decode_min_score: float = Field(default=0.7, ge=-1, le=1)
|
|
|
outbound_allowed_hosts: tuple[str, ...] = ()
|
|
outbound_allowed_hosts: tuple[str, ...] = ()
|
|
|
outbound_allowed_ports: tuple[int, ...] = (443,)
|
|
outbound_allowed_ports: tuple[int, ...] = (443,)
|
|
|
|
|
+ outbound_allowed_http_hosts: tuple[str, ...] = ()
|
|
|
websocket_allowed_origins: tuple[str, ...] = ()
|
|
websocket_allowed_origins: tuple[str, ...] = ()
|
|
|
runtime_factory: str | None = None
|
|
runtime_factory: str | None = None
|
|
|
bind_host: str = "127.0.0.1"
|
|
bind_host: str = "127.0.0.1"
|
|
@@ -110,8 +126,25 @@ class ScriptBuildSettings(BaseSettings):
|
|
|
return self
|
|
return self
|
|
|
|
|
|
|
|
def read_dsn(self) -> str:
|
|
def read_dsn(self) -> str:
|
|
|
|
|
+ direct = make_url(self.direct_read_dsn())
|
|
|
|
|
+ if self.read_database_socks_proxy:
|
|
|
|
|
+ direct = direct.set(
|
|
|
|
|
+ host=self.read_database_tunnel_host,
|
|
|
|
|
+ port=self.read_database_tunnel_port,
|
|
|
|
|
+ )
|
|
|
|
|
+ return direct.render_as_string(hide_password=False)
|
|
|
|
|
+
|
|
|
|
|
+ def direct_read_dsn(self) -> str:
|
|
|
return self.read_database_url.get_secret_value()
|
|
return self.read_database_url.get_secret_value()
|
|
|
|
|
|
|
|
|
|
+ def read_socks_proxy(self) -> tuple[str, int] | None:
|
|
|
|
|
+ if not self.read_database_socks_proxy:
|
|
|
|
|
+ return None
|
|
|
|
|
+ parsed = urlsplit("//" + self.read_database_socks_proxy)
|
|
|
|
|
+ if not parsed.hostname or parsed.port is None:
|
|
|
|
|
+ raise ValueError("read_database_socks_proxy must be host:port")
|
|
|
|
|
+ return parsed.hostname, parsed.port
|
|
|
|
|
+
|
|
|
def write_dsn(self) -> str:
|
|
def write_dsn(self) -> str:
|
|
|
return self.write_database_url.get_secret_value()
|
|
return self.write_database_url.get_secret_value()
|
|
|
|
|
|