152 Commits

Author SHA1 Message Date
4522a29ec6 fix: cleanup_ctx改为on_cleanup,修复hot_reload_cleanup coroutine未await错误
cleanup_ctx要求异步上下文管理器(需yield),但_hot_reload_cleanup是普通async def。
改用on_cleanup接受普通coroutine function。
2026-06-01 22:47:01 +08:00
0fd5ca9dc4 refactor: use EventDispatcher for hot_reload, remove invalidate_all_caches coupling 2026-06-01 18:10:28 +08:00
4e19bb8d06 fix: rbac cache clear - get actual instance from ServerEnv, not new one; uapi also clear apikeys 2026-06-01 17:18:20 +08:00
42eff6cda0 feat: cross-process cache invalidation via signal file for reuse_port multi-process 2026-06-01 17:14:32 +08:00
cd578de80d feat: add invalidate_all_caches() and GET /__hot_reload__ endpoint
invalidate_all_caches() clears module-level caches:
- rbac: UserPermissions.ur_caches + rp_caches
- pricing: PricingProgram.pricing_data
- uapi: UAPIData.apidata + org_users
- llmage: _uapi_cache + _uapiio_cache

GET /__hot_reload__ endpoint (registered only when hot_reload enabled):
- Manual trigger for cache flush during development
- Returns JSON with list of cleared caches

Automatic trigger:
- config.json change → invalidate_all_caches() called automatically

Each module cache is cleared independently with try/except so one
module's import failure doesn't block others.
2026-06-01 16:20:59 +08:00
31d66aa91b feat: add hot-reload support for config and i18n (multi-process safe)
New module: ahserver/hotreload.py
- FileWatcher: mtime-based file change detection
- HotReloader: clears JsonConfig and MiniI18N singletons on change
- Background task runs independently per process (no cross-process coord needed)
- Each process detects changes via stat() on shared filesystem

Config (conf/config.json):
  "hot_reload": true              — enable with default 2s interval
  "hot_reload": {"enabled": true, "interval": 5}  — custom interval
  omit or false                    — disabled (production default)

What gets hot-reloaded:
- config.json → JsonConfig singleton cleared, next getConfig() reloads
- i18n/*/msg.txt → MiniI18N singleton + ServerEnv.myi18n cleared

Already hot-reloads without this feature:
- .dspy files (read from disk every request)
- .md files (read from disk every request)
- .tmpl/.ui files (Jinja2 auto_reload checks mtime)

Multi-process (reuse_port=True): each worker process runs its own
HotReloader. Since all share the filesystem, mtime-based detection
works independently without Redis/signals/pub-sub.
2026-06-01 16:10:35 +08:00
8abb83e4e3 feat: inject debug_params into dspy env; add gitignore for build artifacts 2026-05-29 12:06:41 +08:00
9554e2a791 Revert "feat: add cache_sync module for cross-process cache invalidation"
This reverts commit 2d830a7b5c7e0c04a72ed9b2848ce6c10f0701a1.
2026-05-26 18:31:10 +08:00
2d830a7b5c feat: add cache_sync module for cross-process cache invalidation
- ahserver/cache_sync.py: Redis Pub/Sub triggered + local process cache
  - Each process maintains local cache (zero-latency reads)
  - Cache invalidation via Redis Pub/Sub broadcast
  - TTL fallback to prevent stale cache on missed messages
  - Global singleton via get_cache_sync()
  - Callback registration for auto-reload on invalidation
  - Already depends on redis.asyncio (used by auth_api.py)
2026-05-26 13:43:31 +08:00
574ef00881 perf: static file fast path + remove info() log on every request
- Add fast path in processorResource._handle() for static assets (.js/.css/.png/.jpg/.gif/.ico/.svg/.woff etc)
  Static files skip auth closures, i18n, url2processor, isHtml overhead
- Remove info() call in checkAuth() middleware that logged on EVERY request
  (was causing disk flush on every request, blocking the async event loop)
- Log output now only uses debug() for timecost, exception() for errors
2026-05-26 13:18:32 +08:00
8efae163b8 bugfix 2026-05-26 09:33:09 +08:00
a691774afd fix: optimize isHtml() to read only first 512 bytes instead of entire file
Changed from reading full file content to reading first 512 bytes in
binary mode, significantly improving static file serving performance
for large files like echarts.min.js (1MB+).

Before: async with aiofiles.open(fn,'r',encoding='utf-8') as f: b = await f.read()
After:  async with aiofiles.open(fn,'rb') as f: b = await f.read(512)
2026-05-26 09:27:58 +08:00
f0bf17a72c bugfix 2026-05-14 15:06:04 +08:00
e83ed1ffde bugfix 2026-05-14 14:45:35 +08:00
6994f8b399 bugfix 2026-05-09 17:11:56 +08:00
3f21a9a502 bugfix 2026-05-09 17:10:52 +08:00
d6af12fc69 bugfix 2026-05-09 16:48:40 +08:00
923836952f bugfix 2026-05-09 16:46:59 +08:00
ec30b57b23 bugfix 2026-05-09 16:42:25 +08:00
dd3fc6e37a bugfix 2026-05-05 15:31:41 +08:00
7ac39cd2fc bugfix 2026-04-28 17:33:58 +08:00
06e894cb8f bugfix 2026-04-25 11:10:58 +08:00
e510050257 bugfix 2026-04-24 17:21:47 +08:00
2757fb6203 bugfix 2026-04-24 14:04:30 +08:00
edc8663949 bugfix 2026-04-09 23:34:29 +08:00
3b08e981ca bugfix 2026-04-09 23:30:50 +08:00
10d6d919ce bugfix 2026-04-09 23:15:49 +08:00
2b48dbe094 bugfix 2026-04-09 23:05:08 +08:00
04c6195720 bugfix 2026-04-09 17:26:28 +08:00
ba302ae1c9 bugfix 2026-04-09 10:52:11 +08:00
2b5f075da6 bugfix 2026-04-09 08:17:18 +08:00
5c1c75df0f bugfix 2026-04-09 08:16:18 +08:00
a2dfc23666 bugfix 2026-04-09 08:12:53 +08:00
32add5ddb4 bugfix 2026-04-09 07:46:45 +08:00
ce4324b06f bugfix 2026-04-02 10:45:24 +08:00
eff0972ad4 bugfix 2026-04-01 18:56:13 +08:00
c0756728f3 bugfix 2026-03-31 16:13:38 +08:00
0a09c0a442 bugfix 2026-03-31 15:12:39 +08:00
977c214039 bugfix 2026-03-31 15:07:21 +08:00
83f7f6e65c bugfix 2026-03-31 15:05:03 +08:00
e702034893 bugfix 2026-03-31 14:02:10 +08:00
4dc373efe5 bugfix 2026-03-27 15:11:22 +08:00
1a8c6f9fe9 bugfix 2026-03-27 15:07:08 +08:00
890a40128f bugfix 2026-03-27 15:02:07 +08:00
6fa4dcf31d bugfix 2026-03-27 14:27:33 +08:00
3cba828cab bugfix 2026-03-24 14:29:33 +08:00
bd7f42475f bugfix 2026-03-24 14:14:03 +08:00
yumoqing
6a1ecfed78 bugfix 2026-02-28 21:11:08 +08:00
yumoqing
6bbec483d5 bugfix 2026-02-28 21:10:41 +08:00
52a6b1911d bugfix 2026-02-13 16:03:59 +08:00