runtime: sync router/gateway/config policy and clan role registry
This commit is contained in:
@@ -449,6 +449,177 @@ TOOL_DEFINITIONS = [
|
||||
"required": ["symbol"]
|
||||
}
|
||||
}
|
||||
},
|
||||
# PRIORITY 8: 1OK Window Master tools
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "crm_search_client",
|
||||
"description": "Пошук клієнта в CRM за телефоном/email/ПІБ.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {"type": "string", "description": "Телефон, email або ім'я клієнта"}
|
||||
},
|
||||
"required": ["query"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "crm_upsert_client",
|
||||
"description": "Створити або оновити клієнта в CRM.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"client_payload": {"type": "object", "description": "Дані клієнта"}
|
||||
},
|
||||
"required": ["client_payload"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "crm_upsert_site",
|
||||
"description": "Створити або оновити об'єкт (адресу) в CRM.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"site_payload": {"type": "object", "description": "Дані об'єкта/адреси"}
|
||||
},
|
||||
"required": ["site_payload"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "crm_upsert_window_unit",
|
||||
"description": "Створити або оновити віконний блок/проріз в CRM.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"window_payload": {"type": "object", "description": "Дані віконного блоку"}
|
||||
},
|
||||
"required": ["window_payload"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "crm_create_quote",
|
||||
"description": "Створити quote/КП в CRM.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"quote_payload": {"type": "object", "description": "Дані КП/розрахунку"}
|
||||
},
|
||||
"required": ["quote_payload"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "crm_update_quote",
|
||||
"description": "Оновити існуючий quote в CRM.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"quote_id": {"type": "string"},
|
||||
"patch": {"type": "object"}
|
||||
},
|
||||
"required": ["quote_id", "patch"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "crm_create_job",
|
||||
"description": "Створити job (замір/монтаж/сервіс) в CRM.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"job_payload": {"type": "object", "description": "Дані job"}
|
||||
},
|
||||
"required": ["job_payload"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "calc_window_quote",
|
||||
"description": "Прорахунок вікон через calc-сервіс.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input_payload": {"type": "object", "description": "Вхід для калькулятора"}
|
||||
},
|
||||
"required": ["input_payload"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "docs_render_quote_pdf",
|
||||
"description": "Рендер PDF комерційної пропозиції.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"quote_id": {"type": "string"},
|
||||
"quote_payload": {"type": "object"}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "docs_render_invoice_pdf",
|
||||
"description": "Рендер PDF рахунку.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"invoice_payload": {"type": "object", "description": "Дані рахунку"}
|
||||
},
|
||||
"required": ["invoice_payload"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "schedule_propose_slots",
|
||||
"description": "Запропонувати слоти на замір/монтаж.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"params": {"type": "object", "description": "Параметри планування"}
|
||||
},
|
||||
"required": ["params"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "schedule_confirm_slot",
|
||||
"description": "Підтвердити обраний слот.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"job_id": {"type": "string"},
|
||||
"slot": {}
|
||||
},
|
||||
"required": ["job_id", "slot"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -473,6 +644,11 @@ class ToolManager:
|
||||
self.http_client = httpx.AsyncClient(timeout=60.0)
|
||||
self.swapper_url = os.getenv("SWAPPER_URL", "http://swapper-service:8890")
|
||||
self.comfy_agent_url = os.getenv("COMFY_AGENT_URL", "http://212.8.58.133:8880")
|
||||
self.oneok_crm_url = os.getenv("ONEOK_CRM_BASE_URL", "http://oneok-crm-adapter:8088").rstrip("/")
|
||||
self.oneok_calc_url = os.getenv("ONEOK_CALC_BASE_URL", "http://oneok-calc-adapter:8089").rstrip("/")
|
||||
self.oneok_docs_url = os.getenv("ONEOK_DOCS_BASE_URL", "http://oneok-docs-adapter:8090").rstrip("/")
|
||||
self.oneok_schedule_url = os.getenv("ONEOK_SCHEDULE_BASE_URL", "http://oneok-schedule-adapter:8091").rstrip("/")
|
||||
self.oneok_adapter_api_key = os.getenv("ONEOK_ADAPTER_API_KEY", "").strip()
|
||||
self.tools_config = self._load_tools_config()
|
||||
|
||||
def _load_tools_config(self) -> Dict[str, Dict]:
|
||||
@@ -560,6 +736,31 @@ class ToolManager:
|
||||
# Priority 7: Market Data (SenpAI)
|
||||
elif tool_name == "market_data":
|
||||
return await self._market_data(arguments)
|
||||
# Priority 8: 1OK tools
|
||||
elif tool_name == "crm_search_client":
|
||||
return await self._crm_search_client(arguments)
|
||||
elif tool_name == "crm_upsert_client":
|
||||
return await self._crm_upsert_client(arguments)
|
||||
elif tool_name == "crm_upsert_site":
|
||||
return await self._crm_upsert_site(arguments)
|
||||
elif tool_name == "crm_upsert_window_unit":
|
||||
return await self._crm_upsert_window_unit(arguments)
|
||||
elif tool_name == "crm_create_quote":
|
||||
return await self._crm_create_quote(arguments)
|
||||
elif tool_name == "crm_update_quote":
|
||||
return await self._crm_update_quote(arguments)
|
||||
elif tool_name == "crm_create_job":
|
||||
return await self._crm_create_job(arguments)
|
||||
elif tool_name == "calc_window_quote":
|
||||
return await self._calc_window_quote(arguments)
|
||||
elif tool_name == "docs_render_quote_pdf":
|
||||
return await self._docs_render_quote_pdf(arguments)
|
||||
elif tool_name == "docs_render_invoice_pdf":
|
||||
return await self._docs_render_invoice_pdf(arguments)
|
||||
elif tool_name == "schedule_propose_slots":
|
||||
return await self._schedule_propose_slots(arguments)
|
||||
elif tool_name == "schedule_confirm_slot":
|
||||
return await self._schedule_confirm_slot(arguments)
|
||||
else:
|
||||
return ToolResult(success=False, result=None, error=f"Unknown tool: {tool_name}")
|
||||
except Exception as e:
|
||||
@@ -2875,6 +3076,108 @@ class ToolManager:
|
||||
logger.error(f"Market data tool error: {e}")
|
||||
return ToolResult(success=False, result=None, error=str(e))
|
||||
|
||||
async def _oneok_http_call(self, base_url: str, path: str, payload: Dict[str, Any], method: str = "POST") -> ToolResult:
|
||||
url = f"{base_url}{path}"
|
||||
try:
|
||||
method_up = method.upper()
|
||||
headers = {}
|
||||
if self.oneok_adapter_api_key:
|
||||
headers["Authorization"] = f"Bearer {self.oneok_adapter_api_key}"
|
||||
if method_up == "GET":
|
||||
resp = await self.http_client.get(url, params=payload, headers=headers, timeout=30.0)
|
||||
elif method_up == "PATCH":
|
||||
resp = await self.http_client.patch(url, json=payload, headers=headers, timeout=30.0)
|
||||
else:
|
||||
resp = await self.http_client.post(url, json=payload, headers=headers, timeout=30.0)
|
||||
|
||||
if resp.status_code >= 400:
|
||||
body = (resp.text or "")[:500]
|
||||
return ToolResult(success=False, result=None, error=f"{url} -> HTTP {resp.status_code}: {body}")
|
||||
try:
|
||||
data = resp.json()
|
||||
except Exception:
|
||||
data = {"text": (resp.text or "")[:1000]}
|
||||
return ToolResult(success=True, result=data)
|
||||
except Exception as e:
|
||||
logger.error(f"1OK adapter call failed url={url}: {e}")
|
||||
return ToolResult(success=False, result=None, error=f"{url} unavailable: {e}")
|
||||
|
||||
async def _crm_search_client(self, args: Dict) -> ToolResult:
|
||||
query = (args or {}).get("query")
|
||||
if not query:
|
||||
return ToolResult(success=False, result=None, error="query is required")
|
||||
return await self._oneok_http_call(self.oneok_crm_url, "/crm/search_client", {"query": query}, method="GET")
|
||||
|
||||
async def _crm_upsert_client(self, args: Dict) -> ToolResult:
|
||||
payload = (args or {}).get("client_payload")
|
||||
if not isinstance(payload, dict):
|
||||
return ToolResult(success=False, result=None, error="client_payload is required")
|
||||
return await self._oneok_http_call(self.oneok_crm_url, "/crm/upsert_client", payload)
|
||||
|
||||
async def _crm_upsert_site(self, args: Dict) -> ToolResult:
|
||||
payload = (args or {}).get("site_payload")
|
||||
if not isinstance(payload, dict):
|
||||
return ToolResult(success=False, result=None, error="site_payload is required")
|
||||
return await self._oneok_http_call(self.oneok_crm_url, "/crm/upsert_site", payload)
|
||||
|
||||
async def _crm_upsert_window_unit(self, args: Dict) -> ToolResult:
|
||||
payload = (args or {}).get("window_payload")
|
||||
if not isinstance(payload, dict):
|
||||
return ToolResult(success=False, result=None, error="window_payload is required")
|
||||
return await self._oneok_http_call(self.oneok_crm_url, "/crm/upsert_window_unit", payload)
|
||||
|
||||
async def _crm_create_quote(self, args: Dict) -> ToolResult:
|
||||
payload = (args or {}).get("quote_payload")
|
||||
if not isinstance(payload, dict):
|
||||
return ToolResult(success=False, result=None, error="quote_payload is required")
|
||||
return await self._oneok_http_call(self.oneok_crm_url, "/crm/create_quote", payload)
|
||||
|
||||
async def _crm_update_quote(self, args: Dict) -> ToolResult:
|
||||
quote_id = (args or {}).get("quote_id")
|
||||
patch = (args or {}).get("patch")
|
||||
if not quote_id or not isinstance(patch, dict):
|
||||
return ToolResult(success=False, result=None, error="quote_id and patch are required")
|
||||
return await self._oneok_http_call(self.oneok_crm_url, "/crm/update_quote", {"quote_id": quote_id, "patch": patch}, method="PATCH")
|
||||
|
||||
async def _crm_create_job(self, args: Dict) -> ToolResult:
|
||||
payload = (args or {}).get("job_payload")
|
||||
if not isinstance(payload, dict):
|
||||
return ToolResult(success=False, result=None, error="job_payload is required")
|
||||
return await self._oneok_http_call(self.oneok_crm_url, "/crm/create_job", payload)
|
||||
|
||||
async def _calc_window_quote(self, args: Dict) -> ToolResult:
|
||||
payload = (args or {}).get("input_payload")
|
||||
if not isinstance(payload, dict):
|
||||
return ToolResult(success=False, result=None, error="input_payload is required")
|
||||
return await self._oneok_http_call(self.oneok_calc_url, "/calc/window_quote", payload)
|
||||
|
||||
async def _docs_render_quote_pdf(self, args: Dict) -> ToolResult:
|
||||
quote_id = (args or {}).get("quote_id")
|
||||
quote_payload = (args or {}).get("quote_payload")
|
||||
if not quote_id and not isinstance(quote_payload, dict):
|
||||
return ToolResult(success=False, result=None, error="quote_id or quote_payload is required")
|
||||
payload = {"quote_id": quote_id, "quote_payload": quote_payload}
|
||||
return await self._oneok_http_call(self.oneok_docs_url, "/docs/render_quote_pdf", payload)
|
||||
|
||||
async def _docs_render_invoice_pdf(self, args: Dict) -> ToolResult:
|
||||
payload = (args or {}).get("invoice_payload")
|
||||
if not isinstance(payload, dict):
|
||||
return ToolResult(success=False, result=None, error="invoice_payload is required")
|
||||
return await self._oneok_http_call(self.oneok_docs_url, "/docs/render_invoice_pdf", payload)
|
||||
|
||||
async def _schedule_propose_slots(self, args: Dict) -> ToolResult:
|
||||
payload = (args or {}).get("params")
|
||||
if not isinstance(payload, dict):
|
||||
return ToolResult(success=False, result=None, error="params is required")
|
||||
return await self._oneok_http_call(self.oneok_schedule_url, "/schedule/propose_slots", payload)
|
||||
|
||||
async def _schedule_confirm_slot(self, args: Dict) -> ToolResult:
|
||||
job_id = (args or {}).get("job_id")
|
||||
slot = (args or {}).get("slot")
|
||||
if not job_id or slot is None:
|
||||
return ToolResult(success=False, result=None, error="job_id and slot are required")
|
||||
return await self._oneok_http_call(self.oneok_schedule_url, "/schedule/confirm_slot", {"job_id": job_id, "slot": slot})
|
||||
|
||||
async def close(self):
|
||||
await self.http_client.aclose()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user