ReMe/reme/schema/application_config.py
jinliyl 618e8cec66
feat: add entry-point plugin system and extract Auto Fin (#459)
* feat: add entry-point plugin system

* fix: harden plugin config and client loading

* docs(workflow): add detailed manual for publishing reme-auto-fin to PyPI

- Provide step-by-step instructions for updating project.version and merging branches
- Explain dependency verification for reme-ai on PyPI during build
- Specify requirements for GitHub Actions secret configuration and version uniqueness
- Describe manual workflow triggering and input of version number
- Recommend publishing order for related projects
- Clarify that only manual dispatch triggers publishing, no automatic triggers on push or tag

* feat: support plugin-defined component types

* refactor: simplify plugin configuration

* fix: isolate plugin loading and defer client fallback

* refactor: freeze built-in component registry

* fix: isolate config entry point loading

* fix: complete auto-fin package metadata
2026-08-19 17:23:23 +08:00

87 lines
4.4 KiB
Python

"""Application configuration models."""
import os
from pathlib import Path
from pathlib import PurePosixPath, PureWindowsPath
from pydantic import BaseModel, ConfigDict, Field, field_validator
from ..enumeration import component_type_name
class ComponentConfig(BaseModel):
"""Base config for a component; extra fields allowed for backend-specific options."""
model_config = ConfigDict(extra="allow")
backend: str = Field(default="", description="Backend implementation class name")
class JobConfig(ComponentConfig):
"""Config for a job — an ordered sequence of step components. Keyed by name in ApplicationConfig.jobs."""
description: str = Field(default="", description="Human-readable description")
parameters: dict = Field(default_factory=dict, description="Job-level parameters")
steps: list[ComponentConfig] = Field(default_factory=list, description="Ordered step configs")
enable_serve: bool = Field(default=True, description="Whether to expose this job through the service layer")
class ApplicationConfig(BaseModel):
"""Root config for the ReMe application."""
app_name: str = Field(default=os.getenv("APP_NAME", "ReMe"), description="Application display name")
environment: dict[str, str] = Field(
default_factory=dict,
description="Environment variables loaded once at startup and passed to agent subprocesses",
)
workspace_dir: str = Field(
default=".reme",
description="Workspace root directory for runtime files",
validate_default=True,
)
metadata_dir: str = Field(default="metadata", description="Subdirectory for ReMe persistent state")
session_dir: str = Field(default="session", description="Subdirectory for persisted agent sessions")
# dialog_dir was removed; standard transcripts are always derived as ``{session_dir}/dialog``.
mem_session_dir: str = Field(default="mem_session", description="Subdirectory for persisted agent sessions")
resource_dir: str = Field(default="resource", description="Subdirectory for external assets")
daily_dir: str = Field(default="daily", description="Subdirectory for daily memory")
digest_dir: str = Field(default="digest", description="Subdirectory for digest memory")
enable_logo: bool = Field(default=True, description="Show ASCII logo on startup")
timezone: str | None = Field(default="Asia/Shanghai", description="IANA timezone; None uses local time")
language: str = Field(default="", description="Default language for LLM interactions")
log_to_console: bool = Field(default=True, description="Log to console")
log_to_file: bool = Field(default=True, description="Log to file")
mcp_servers: dict[str, dict] = Field(default_factory=dict, description="MCP server configs by name")
plugins: list[str] = Field(default_factory=list, description="Installed plugins enabled for this app")
service: ComponentConfig = Field(default_factory=ComponentConfig, description="Service endpoint config")
jobs: dict[str, JobConfig] = Field(default_factory=dict, description="Job definitions keyed by job name")
thread_pool_max_workers: int = Field(default=0, description="Max worker threads; 0 to disable")
components: dict[str, dict[str, ComponentConfig]] = Field(
default_factory=dict,
description="Component registry keyed by type then name",
)
@field_validator("components", mode="before")
@classmethod
def normalize_component_types(cls, value):
"""Canonicalize built-in enums and allow plugin-defined component type names."""
if value is None:
return {}
if not isinstance(value, dict):
return value
return {component_type_name(component_type): group for component_type, group in value.items()}
@field_validator("workspace_dir", mode="before")
@classmethod
def normalize_workspace_dir(cls, value) -> str:
"""Expand home-relative paths once so every component sees the same absolute workspace."""
return str(Path(value).expanduser().resolve(strict=False))
@field_validator("session_dir")
@classmethod
def validate_session_dir(cls, value: str) -> str:
"""Keep standard session storage inside the configured workspace."""
path = value.strip()
if PurePosixPath(path.replace("\\", "/")).is_absolute() or PureWindowsPath(path).anchor:
raise ValueError("session_dir must be a workspace-relative path")
return value