chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
"""Addon parameters: observable mapping + normalization helper.
|
||||
|
||||
``addon_params`` is a free-form configuration dict on :class:`LightRAG` that
|
||||
controls things like summary language and entity-type prompt overrides. The
|
||||
module exposes:
|
||||
|
||||
- :class:`ObservableAddonParams` — a ``dict`` subclass that calls a callback
|
||||
whenever the contents change so the LightRAG runtime can invalidate cached
|
||||
derived state.
|
||||
- :func:`default_addon_params` — environment-driven defaults.
|
||||
- :func:`normalize_addon_params` — converts an arbitrary input into a plain
|
||||
``dict`` with the env-driven defaults backfilled.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable, Mapping
|
||||
|
||||
from lightrag.constants import DEFAULT_SUMMARY_LANGUAGE
|
||||
from lightrag.utils import get_env_value, logger
|
||||
|
||||
|
||||
# Keys that used to live in addon_params but have been superseded by
|
||||
# per-document ``process_options``. We log once when callers still pass them
|
||||
# so existing configs surface their drift without breaking.
|
||||
_DEPRECATED_ADDON_PARAM_KEYS: tuple[str, ...] = ("enable_multimodal_pipeline",)
|
||||
_warned_deprecated_keys: set[str] = set()
|
||||
|
||||
|
||||
def _emit_deprecated_addon_warnings(params: Mapping[str, Any]) -> None:
|
||||
for key in _DEPRECATED_ADDON_PARAM_KEYS:
|
||||
if key in params and key not in _warned_deprecated_keys:
|
||||
logger.warning(
|
||||
f"addon_params['{key}'] is deprecated and ignored; per-document "
|
||||
f"behaviour is now controlled by filename-hint process_options "
|
||||
f"(see docs/FileProcessingConfiguration-zh.md)."
|
||||
)
|
||||
_warned_deprecated_keys.add(key)
|
||||
|
||||
|
||||
def default_addon_params() -> dict[str, Any]:
|
||||
# Lazy import to avoid the parser_routing → utils → … cycle that
|
||||
# would otherwise form when parser_routing imports back into this
|
||||
# module via ``LightRAG`` construction paths.
|
||||
from lightrag.parser.routing import default_chunker_config
|
||||
|
||||
return {
|
||||
"language": get_env_value("SUMMARY_LANGUAGE", DEFAULT_SUMMARY_LANGUAGE, str),
|
||||
"entity_type_prompt_file": get_env_value("ENTITY_TYPE_PROMPT_FILE", "", str),
|
||||
# Per-strategy chunker parameters; mutate at runtime (e.g.
|
||||
# ``rag.addon_params["chunker"]["recursive_character"]["separators"]
|
||||
# = [...]``) to change defaults applied to subsequently
|
||||
# enqueued documents. Per-document snapshots are persisted to
|
||||
# ``full_docs[doc_id]["chunk_options"]`` at enqueue time and
|
||||
# are not affected by later runtime mutations.
|
||||
"chunker": default_chunker_config(),
|
||||
}
|
||||
|
||||
|
||||
def normalize_addon_params(addon_params: Mapping[str, Any] | None) -> dict[str, Any]:
|
||||
"""Coerce ``addon_params`` to a plain dict with env defaults backfilled."""
|
||||
from lightrag.parser.routing import default_chunker_config
|
||||
|
||||
if addon_params is None:
|
||||
normalized = default_addon_params()
|
||||
elif isinstance(addon_params, Mapping):
|
||||
_emit_deprecated_addon_warnings(addon_params)
|
||||
normalized = {
|
||||
k: v
|
||||
for k, v in addon_params.items()
|
||||
if k not in _DEPRECATED_ADDON_PARAM_KEYS
|
||||
}
|
||||
else:
|
||||
raise TypeError(
|
||||
f"addon_params must be a Mapping or None, got {type(addon_params).__name__}"
|
||||
)
|
||||
|
||||
# When the caller supplies addon_params explicitly, the dataclass
|
||||
# default_factory is skipped — fall back to environment variables so
|
||||
# ENTITY_TYPE_PROMPT_FILE / SUMMARY_LANGUAGE / chunker still apply.
|
||||
normalized.setdefault(
|
||||
"language", get_env_value("SUMMARY_LANGUAGE", DEFAULT_SUMMARY_LANGUAGE, str)
|
||||
)
|
||||
normalized.setdefault(
|
||||
"entity_type_prompt_file",
|
||||
get_env_value("ENTITY_TYPE_PROMPT_FILE", "", str),
|
||||
)
|
||||
# Build the chunker default lazily — `default_chunker_config()` reads env
|
||||
# vars (e.g. CHUNK_R_SEPARATORS via json.loads) and would raise on a
|
||||
# malformed value, which would prevent an explicit caller-supplied
|
||||
# `chunker` from bypassing a broken environment.
|
||||
if "chunker" not in normalized:
|
||||
normalized["chunker"] = default_chunker_config()
|
||||
return normalized
|
||||
|
||||
|
||||
class ObservableAddonParams(dict[str, Any]):
|
||||
def __init__(
|
||||
self,
|
||||
*args: Any,
|
||||
on_change: Callable[[], None] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
self._on_change = on_change
|
||||
|
||||
def _changed(self) -> None:
|
||||
if self._on_change is not None:
|
||||
self._on_change()
|
||||
|
||||
def __setitem__(self, key: str, value: Any) -> None:
|
||||
super().__setitem__(key, value)
|
||||
self._changed()
|
||||
|
||||
def __delitem__(self, key: str) -> None:
|
||||
super().__delitem__(key)
|
||||
self._changed()
|
||||
|
||||
def clear(self) -> None:
|
||||
if self:
|
||||
super().clear()
|
||||
self._changed()
|
||||
|
||||
def pop(self, key: str, default: Any = ...):
|
||||
existed = key in self
|
||||
if default is ...:
|
||||
value = super().pop(key)
|
||||
self._changed()
|
||||
else:
|
||||
value = super().pop(key, default)
|
||||
if existed:
|
||||
self._changed()
|
||||
return value
|
||||
|
||||
def popitem(self) -> tuple[str, Any]:
|
||||
item = super().popitem()
|
||||
self._changed()
|
||||
return item
|
||||
|
||||
def setdefault(self, key: str, default: Any = None) -> Any:
|
||||
if key in self:
|
||||
return self[key]
|
||||
value = super().setdefault(key, default)
|
||||
self._changed()
|
||||
return value
|
||||
|
||||
def update(self, *args: Any, **kwargs: Any) -> None:
|
||||
if not args and not kwargs:
|
||||
return
|
||||
super().update(*args, **kwargs)
|
||||
self._changed()
|
||||
|
||||
def __ior__(self, other: Mapping[str, Any]):
|
||||
super().__ior__(other)
|
||||
self._changed()
|
||||
return self
|
||||
Reference in New Issue
Block a user