mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-09-21 00:22:45 +00:00
up
This commit is contained in:
parent
3367d48ea1
commit
9d48a30fd6
14 changed files with 78 additions and 15 deletions
|
|
@ -1,2 +1,7 @@
|
|||
1. 完善mcp_servers config
|
||||
2. 完善mcp/http的服务测试
|
||||
2. 完善mcp/http的服务测试
|
||||
3. [PosixPath('.reme')]
|
||||
4. error
|
||||
5. meta信息存在一个地方
|
||||
6. 测试一个完整的Service client的框架,测试各种命令
|
||||
7. config 默认改成default
|
||||
|
|
@ -127,6 +127,13 @@ class BaseComponent(ABC):
|
|||
return Path.cwd()
|
||||
return Path(self.app_context.app_config.working_dir)
|
||||
|
||||
@property
|
||||
def working_metadata_path(self) -> Path:
|
||||
"""Resolved metadata directory: working_path / metadata_dir, or absolute metadata_dir."""
|
||||
if self.app_context is None:
|
||||
return Path.cwd() / "metadata"
|
||||
return self.working_path / self.app_context.app_config.metadata_dir
|
||||
|
||||
# ----- Lifecycle -----------------------------------------------------
|
||||
|
||||
async def _start(self) -> None:
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class BaseEmbeddingModel(BaseComponent):
|
|||
@property
|
||||
def cache_path(self) -> Path:
|
||||
"""Disk path for the embedding cache file."""
|
||||
return self.working_path / "embedding_cache" / f"{self.name}.npz"
|
||||
return self.working_metadata_path / "embedding_cache" / f"{self.name}.npz"
|
||||
|
||||
async def _start(self) -> None:
|
||||
"""Load cache from disk on startup."""
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class BaseFileGraph(BaseComponent):
|
|||
def __init__(self, graph_name: str = "default", **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.graph_name: str = graph_name or self.name
|
||||
self.graph_path: Path = self.working_path / self.component_type.value
|
||||
self.graph_path: Path = self.working_metadata_path / self.component_type.value
|
||||
self.graph_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# -- Node CRUD ---------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class BaseFileStore(BaseComponent):
|
|||
self.embedding_model = self.bind(embedding_model, BaseEmbeddingModel, default_factory=OpenAIEmbeddingModel)
|
||||
self.keyword_index = self.bind(keyword_index, BaseKeywordIndex, default_factory=BM25Index)
|
||||
self.file_graph = self.bind(file_graph, BaseFileGraph, default_factory=LocalFileGraph)
|
||||
self.store_path = self.working_path / self.component_type.value / store_name
|
||||
self.store_path = self.working_metadata_path / self.component_type.value / store_name
|
||||
self.store_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
async def upsert_file(
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class BaseFileWatcher(BaseComponent):
|
|||
async def _start(self):
|
||||
self._stop_event = asyncio.Event()
|
||||
self._background_task = asyncio.create_task(self._background_run())
|
||||
self.logger.info(f"Started watching: {self.watch_paths}")
|
||||
self.logger.info(f"Started watching: {[str(p) for p in self.watch_paths]}")
|
||||
|
||||
async def _background_run(self):
|
||||
"""Sync store then enter watch loop."""
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ class LiteFileWatcher(BaseFileWatcher):
|
|||
|
||||
invalid = set(self.watch_paths) - set(valid_paths)
|
||||
if invalid:
|
||||
self.logger.warning(f"Skipping invalid paths: {invalid}")
|
||||
self.logger.warning(f"Skipping invalid paths: {[str(p) for p in invalid]}")
|
||||
|
||||
try:
|
||||
self.logger.info(f"Watching: {valid_paths}")
|
||||
self.logger.info(f"Watching: {[str(p) for p in valid_paths]}")
|
||||
async for changes in awatch(
|
||||
*valid_paths,
|
||||
watch_filter=self.watch_filter,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class BaseKeywordIndex(BaseComponent):
|
|||
from ..tokenizer import RegexTokenizer
|
||||
|
||||
self.tokenizer = self.bind(tokenizer, BaseTokenizer, default_factory=RegexTokenizer)
|
||||
self.index_path = self.working_path / self.component_type.value
|
||||
self.index_path = self.working_metadata_path / self.component_type.value
|
||||
self.index_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
async def _start(self) -> None:
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ def parse_args(*args, **kwargs) -> tuple[str, dict]:
|
|||
|
||||
action = first
|
||||
configs: list[dict] = []
|
||||
explicit_config = False
|
||||
|
||||
for raw in args[1:]:
|
||||
arg = _strip_arg_dashes(raw)
|
||||
|
|
@ -189,9 +190,16 @@ def parse_args(*args, **kwargs) -> tuple[str, dict]:
|
|||
path = arg.split("=", 1)[1].strip()
|
||||
if path:
|
||||
configs.append(_load_config(path))
|
||||
explicit_config = True
|
||||
elif "=" in arg:
|
||||
configs.append(parse_dot_notation([arg]))
|
||||
|
||||
if not explicit_config and "default" in _CONFIG_REGISTRY:
|
||||
from ..utils import get_logger
|
||||
|
||||
get_logger().info("No config specified, using 'default'")
|
||||
configs.insert(0, _load_config("default"))
|
||||
|
||||
configs.append(kwargs)
|
||||
|
||||
merged: dict = {}
|
||||
|
|
|
|||
|
|
@ -23,44 +23,45 @@ jobs:
|
|||
- backend: demo_echo_step2
|
||||
|
||||
components:
|
||||
# 1. tokenizer — 无依赖
|
||||
# 1. tokenizer — no dependencies
|
||||
tokenizer:
|
||||
default:
|
||||
backend: regex
|
||||
|
||||
# 2. embedding_model — 无依赖
|
||||
# 2. embedding_model — no dependencies
|
||||
embedding_model:
|
||||
default:
|
||||
backend: openai
|
||||
model_name: text-embedding-3-small
|
||||
dimensions: 1536
|
||||
|
||||
# 3. file_graph — 无依赖
|
||||
# 3. file_graph — no dependencies
|
||||
file_graph:
|
||||
default:
|
||||
backend: local
|
||||
|
||||
# 4. file_parser — 无依赖
|
||||
# 4. file_parser — no dependencies
|
||||
file_parser:
|
||||
default:
|
||||
backend: default
|
||||
|
||||
# 5. keyword_index — 依赖 tokenizer
|
||||
# 5. keyword_index — depends on tokenizer
|
||||
keyword_index:
|
||||
default:
|
||||
backend: bm25
|
||||
tokenizer: default
|
||||
|
||||
# 6. file_store — 依赖 embedding_model / keyword_index / file_graph
|
||||
# 6. file_store — depends on embedding_model / keyword_index / file_graph
|
||||
file_store:
|
||||
default:
|
||||
backend: local
|
||||
store_name: default
|
||||
embedding_model: default
|
||||
# embedding_model: ""
|
||||
keyword_index: default
|
||||
file_graph: default
|
||||
|
||||
# 7. file_watcher — 依赖 file_store / file_parser
|
||||
# 7. file_watcher — depends on file_store / file_parser
|
||||
file_watcher:
|
||||
default:
|
||||
backend: lite
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ from .application import Application
|
|||
from .components import R
|
||||
from .config import parse_args
|
||||
from .enumeration import ComponentEnum
|
||||
from .utils import load_env
|
||||
|
||||
load_env()
|
||||
|
||||
|
||||
class ReMe(Application):
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ class ApplicationConfig(BaseModel):
|
|||
|
||||
app_name: str = Field(default=os.getenv("APP_NAME", "ReMe"), description="Application display name")
|
||||
working_dir: str = Field(default=".reme", description="Working directory for runtime files")
|
||||
metadata_dir: str = Field(default="reme_metadata",description="Subdirectory for ReMe persistent state")
|
||||
enable_logo: bool = Field(default=True, description="Show ASCII logo on startup")
|
||||
language: str = Field(default="", description="Default language for LLM interactions")
|
||||
log_to_console: bool = Field(default=True, description="Log to console")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"""Utility modules."""
|
||||
|
||||
from .common_utils import hash_text, execute_stream_task
|
||||
from .env_utils import load_env
|
||||
from .logger_utils import get_logger
|
||||
from .logo_utils import print_logo
|
||||
from .similarity_utils import cosine_similarity, batch_cosine_similarity
|
||||
|
|
@ -8,6 +9,7 @@ from .similarity_utils import cosine_similarity, batch_cosine_similarity
|
|||
__all__ = [
|
||||
"hash_text",
|
||||
"execute_stream_task",
|
||||
"load_env",
|
||||
"get_logger",
|
||||
"print_logo",
|
||||
"cosine_similarity",
|
||||
|
|
|
|||
36
reme4/utils/env_utils.py
Normal file
36
reme4/utils/env_utils.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
"""Load .env files into os.environ (idempotent)."""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
_LOADED = False
|
||||
|
||||
|
||||
def _parse(path: Path) -> None:
|
||||
for line in path.read_text(encoding="utf-8").splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
key, value = line.split("=", 1)
|
||||
os.environ[key.strip()] = value.strip().strip("'\"")
|
||||
|
||||
|
||||
def load_env(path: str | Path | None = None) -> None:
|
||||
"""Load .env from given path, or search cwd and up to 5 parents."""
|
||||
global _LOADED
|
||||
if _LOADED:
|
||||
return
|
||||
|
||||
if path:
|
||||
path = Path(path)
|
||||
if path.exists():
|
||||
_parse(path)
|
||||
_LOADED = True
|
||||
return
|
||||
|
||||
for directory in [Path.cwd(), *Path.cwd().parents[:5]]:
|
||||
env_path = directory / ".env"
|
||||
if env_path.exists():
|
||||
_parse(env_path)
|
||||
_LOADED = True
|
||||
return
|
||||
Loading…
Add table
Reference in a new issue