Initial commit
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PathConfig:
|
||||
project_root: Path
|
||||
pdf_root: Path
|
||||
database: Path
|
||||
publish_database: Path
|
||||
tmp_dir: Path
|
||||
text_dir: Path
|
||||
log_dir: Path
|
||||
papers_org: Path
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RemoteConfig:
|
||||
host: str
|
||||
database_dir: str
|
||||
pdf_dir: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AIConfig:
|
||||
backend: str
|
||||
claude_permission_mode: str
|
||||
claude_allowed_tools: tuple[str, ...]
|
||||
codex_model: str | None
|
||||
codex_sandbox: str
|
||||
codex_full_auto: bool
|
||||
|
||||
@property
|
||||
def claude_allowed_tools_argument(self) -> str:
|
||||
return ",".join(self.claude_allowed_tools)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ProcessingConfig:
|
||||
scan_directories: tuple[str, ...]
|
||||
ignore_directories: tuple[str, ...]
|
||||
max_papers: int
|
||||
initial_pages: int
|
||||
fallback_pages: int
|
||||
min_text_length: int
|
||||
update_database: bool
|
||||
validate_database: bool
|
||||
export_org: bool
|
||||
backup_database: bool
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DomainConfig:
|
||||
name: str
|
||||
description: str
|
||||
categories: tuple[str, ...]
|
||||
default_category: str
|
||||
summary_length_ja: str
|
||||
summary_focus: tuple[str, ...]
|
||||
relevance_criteria: tuple[str, ...]
|
||||
priorities: tuple[str, ...] = ("A", "B", "C", "D")
|
||||
|
||||
def validate(self) -> None:
|
||||
if not self.name.strip():
|
||||
raise ValueError("domain.nameが空です")
|
||||
if not self.description.strip():
|
||||
raise ValueError("domain.descriptionが空です")
|
||||
if not self.categories:
|
||||
raise ValueError("domain.categoriesが空です")
|
||||
if len(set(self.categories)) != len(self.categories):
|
||||
raise ValueError("domain.categoriesに重複があります")
|
||||
if self.default_category not in self.categories:
|
||||
raise ValueError(
|
||||
"default_categoryをcategoriesに含めてください"
|
||||
)
|
||||
if len(self.relevance_criteria) != 6:
|
||||
raise ValueError(
|
||||
"relevance_criteriaは0から5の6項目にしてください"
|
||||
)
|
||||
|
||||
def categories_prompt(self) -> str:
|
||||
return "\n".join(
|
||||
f"- {category}" for category in self.categories
|
||||
)
|
||||
|
||||
def summary_focus_prompt(self) -> str:
|
||||
return "\n".join(
|
||||
f"- {item}" for item in self.summary_focus
|
||||
)
|
||||
|
||||
def relevance_prompt(self) -> str:
|
||||
return "\n".join(
|
||||
f"- {score}: {description}"
|
||||
for score, description in enumerate(
|
||||
self.relevance_criteria
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Config:
|
||||
path: PathConfig
|
||||
remote: RemoteConfig
|
||||
ai: AIConfig
|
||||
processing: ProcessingConfig
|
||||
domain: DomainConfig
|
||||
|
||||
@property
|
||||
def scan_paths(self) -> tuple[Path, ...]:
|
||||
return tuple(
|
||||
self.path.pdf_root / directory
|
||||
for directory in self.processing.scan_directories
|
||||
)
|
||||
|
||||
def validate(self, *, require_remote: bool = False) -> None:
|
||||
root = self.path.project_root.resolve()
|
||||
if not root.is_dir():
|
||||
raise NotADirectoryError(root)
|
||||
if not self.path.pdf_root.is_dir():
|
||||
raise NotADirectoryError(
|
||||
f"PDF_ROOTがありません: {self.path.pdf_root}"
|
||||
)
|
||||
if not self.processing.scan_directories:
|
||||
raise ValueError("scan_directoriesが空です")
|
||||
for path in self.scan_paths:
|
||||
if not path.is_dir():
|
||||
raise NotADirectoryError(
|
||||
f"探索ディレクトリがありません: {path}"
|
||||
)
|
||||
if self.processing.max_papers < 1:
|
||||
raise ValueError("max_papersは1以上にしてください")
|
||||
if self.processing.initial_pages < 1:
|
||||
raise ValueError("initial_pagesは1以上にしてください")
|
||||
if (
|
||||
self.processing.fallback_pages
|
||||
< self.processing.initial_pages
|
||||
):
|
||||
raise ValueError(
|
||||
"fallback_pagesはinitial_pages以上にしてください"
|
||||
)
|
||||
if self.processing.min_text_length < 0:
|
||||
raise ValueError(
|
||||
"min_text_lengthは0以上にしてください"
|
||||
)
|
||||
if self.ai.backend.lower() not in {"claude", "codex"}:
|
||||
raise ValueError(
|
||||
"ai.backendはclaudeまたはcodexにしてください"
|
||||
)
|
||||
self.domain.validate()
|
||||
if require_remote:
|
||||
values = (
|
||||
self.remote.host,
|
||||
self.remote.database_dir,
|
||||
self.remote.pdf_dir,
|
||||
)
|
||||
if any(not value.strip() for value in values):
|
||||
raise ValueError("公開サーバ設定が不足しています")
|
||||
Reference in New Issue
Block a user