Skip to content

Commit 0e9b034

Browse files
committed
fix(functions): restore language translation, and token/s display
1 parent 6d11f7a commit 0e9b034

File tree

1 file changed

+110
-36
lines changed

1 file changed

+110
-36
lines changed

resources/functions/openwebui_monitor.py

+110-36
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
"""
99

1010
import logging
11+
import time
1112
from typing import Dict, Optional
12-
1313
from httpx import AsyncClient
1414
from pydantic import BaseModel, Field
1515
import json
@@ -18,40 +18,90 @@
1818
logger = logging.getLogger(__name__)
1919
logger.setLevel(logging.INFO)
2020

21+
TRANSLATIONS = {
22+
"en": {
23+
"request_failed": "Request failed: {error_msg}",
24+
"insufficient_balance": "Insufficient balance: Current balance `{balance:.4f}`",
25+
"cost": "Cost: ${cost:.4f}",
26+
"balance": "Balance: ${balance:.4f}",
27+
"tokens": "Tokens: {input}+{output}",
28+
"time_spent": "Time: {time:.2f}s",
29+
"tokens_per_sec": "{tokens_per_sec:.2f} T/s",
30+
},
31+
"zh": {
32+
"request_failed": "请求失败: {error_msg}",
33+
"insufficient_balance": "余额不足: 当前余额 `{balance:.4f}`",
34+
"cost": "费用: ¥{cost:.4f}",
35+
"balance": "余额: ¥{balance:.4f}",
36+
"tokens": "Token: {input}+{output}",
37+
"time_spent": "耗时: {time:.2f}s",
38+
"tokens_per_sec": "{tokens_per_sec:.2f} T/s",
39+
},
40+
}
41+
2142

2243
class CustomException(Exception):
2344
pass
2445

2546

2647
class Filter:
2748
class Valves(BaseModel):
28-
api_endpoint: str = Field(default="", description="openwebui-monitor's base url")
49+
api_endpoint: str = Field(
50+
default="", description="openwebui-monitor's base url"
51+
)
2952
api_key: str = Field(default="", description="openwebui-monitor's api key")
3053
priority: int = Field(default=5, description="filter priority")
54+
language: str = Field(default="zh", description="language (en/zh)")
55+
show_time_spent: bool = Field(default=True, description="show time spent")
56+
show_tokens_per_sec: bool = Field(
57+
default=True, description="show tokens per second"
58+
)
59+
show_cost: bool = Field(default=True, description="show cost")
60+
show_balance: bool = Field(default=True, description="show balance")
61+
show_tokens: bool = Field(default=True, description="show tokens")
3162

3263
def __init__(self):
3364
self.type = "filter"
65+
self.name = "OpenWebUI Monitor"
3466
self.valves = self.Valves()
3567
self.outage_map: Dict[str, bool] = {}
36-
37-
async def request(self, client: AsyncClient, url: str, headers: dict, json_data: dict):
38-
json_data = json.loads(json.dumps(json_data, default=lambda o: o.dict() if hasattr(o, "dict") else str(o)))
39-
68+
self.start_time: Optional[float] = None
69+
70+
def get_text(self, key: str, **kwargs) -> str:
71+
lang = self.valves.language if self.valves.language in TRANSLATIONS else "en"
72+
text = TRANSLATIONS[lang].get(key, TRANSLATIONS["en"][key])
73+
return text.format(**kwargs) if kwargs else text
74+
75+
async def request(
76+
self, client: AsyncClient, url: str, headers: dict, json_data: dict
77+
):
78+
json_data = json.loads(
79+
json.dumps(
80+
json_data, default=lambda o: o.dict() if hasattr(o, "dict") else str(o)
81+
)
82+
)
4083
response = await client.post(url=url, headers=headers, json=json_data)
4184
response.raise_for_status()
4285
response_data = response.json()
4386
if not response_data.get("success"):
44-
logger.error("[usage_monitor] req monitor failed: %s", response_data)
45-
raise CustomException("calculate usage failed, please contact administrator")
87+
logger.error(self.get_text("request_failed", error_msg=response_data))
88+
raise CustomException(
89+
self.get_text("request_failed", error_msg=response_data)
90+
)
4691
return response_data
4792

48-
async def inlet(self, body: dict, __metadata__: Optional[dict] = None, __user__: Optional[dict] = None) -> dict:
93+
async def inlet(
94+
self,
95+
body: dict,
96+
__metadata__: Optional[dict] = None,
97+
__user__: Optional[dict] = None,
98+
) -> dict:
4999
__user__ = __user__ or {}
50100
__metadata__ = __metadata__ or {}
51-
user_id = __user__["id"]
101+
self.start_time = time.time()
102+
user_id = __user__.get("id", "default")
52103

53104
client = AsyncClient()
54-
55105
try:
56106
response_data = await self.request(
57107
client=client,
@@ -61,17 +111,22 @@ async def inlet(self, body: dict, __metadata__: Optional[dict] = None, __user__:
61111
)
62112
self.outage_map[user_id] = response_data.get("balance", 0) <= 0
63113
if self.outage_map[user_id]:
64-
logger.info("[usage_monitor] no balance: %s", user_id)
65-
raise CustomException("no balance, please contact administrator")
66-
114+
logger.info(
115+
self.get_text(
116+
"insufficient_balance", balance=response_data.get("balance", 0)
117+
)
118+
)
119+
raise CustomException(
120+
self.get_text(
121+
"insufficient_balance", balance=response_data.get("balance", 0)
122+
)
123+
)
67124
return body
68-
69125
except Exception as err:
70-
logger.exception("[usage_monitor] error calculating usage: %s", err)
126+
logger.exception(self.get_text("request_failed", error_msg=err))
71127
if isinstance(err, CustomException):
72128
raise err
73129
raise Exception(f"error calculating usage, {err}") from err
74-
75130
finally:
76131
await client.aclose()
77132

@@ -80,17 +135,16 @@ async def outlet(
80135
body: dict,
81136
__metadata__: Optional[dict] = None,
82137
__user__: Optional[dict] = None,
83-
__event_emitter__: callable = None,
138+
__event_emitter__: Optional[callable] = None,
84139
) -> dict:
85140
__user__ = __user__ or {}
86141
__metadata__ = __metadata__ or {}
87-
user_id = __user__["id"]
142+
user_id = __user__.get("id", "default")
88143

89-
if self.outage_map[user_id]:
144+
if self.outage_map.get(user_id, False):
90145
return body
91146

92147
client = AsyncClient()
93-
94148
try:
95149
response_data = await self.request(
96150
client=client,
@@ -99,23 +153,43 @@ async def outlet(
99153
json_data={"user": __user__, "body": body},
100154
)
101155

102-
# pylint: disable=C0209
103-
stats = " | ".join(
104-
[
105-
f"Tokens: {response_data['inputTokens']} + {response_data['outputTokens']}",
106-
"Cost: %.4f" % response_data["totalCost"],
107-
"Balance: %.4f" % response_data["newBalance"],
108-
]
109-
)
110-
111-
await __event_emitter__({"type": "status", "data": {"description": stats, "done": True}})
112-
156+
stats_list = []
157+
if self.valves.show_tokens:
158+
stats_list.append(
159+
self.get_text(
160+
"tokens",
161+
input=response_data["inputTokens"],
162+
output=response_data["outputTokens"],
163+
)
164+
)
165+
if self.valves.show_cost:
166+
stats_list.append(
167+
self.get_text("cost", cost=response_data["totalCost"])
168+
)
169+
if self.valves.show_balance:
170+
stats_list.append(
171+
self.get_text("balance", balance=response_data["newBalance"])
172+
)
173+
if self.start_time and self.valves.show_time_spent:
174+
elapsed = time.time() - self.start_time
175+
stats_list.append(self.get_text("time_spent", time=elapsed))
176+
if self.valves.show_tokens_per_sec:
177+
tokens_per_sec = (
178+
response_data["outputTokens"] / elapsed if elapsed > 0 else 0
179+
)
180+
stats_list.append(
181+
self.get_text("tokens_per_sec", tokens_per_sec=tokens_per_sec)
182+
)
183+
184+
stats = " | ".join(stats_list)
185+
if __event_emitter__:
186+
await __event_emitter__(
187+
{"type": "status", "data": {"description": stats, "done": True}}
188+
)
113189
logger.info("usage_monitor: %s %s", user_id, stats)
114190
return body
115-
116191
except Exception as err:
117-
logger.exception("[usage_monitor] error calculating usage: %s", err)
118-
raise Exception(f"error calculating usage, {err}") from err
119-
192+
logger.exception(self.get_text("request_failed", error_msg=err))
193+
raise Exception(self.get_text("request_failed", error_msg=err))
120194
finally:
121195
await client.aclose()

0 commit comments

Comments
 (0)