Spaces:
Sleeping
Sleeping
κΉλ―Όκ²½
fix: μλΉμ€ μ€ μ¬μΈλ±μ± μ κ²μ 곡백·μ΄λ²€νΈ 루ν λΈλ‘νΉΒ·λ€μ€ μ½λ°± λ¬Έμ ν΄κ²°
2bc2974 | """LangChain λꡬ λͺ¨μ β λμ ToolRegistryλ‘ λ°νμ λꡬ ν«λ¦¬λ‘λ μ§μ. | |
| μ λꡬ μΆκ° μ: | |
| 1. ν΄λΉ λͺ¨λμ λꡬ ν¨μ μμ± | |
| 2. λͺ¨λ νλ¨ TOOLS 리μ€νΈμ μΆκ° | |
| (μ λͺ¨λμ΄λ©΄ _TOOL_MODULESμ λͺ¨λ μΆκ°) | |
| λ°νμ λ±λ‘: | |
| registry = get_tool_registry() | |
| registry.register(my_new_tool) # λ¨μΌ λꡬ λ±λ‘ + ChromaDB μλ μΈλ±μ± | |
| registry.unregister("my_new_tool") # μ΄λ¦μΌλ‘ ν΄μ | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| import threading | |
| from typing import Callable | |
| from langchain_core.tools import BaseTool | |
| from app.tools import ( | |
| product, premium, coverage, underwriting, | |
| compliance, claims, customer_db, rag_tools, | |
| ) | |
| logger = logging.getLogger("insurance.tools.registry") | |
| _TOOL_MODULES = [ | |
| product, premium, coverage, underwriting, | |
| compliance, claims, customer_db, rag_tools, | |
| ] | |
| def _inject_when_not_to_use(tool: BaseTool) -> BaseTool: | |
| """ToolCardμ when_not_to_useλ₯Ό LLMμ΄ λ³΄λ tool descriptionμ μ£Όμ νλ€. | |
| LLMμ΄ bind_tools()λ‘ λꡬ λͺ©λ‘μ λ°μ λ description μ μ²΄κ° μ λ¬λλ€. | |
| νΌλνκΈ° μ¬μ΄ μ μ¬ λꡬ μ(μ: premium_estimate vs plan_options)μ | |
| λͺ μμ μΌλ‘ μλ €μ€μΌλ‘μ¨ μλͺ»λ λꡬ μ νμ μ€μΈλ€. | |
| when_not_to_useλ ChromaDB μλ² λ© ν μ€νΈ(to_embed_text)μλ ν¬ν¨λμ§ μλλ€. | |
| (ν λꡬ μ΄νκ° ν¬ν¨λΌ μλ² λ© λ²‘ν°λ₯Ό μ€μΌμν€κΈ° λλ¬Έ) | |
| """ | |
| from app.tool_search.tool_cards import get_card | |
| card = get_card(tool.name) | |
| if not card or not card.when_not_to_use: | |
| return tool | |
| neg = "\n[μ¬μ© κΈμ§ μν©]\n" + "\n".join(f"Β· {w}" for w in card.when_not_to_use) | |
| return tool.model_copy(update={"description": tool.description.rstrip() + neg}) | |
| class ToolRegistry: | |
| """μ€λ λ μμ λμ λꡬ λ μ§μ€νΈλ¦¬. | |
| μλ² μμ μ κΈ°μ‘΄ λͺ¨λμμ λꡬλ₯Ό μΌκ΄ λ‘λνκ³ , | |
| λ°νμμ register()/unregister()λ‘ μλ² μ¬μμ μμ΄ λꡬλ₯Ό μΆκ°Β·μ κ±°νλ€. | |
| λ³κ²½ μ λ±λ‘λ μ½λ°±(on_change)μ νΈμΆνμ¬ ChromaDB μ¬μΈλ±μ± λ±μ νΈλ¦¬κ±°νλ€. | |
| """ | |
| def __init__(self) -> None: | |
| self._tools: dict[str, BaseTool] = {} | |
| self._lock = threading.Lock() | |
| self._version = 0 | |
| self._on_change_callbacks: list[Callable[["ToolRegistry"], None]] = [] | |
| # ββ μ‘°ν ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def get_all(self) -> tuple[BaseTool, ...]: | |
| with self._lock: | |
| return tuple(self._tools.values()) | |
| def get_by_name(self, name: str) -> BaseTool | None: | |
| with self._lock: | |
| return self._tools.get(name) | |
| def version(self) -> int: | |
| return self._version | |
| def __len__(self) -> int: | |
| return len(self._tools) | |
| # ββ λ±λ‘ / ν΄μ ββββββββββββββββββββββββββββββββββββββββββ | |
| def register(self, tool: BaseTool) -> None: | |
| """λ¨μΌ λꡬ λ±λ‘. when_not_to_use μλ μ£Όμ + λ³κ²½ μ½λ°± νΈμΆ.""" | |
| enriched = _inject_when_not_to_use(tool) | |
| with self._lock: | |
| self._tools[enriched.name] = enriched | |
| self._version += 1 | |
| logger.info("Registered tool: %s (v=%d)", enriched.name, self._version) | |
| self._fire_on_change() | |
| def register_many(self, tools: list[BaseTool] | tuple[BaseTool, ...]) -> None: | |
| """λ€μ λꡬ μΌκ΄ λ±λ‘. μ½λ°±μ λ§μ§λ§μ ν λ²λ§ νΈμΆ.""" | |
| with self._lock: | |
| for t in tools: | |
| self._tools[t.name] = _inject_when_not_to_use(t) | |
| self._version += 1 | |
| logger.info("Registered %d tools (v=%d)", len(tools), self._version) | |
| self._fire_on_change() | |
| def unregister(self, name: str) -> bool: | |
| """μ΄λ¦μΌλ‘ λꡬ ν΄μ . μ κ±° μ±κ³΅ μ True.""" | |
| with self._lock: | |
| removed = self._tools.pop(name, None) | |
| if removed: | |
| self._version += 1 | |
| if removed: | |
| logger.info("Unregistered tool: %s (v=%d)", name, self._version) | |
| self._fire_on_change() | |
| return removed is not None | |
| # ββ λ³κ²½ μ½λ°± ββββββββββββββββββββββββββββββββββββββββββββ | |
| def on_change(self, callback: Callable[["ToolRegistry"], None]) -> None: | |
| """λꡬ λͺ©λ‘ λ³κ²½ μ νΈμΆλ μ½λ°±μ λ±λ‘νλ€.""" | |
| self._on_change_callbacks.append(callback) | |
| def _fire_on_change(self) -> None: | |
| """μ½λ°±μ λ°±κ·ΈλΌμ΄λ λ°λͺ¬ μ€λ λμμ μ€ν. | |
| FastAPI async μ΄λ²€νΈ 루νλ₯Ό λΈλ‘νΉνμ§ μμΌλ©΄μ, | |
| embedder._index_lockμ΄ λμ μ€νμ μ§λ ¬ννλ€. | |
| """ | |
| for cb in self._on_change_callbacks: | |
| threading.Thread( | |
| target=self._safe_callback, | |
| args=(cb,), | |
| daemon=True, | |
| name="tool-registry-reindex", | |
| ).start() | |
| def _safe_callback(self, cb: Callable[["ToolRegistry"], None]) -> None: | |
| try: | |
| cb(self) | |
| except Exception: | |
| logger.exception("on_change callback failed") | |
| # ββ μ΄κΈ° λ‘λ ββββββββββββββββββββββββββββββββββββββββββββ | |
| def load_from_modules(self) -> None: | |
| """_TOOL_MODULESμμ λꡬλ₯Ό μΌκ΄ μμ§νμ¬ λ±λ‘. μλ² μμ μ 1ν νΈμΆ.""" | |
| tools: list[BaseTool] = [] | |
| for mod in _TOOL_MODULES: | |
| tools.extend(getattr(mod, "TOOLS", [])) | |
| self.register_many(tools) | |
| # ββ μ±κΈν€ ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| _registry: ToolRegistry | None = None | |
| _registry_lock = threading.Lock() | |
| def get_tool_registry() -> ToolRegistry: | |
| """ToolRegistry μ±κΈν€μ λ°ννλ€.""" | |
| global _registry | |
| if _registry is None: | |
| with _registry_lock: | |
| if _registry is None: | |
| _registry = ToolRegistry() | |
| return _registry | |
| def get_all_tools() -> tuple[BaseTool, ...]: | |
| """νμ νΈνμ© β κΈ°μ‘΄ μ½λκ° get_all_tools()λ₯Ό νΈμΆνλ κ³³μμ λμ μ μ§.""" | |
| return get_tool_registry().get_all() | |