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