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

+77 -3
View File
@@ -10,6 +10,7 @@ licence: MIT
"""
import requests
from datetime import datetime, timedelta
from pydantic import BaseModel, Field
from typing import Optional
@@ -40,6 +41,76 @@ class Tools:
"""Get base URL, strip trailing slash."""
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:
"""
Check LiteLLM proxy health and authentication status.
@@ -123,17 +194,20 @@ class Tools:
) -> str:
"""
Get LiteLLM cost breakdown for a date range.
:param start_date: Start date in YYYY-MM-DD format (e.g. "2026-06-01")
:param end_date: End date in YYYY-MM-DD format (e.g. "2026-06-04")
: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 — 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.
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:
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"
headers = self._headers()
params = {"start_date": start_date, "end_date": end_date}
params = {"start_date": resolved_start, "end_date": resolved_end}
try:
resp = requests.get(url, headers=headers, params=params, timeout=30)