Commit fa37f0f
gemini2api bot
v1.0: comprehensive refactor — security, concurrency, real streaming, multimodal, function calling, WebUI rewrite, 51 tests, CI
Major changes
=============
Security
--------
* Bearer Token auth middleware (verify_api_key) for /v1/*
* Admin cookie auth + /webui/login for the management UI
* Drop allow_origins=[*] + credentials=True CORS anti-pattern;
opt-in via CORS_ORIGINS env var
* WebUI template XSS closed (html.escape on every dynamic value)
* Pydantic v2 strict mode with field validators (account name,
role whitelist, temperature range, …)
Concurrency / stability
-----------------------
* Global _reqid counter replaced with per-request local base
* AccountPool wrapped in asyncio.Lock; select / record_* /
release all atomic
* Per-account in-flight cap (max_concurrent) and per-minute
rate limit (rate_limit_rpm) — LRU candidates skip saturated
accounts
* Shared process-wide httpx.AsyncClient (no per-request TLS
handshake)
* Per-stage httpx.Timeout (connect/read/write/pool)
* Exponential-backoff retry on 401/403/429/5xx with stream
fallback to blocking call
* Background _env_watcher reloads .env on mtime change
Protocol
--------
* Real SSE incremental streaming (httpx.stream + server-side
chunking with delta events)
* Token usage extracted from inner_data[2]._mtokenCount /
_ttokenCount / _stokenCount
* Multipart image upload via Gemini UploadFile endpoint;
supports data: URIs, HTTP(S) URLs, and local file paths
* Function calling: tools / tool_choice passthrough and
response tool_calls detection
* parse_response picks the first non-empty candidate as the
primary answer and the second as reasoning_content
Operations
----------
* Structured logger (logger.py) with rotating file handler;
_SafeLogger adapter scrubs reserved LogRecord keys from
any 'extra' dict
* SSE endpoint /api/events/stream pushes log events to the
new /webui/logs page
* AccountPool.save_to_env() writes back atomically; WebUI
mutations persist immediately
* Token-bucket rate limiter (rate_limit.py) with
x-forwarded-for / x-real-ip awareness
WebUI
-----
* Full rewrite: dark theme, static/style.css + static/app.js
* New pages: /webui/login, /webui/logs
* Accounts page: inline add form, HAR paste → /api/accounts
/from-har
* Dashboard: 5s auto-refresh of stats
Tooling
-------
* HAR parser (har_parser.py) simplified: dead code removed,
reused by config_tool.py (no more duplicate implementation)
* config_tool.py: real-time stdout streaming into the GUI
log box, port-conflict pre-flight check
* requirements.txt pinned to compatible ranges; pytest +
pytest-asyncio added
* start.bat: 18000 -> 1800 typo fixed; where python pre-check
Tests
-----
* 51 pytest cases covering parse_response, build_jspb_header,
build_request_body, account_pool (LRU/random/limit/concurrent
/persist), auth, rate_limit, har_parser, and the /health,
/v1/models, and /v1/chat/completions endpoints
* pytest.ini with asyncio_mode=auto
* CI workflow (.github/workflows/ci.yml) runs the full
suite on Python 3.10/3.11/3.12 + pyflakes lint1 parent 2b991bd commit fa37f0f
31 files changed
Lines changed: 3891 additions & 1455 deletions
File tree
- .github/workflows
- static
- templates
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
6 | 9 | | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
7 | 18 | | |
8 | 19 | | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
9 | 32 | | |
10 | 33 | | |
11 | | - | |
12 | | - | |
| 34 | + | |
| 35 | + | |
13 | 36 | | |
14 | 37 | | |
15 | 38 | | |
16 | 39 | | |
17 | 40 | | |
18 | 41 | | |
19 | | - | |
20 | 42 | | |
21 | 43 | | |
22 | | - | |
23 | 44 | | |
24 | 45 | | |
25 | | - | |
26 | 46 | | |
27 | 47 | | |
28 | | - | |
29 | 48 | | |
30 | 49 | | |
31 | | - | |
32 | 50 | | |
33 | 51 | | |
34 | 52 | | |
35 | 53 | | |
36 | 54 | | |
37 | | - | |
38 | | - | |
| 55 | + | |
39 | 56 | | |
40 | 57 | | |
41 | 58 | | |
| |||
46 | 63 | | |
47 | 64 | | |
48 | 65 | | |
| 66 | + | |
| 67 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
13 | | - | |
| 12 | + | |
14 | 13 | | |
| 14 | + | |
| 15 | + | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
22 | 35 | | |
23 | 36 | | |
24 | 37 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
0 commit comments