feat: NanoClaude Phase 2 — EXAONE→Gemma 파이프라인, 큐, 상태 API
- ModelAdapter: 범용 OpenAI-compat 어댑터 (stream/complete/health)
- BackendRegistry: rewriter(EXAONE) + reasoner(Gemma4) 헬스체크 루프
- 2단계 파이프라인: EXAONE rewrite → Gemma reasoning (SSE rewrite 이벤트 노출)
- Fallback: 맥미니 다운 시 EXAONE 단독 모드, stream 중간 실패 시 자동 전환
- Cancel-safe: rewrite 전/후, streaming loop 내, fallback 경로 모두 체크
- Rewrite heartbeat: complete_chat 대기 중 2초 간격 processing 이벤트
- JobQueue: Semaphore(3) 기반 동시성 제한, 정확한 queue position
- GET /chat/{job_id}/status, GET /queue/stats 엔드포인트
- DB: rewrite_model, reasoning_model, rewritten_message 컬럼 추가
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,18 +14,35 @@ CREATE TABLE IF NOT EXISTS request_logs (
|
||||
response_chars INTEGER DEFAULT 0,
|
||||
latency_ms REAL DEFAULT 0,
|
||||
created_at REAL NOT NULL,
|
||||
completed_at REAL
|
||||
completed_at REAL,
|
||||
rewrite_model TEXT,
|
||||
reasoning_model TEXT,
|
||||
rewritten_message TEXT,
|
||||
rewrite_latency_ms REAL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_logs_job ON request_logs(job_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_logs_created ON request_logs(created_at);
|
||||
"""
|
||||
|
||||
# Phase 1 → Phase 2 마이그레이션 (이미 존재하면 무시)
|
||||
MIGRATIONS = [
|
||||
"ALTER TABLE request_logs ADD COLUMN rewrite_model TEXT",
|
||||
"ALTER TABLE request_logs ADD COLUMN reasoning_model TEXT",
|
||||
"ALTER TABLE request_logs ADD COLUMN rewritten_message TEXT",
|
||||
"ALTER TABLE request_logs ADD COLUMN rewrite_latency_ms REAL DEFAULT 0",
|
||||
]
|
||||
|
||||
|
||||
async def init_db():
|
||||
async with aiosqlite.connect(settings.db_path) as db:
|
||||
await db.execute("PRAGMA journal_mode=WAL")
|
||||
await db.executescript(SCHEMA)
|
||||
for migration in MIGRATIONS:
|
||||
try:
|
||||
await db.execute(migration)
|
||||
except Exception:
|
||||
pass # 이미 존재하는 컬럼
|
||||
await db.commit()
|
||||
|
||||
|
||||
@@ -38,10 +55,26 @@ async def log_request(job_id: str, message: str, model: str, created_at: float):
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def log_completion(job_id: str, status: str, response_chars: int, latency_ms: float, completed_at: float):
|
||||
async def log_completion(
|
||||
job_id: str,
|
||||
status: str,
|
||||
response_chars: int,
|
||||
latency_ms: float,
|
||||
completed_at: float,
|
||||
*,
|
||||
rewrite_model: str | None = None,
|
||||
reasoning_model: str | None = None,
|
||||
rewritten_message: str | None = None,
|
||||
rewrite_latency_ms: float = 0,
|
||||
):
|
||||
async with aiosqlite.connect(settings.db_path) as db:
|
||||
await db.execute(
|
||||
"UPDATE request_logs SET status=?, response_chars=?, latency_ms=?, completed_at=? WHERE job_id=?",
|
||||
(status, response_chars, latency_ms, completed_at, job_id),
|
||||
"""UPDATE request_logs
|
||||
SET status=?, response_chars=?, latency_ms=?, completed_at=?,
|
||||
rewrite_model=?, reasoning_model=?, rewritten_message=?, rewrite_latency_ms=?
|
||||
WHERE job_id=?""",
|
||||
(status, response_chars, latency_ms, completed_at,
|
||||
rewrite_model, reasoning_model, rewritten_message, rewrite_latency_ms,
|
||||
job_id),
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
Reference in New Issue
Block a user