feat: natural language date shortcuts for litellm_cost()

Adds _resolve_date() helper that converts natural language shortcuts
into YYYY-MM-DD before calling the LiteLLM API.

Supported shortcuts:
- today, yesterday, tomorrow
- N days ago (e.g. "two days ago")
- monday-sunday (most recent occurrence)
- last monday-last sunday
- this week, last week
- this month, last month

Falls back to passthrough for already-formatted YYYY-MM-DD strings.

Updated README with new parameter documentation.
This commit is contained in:
rin.kagamine committed 2026-06-13 16:42:39 -04:00
1 parent 67f79cfabe
commit 0fd8fcb742
2 files changed
+79 -5

No files matched your search

+2 -2
View File
@@ -11,8 +11,8 @@ Check LiteLLM proxy health and authentication status. Returns which models are h
Get LiteLLM cost breakdown for a date range. Returns per-model and per-day spend, token counts, cache hit rate, and request stats. Get LiteLLM cost breakdown for a date range. Returns per-model and per-day spend, token counts, cache hit rate, and request stats.
**Parameters:** **Parameters:**
- `start_date` (str, required): Start date in YYYY-MM-DD format - `start_date` (str, required): Start date — YYYY-MM-DD or natural language shortcut (e.g. "2026-06-01", "today", "yesterday", "monday", "last monday", "two days ago", "this week", "last month")
- `end_date` (str, required): End date in YYYY-MM-DD format - `end_date` (str, required): End date — YYYY-MM-DD or natural language shortcut
- `hide_zero_spend` (bool, default false): Hide models with sub-$0.001 spend. Useful when free-tier or subscription models (e.g. Ollama Cloud) dominate the breakdown with high token counts but negligible cost. - `hide_zero_spend` (bool, default false): Hide models with sub-$0.001 spend. Useful when free-tier or subscription models (e.g. Ollama Cloud) dominate the breakdown with high token counts but negligible cost.
### `litellm_history` ### `litellm_history`
+77 -3
View File
@@ -10,6 +10,7 @@ licence: MIT
""" """
import requests import requests
from datetime import datetime, timedelta
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from typing import Optional from typing import Optional
@@ -40,6 +41,76 @@ class Tools:
"""Get base URL, strip trailing slash.""" """Get base URL, strip trailing slash."""
return self.valves.litellm_url.rstrip("/") return self.valves.litellm_url.rstrip("/")
def _resolve_date(self, date_str: str) -> str:
"""Resolve natural language date shortcuts to YYYY-MM-DD.
Supports: today, yesterday, tomorrow, N days ago,
monday-sunday (this week), last monday-last sunday,
this week, last week, this month, last month.
Falls back to passthrough if already YYYY-MM-DD or unrecognized.
"""
date_str = date_str.strip().lower()
today = datetime.utcnow().date()
# Direct shortcuts
if date_str == "today":
return today.isoformat()
if date_str == "yesterday":
return (today - timedelta(days=1)).isoformat()
if date_str == "tomorrow":
return (today + timedelta(days=1)).isoformat()
# "N days ago"
if "days ago" in date_str:
try:
n = int(date_str.split()[0])
return (today - timedelta(days=n)).isoformat()
except (ValueError, IndexError):
pass
# Day names — most recent occurrence (including today if it matches)
days = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]
if date_str in days:
target_day = days.index(date_str)
days_back = (today.weekday() - target_day) % 7
return (today - timedelta(days=days_back)).isoformat()
# "last monday", "last tuesday", etc.
if date_str.startswith("last "):
day_part = date_str[5:].strip()
if day_part in days:
target_day = days.index(day_part)
days_back = (today.weekday() - target_day) % 7 + 7
return (today - timedelta(days=days_back)).isoformat()
# "this week" → Monday of this week
if date_str == "this week":
return (today - timedelta(days=today.weekday())).isoformat()
# "last week" → Monday of last week
if date_str == "last week":
return (today - timedelta(days=today.weekday() + 7)).isoformat()
# "this month" → 1st of this month
if date_str == "this month":
return today.replace(day=1).isoformat()
# "last month" → 1st of last month
if date_str == "last month":
if today.month == 1:
return today.replace(year=today.year - 1, month=12, day=1).isoformat()
return today.replace(month=today.month - 1, day=1).isoformat()
# Already YYYY-MM-DD — passthrough
try:
datetime.strptime(date_str, "%Y-%m-%d")
return date_str
except ValueError:
pass
# Unknown — return as-is (API will reject with clear error)
return date_str
def litellm_health(self) -> str: def litellm_health(self) -> str:
""" """
Check LiteLLM proxy health and authentication status. Check LiteLLM proxy health and authentication status.
@@ -123,17 +194,20 @@ class Tools:
) -> str: ) -> str:
""" """
Get LiteLLM cost breakdown for a date range. Get LiteLLM cost breakdown for a date range.
:param start_date: Start date in YYYY-MM-DD format (e.g. "2026-06-01") :param start_date: Start date — YYYY-MM-DD or natural language shortcut (e.g. "2026-06-01", "today", "yesterday", "monday", "last monday", "two days ago", "this week", "last month")
:param end_date: End date in YYYY-MM-DD format (e.g. "2026-06-04") :param end_date: End date — YYYY-MM-DD or natural language shortcut
:param hide_zero_spend: Hide models with sub-$0.001 spend (default False). Useful when free-tier or subscription models (e.g. Ollama Cloud) dominate the breakdown with high token counts but negligible cost. :param hide_zero_spend: Hide models with sub-$0.001 spend (default False). Useful when free-tier or subscription models (e.g. Ollama Cloud) dominate the breakdown with high token counts but negligible cost.
Returns formatted cost report with per-model and per-day breakdowns, token counts, cache hit rate, and request stats. Returns formatted cost report with per-model and per-day breakdowns, token counts, cache hit rate, and request stats.
""" """
if not self.valves.litellm_master_key: if not self.valves.litellm_master_key:
return "❌ Master key required for cost data. Set the litellm_master_key valve." return "❌ Master key required for cost data. Set the litellm_master_key valve."
resolved_start = self._resolve_date(start_date)
resolved_end = self._resolve_date(end_date)
url = f"{self._base_url()}/user/daily/activity" url = f"{self._base_url()}/user/daily/activity"
headers = self._headers() headers = self._headers()
params = {"start_date": start_date, "end_date": end_date} params = {"start_date": resolved_start, "end_date": resolved_end}
try: try:
resp = requests.get(url, headers=headers, params=params, timeout=30) resp = requests.get(url, headers=headers, params=params, timeout=30)