Coverage for src/kimi_code_usage/providers/openai.py: 100%
42 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-20 20:05 +0800
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-20 20:05 +0800
1from datetime import datetime, timedelta
3import aiohttp
5from . import ProviderUsage
8async def fetch_openai_usage(api_key: str, base_url: str, management_key: str | None = None) -> list[ProviderUsage]:
9 headers = {"Authorization": f"Bearer {api_key}"}
11 # Calculate start_time (1st day of current month) and end_time (tomorrow)
12 now = datetime.now()
13 start_str = now.strftime("%Y-%m-01")
14 end_str = (now + timedelta(days=1)).strftime("%Y-%m-%d")
16 base_url_stripped = base_url.rstrip('/')
17 if base_url_stripped.endswith('/v1'):
18 completions_url = f"{base_url_stripped}/organization/usage/completions?start_time={start_str}&end_time={end_str}"
19 costs_url = f"{base_url_stripped}/organization/usage/costs?start_time={start_str}&end_time={end_str}"
20 else:
21 completions_url = f"{base_url_stripped}/v1/organization/usage/completions?start_time={start_str}&end_time={end_str}"
22 costs_url = f"{base_url_stripped}/v1/organization/usage/costs?start_time={start_str}&end_time={end_str}"
24 async with aiohttp.ClientSession() as session:
25 async with session.get(completions_url, headers=headers) as resp:
26 if resp.status in (401, 403):
27 raise Exception("Requires Org Admin Key")
28 if resp.status != 200:
29 text = await resp.text()
30 raise Exception(f"OpenAI API Error {resp.status}: {text}")
31 completions_data = await resp.json()
33 cost_value = 0.0
34 try:
35 async with session.get(costs_url, headers=headers) as resp_cost:
36 if resp_cost.status == 200:
37 costs_data = await resp_cost.json()
38 for item in costs_data.get("data", []):
39 amt = item.get("amount", {})
40 if isinstance(amt, dict):
41 val = amt.get("value")
42 if val is not None:
43 cost_value += float(val)
44 except Exception:
45 pass # Ignore cost fetch errors as optional fallback
47 total_input = 0
48 total_output = 0
49 for bucket in completions_data.get("data", []):
50 total_input += bucket.get("input_tokens", 0)
51 total_output += bucket.get("output_tokens", 0)
53 total_tokens = total_input + total_output
55 return [
56 ProviderUsage(
57 provider="openai",
58 label="Tokens",
59 used=float(total_tokens),
60 limit=None,
61 remaining=None,
62 percent=None,
63 reset_at=None,
64 unit="tokens"
65 ),
66 ProviderUsage(
67 provider="openai",
68 label="Cost",
69 used=cost_value,
70 limit=None,
71 remaining=None,
72 percent=None,
73 reset_at=None,
74 unit="$"
75 )
76 ]