Spaces:
Sleeping
Sleeping
File size: 8,449 Bytes
1063f06 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | import os
from typing import Any, Optional
import httpx
from langchain_core.tools import tool
API_URL = os.getenv("API_URL", "https://levinaleksey-wb.hf.space").rstrip("/")
HTTP_TIMEOUT = float(os.getenv("HTTP_TIMEOUT", "30.0"))
ALLOWED_FINANCE_STATUSES = {"loss", "low_margin", "has_penalties", "profit_falling", "ok"}
ALLOWED_FINANCE_SORT = {"profit", "gmv", "margin_net_pct", "roi_pct", "unit_profit_net"}
ALLOWED_UNIT_ECONOMICS_SORT = {"unit_profit_net", "unit_revenue", "unit_cost"}
async def _api_get(path: str, params: dict[str, Any] | None = None) -> Any:
"""Единый helper для GET-запросов к внешнему API."""
async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client:
# Убираем параметры со значением None, чтобы не передавать их в URL
if params:
params = {k: v for k, v in params.items() if v is not None}
response = await client.get(f"{API_URL}{path}", params=params)
response.raise_for_status()
return response.json()
async def _api_post(path: str, body: dict[str, Any]) -> Any:
"""Единый helper для POST-запросов к внешнему API."""
async with httpx.AsyncClient(timeout=HTTP_TIMEOUT) as client:
response = await client.post(f"{API_URL}{path}", json=body)
response.raise_for_status()
return response.json()
@tool
async def get_finance_summary() -> Any:
"""Общая финансовая сводка за 30 дней.
Возвращает: общую статистику (GMV, прибыль WB, чистую прибыль,
рентабельность/маржу, сумму комиссий, логистики, хранения и кол-во убыточных товаров).
"""
return await _api_get("/finance/summary")
@tool
async def get_finance_products(
limit: int = 50,
status: Optional[str] = None,
sort_by: str = "profit"
) -> Any:
"""Список товаров с основной финансовой информацией (маржа, прибыль, ROI).
Args:
limit: Сколько товаров вернуть (по умолчанию 50).
status: Опциональный фильтр статуса. Допустимые:
'loss', 'low_margin', 'has_penalties', 'profit_falling', 'ok'.
sort_by: Поле сортировки (по умолчанию 'profit'). Допустимые:
'profit', 'gmv', 'margin_net_pct', 'roi_pct', 'unit_profit_net'.
"""
if status is not None and status not in ALLOWED_FINANCE_STATUSES:
raise ValueError(
f"Некорректный status='{status}'. Допустимо: {sorted(ALLOWED_FINANCE_STATUSES)}"
)
if sort_by not in ALLOWED_FINANCE_SORT:
raise ValueError(
f"Некорректный sort_by='{sort_by}'. Допустимо: {sorted(ALLOWED_FINANCE_SORT)}"
)
return await _api_get("/finance/products", {"limit": limit, "status": status, "sort_by": sort_by})
@tool
async def get_finance_product(nm_id: int) -> Any:
"""Полная финансовая карточка конкретного товара.
Возвращает выручку, все виды расходов (комиссия, логистика, штрафы),
юнит-экономику, динамику и ROI.
Args:
nm_id: Артикул Wildberries.
"""
return await _api_get(f"/finance/product/{nm_id}")
@tool
async def get_loss_products(limit: int = 50) -> Any:
"""Убыточные товары.
Возвращает товары с отрицательной чистой прибылью (profit_net < 0).
Args:
limit: Сколько товаров вернуть.
"""
return await _api_get("/finance/loss", {"limit": limit})
@tool
async def get_low_margin_products(threshold: float = 5.0, limit: int = 50) -> Any:
"""Товары с низкой маржой.
Возвращает товары, у которых маржа (margin_net_pct) ниже заданного порога.
Args:
threshold: Порог маржи в процентах (по умолчанию 5.0%).
limit: Сколько товаров вернуть.
"""
return await _api_get("/finance/low-margin", {"threshold": threshold, "limit": limit})
@tool
async def get_top_profit_products(limit: int = 50) -> Any:
"""Самые прибыльные товары.
Возвращает ТОП товаров по абсолютной чистой прибыли (profit_net > 0).
Args:
limit: Сколько товаров вернуть.
"""
return await _api_get("/finance/top-profit", {"limit": limit})
@tool
async def get_top_roi_products(limit: int = 50) -> Any:
"""Товары с лучшим возвратом на инвестиции (ROI).
Args:
limit: Сколько товаров вернуть.
"""
return await _api_get("/finance/top-roi", {"limit": limit})
@tool
async def get_unit_economics(limit: int = 50, sort_by: str = "unit_profit_net") -> Any:
"""Юнит-экономика товаров (в пересчете на 1 штуку).
Разбивка доходов и расходов на 1 единицу проданного товара.
Args:
limit: Сколько товаров вернуть.
sort_by: Поле сортировки (по умолчанию 'unit_profit_net'). Допустимые:
'unit_profit_net', 'unit_revenue', 'unit_cost'.
"""
if sort_by not in ALLOWED_UNIT_ECONOMICS_SORT:
raise ValueError(
f"Некорректный sort_by='{sort_by}'. Допустимо: {sorted(ALLOWED_UNIT_ECONOMICS_SORT)}"
)
return await _api_get("/finance/unit-economics", {"limit": limit, "sort_by": sort_by})
@tool
async def get_expenses_breakdown() -> Any:
"""Структура расходов продавца на WB.
Разбивка в абсолютных значениях и процентах: комиссия, эквайринг,
логистика, хранение, штрафы, платная приемка.
"""
return await _api_get("/finance/expenses-breakdown")
@tool
async def get_finance_trend(days: int = 30) -> Any:
"""Динамика финансов по дням (тренд).
Возвращает: продажи, GMV, прибыль, комиссию и расходы по дням
для построения графиков.
Args:
days: Период в днях (по умолчанию 30).
"""
return await _api_get("/finance/trend", {"days": days})
@tool
async def get_category_report() -> Any:
"""Финансы агрегированные по категориям товаров.
Аналитика по продажам, выручке, прибыли, средней марже и ROI
в разрезе каждой категории (предмета) магазина.
"""
return await _api_get("/finance/category-report")
@tool
async def add_finance_insight(nm_id: int, tag: str, recommendation: str, score: float) -> Any:
"""Сохранить AI-рекомендацию по финансам для товара в базу.
Args:
nm_id: Артикул Wildberries.
tag: Тег рекомендации (например 'reduce_cost', 'increase_price').
recommendation: Конкретное действие по улучшению юнит-экономики с цифрами.
score: Уверенность от 0.5 до 1.0.
"""
return await _api_post("/insights/add", {
"nm_id": nm_id,
"tag": tag,
"recommendation": recommendation,
"score": score
})
# Экспортируем все 12 инструментов одним списком
finance_tools = [
get_finance_summary,
get_finance_products,
get_finance_product,
get_loss_products,
get_low_margin_products,
get_top_profit_products,
get_top_roi_products,
get_unit_economics,
get_expenses_breakdown,
get_finance_trend,
get_category_report,
add_finance_insight,
] |