5.6 KiB
5.6 KiB
Server (Express + TypeScript)
진입점: server/src/index.ts
Directory Structure
server/src/
index.ts # Express 엔트리포인트, API/admin 라우트 마운트, dashboard 정적 파일 서빙
config/
admin-auth.ts # 관리자 인증 ENV 파싱
database.ts # core DB 초기화
analytics-db.ts # analytics DB 초기화
request-logs-db.ts # 월별 request log DB 초기화
routes/
api.ts # /v1 핸들러
admin-auth.ts # /admin/auth 핸들러
admin.ts # /admin CRUD 핸들러
analytics.ts # /admin/analytics 핸들러
services/
RouterService.ts # 백엔드 선택 및 포워딩
ModelCatalogService.ts # 백엔드 모델 캐시, 모델 인덱스, rewrite/fallback 규칙
AnalyticsService.ts # 사용량 집계 및 요청 로그 조회
ScriptEngine.ts # 스크립트 오케스트레이션
utils/
adminAuth.ts # 관리자 세션/토큰 인증 + CSRF 검사
adminSecurity.ts # 비밀번호/토큰 보안 유틸
logger.ts # 콘솔 로깅
Runtime Behavior
/v1/**는 공개 라우터 API를 유지한다/health는 공개 상태 확인 엔드포인트를 유지한다/admin/**는 관리자 API 표면을 유지한다/dashboard와/dashboard/**는 빌드된 관리자 SPA를 서빙한다- 빌드된
client/dist가 있으면 관리자 UI를 함께 제공하고, 없으면 API 전용 모드처럼 동작한다 - 서버 시작 시 활성 백엔드의
/v1/models를 조회해 메모리 모델 카탈로그를 초기화한다 - 라우팅은 DB가 아니라 메모리 모델 카탈로그를 사용하고, 비활성 백엔드는 모델 조회와 후보 선택에서 항상 제외된다
라우트 우선순위는 다음과 같다.
/admin/auth/admin/analytics/admin/v1/health/dashboard정적 파일 + SPA fallback404
Storage
DB_DIR에core.db,analytics.db,request_logs/request_logs_YYYY-MM.db가 저장된다core.db에는admin_sessions,admin_api_tokens도 함께 저장된다core.db에는backend_models,model_rewrites도 저장된다- 시간 경계 계산은
TZ기준이다 - 상세 로그가 켜진 stream 응답은
DETAIL_STREAM_LOG_MODE에 따라 저장된다compact(기본): SSE chunk를 전달하면서 동시에 누적 파싱해 반복 필드를 제거한kyush.chat_stream.compact.v1JSON을response_body에 저장한다raw: 기존 동작처럼 raw SSE 문자열 전체를 저장한다both: compact JSON과 raw SSE를 함께 담은kyush.chat_stream.raw.v1JSON을 저장한다off: streamresponse_body저장을 생략한다. request/response headers와 request body는 detail logging 설정에 따라 계속 저장된다
Model Routing
- 요청 모델명은 먼저 전역
model_rewrites체인을 확인한다 force=1규칙은 항상source_model -> target_model로 변환하고 다음 규칙을 계속 확인한다force=0규칙은 현재 모델을 서빙하는 허용 가능한 활성 백엔드가 없을 때만 fallback 으로 적용하고 다음 규칙을 계속 확인한다- 활성 rewrite cycle은 관리자 생성/수정 시 거부하고, runtime에서도 방어한다
- 최종 모델을 서빙하는 허용 가능한 활성 백엔드가 없으면
/v1/chat/completions는 모델 미지원 오류를 반환한다 /v1/models는 허용 가능한 활성 백엔드들의 native 모델과 실제 요청 가능한 rewrite alias 합집합을 반환한다MODEL_LIST_INCLUDE_ROUTING_METADATA가 켜져 있으면/v1/models는 비표준kyush_routermetadata를 추가해 요청 모델, 최종 라우팅 모델, 적용된 rewrite path를 노출한다.
Reasoning Compatibility
- 사용자별
copy_reasoning_to_reasoning_content옵션이 켜져 있으면/v1/chat/completions응답에서reasoning을reasoning_content로 추가 복제한다 - 같은 백엔드라도 API 키별로 옵션을 다르게 둘 수 있다
- streaming 응답은 옵션이 켜진 경우에만 SSE JSON frame을 변환하고, 옵션이 꺼진 경우 기존처럼 원본 바이트를 전달한다
- 이미
reasoning_content가 있으면 덮어쓰지 않고reasoning원본도 유지한다
참고:
- 세부 라우팅 규칙과 캐시 트리거는 docs/model-routing.md 참고
Analytics 메모
/admin/analytics는 기존 usage/requests/metrics 외에 chart 전용 집계 endpoint를 함께 제공한다.- 추가 endpoint:
daily-totals,backend-quality,model-trends,response-length-histogram,response-length-box-plot AnalyticsService는analytics.db의 일별 집계와request_logs_YYYY-MM.db의 범위 조회를 함께 사용해 시계열/분포 데이터를 만든다.- 모델 추이 키는
response_model -> routed_model -> request_model -> unknown순서로 결정한다. - response length 계열 집계는
completion_tokens가 있는 요청만 포함한다. - response length histogram은 긴 꼬리 분포를 위해 로그 간격 bin을 사용한다.
- 자세한 화면/API 설명은 docs/analytics.md 참고.
Deployment Notes
- 권장 런타임은 단일 OCI 이미지다
- 이 이미지는 아래를 함께 포함한다
- 서버 런타임
- 빌드된 관리자 대시보드 자산
- database 스키마 파일
docker-compose에서는 API와 관리자 UI를 단일 포트로 노출할 수 있다- Kubernetes에서는 Traefik이 아래처럼 path 기준으로 나눈다
- 공개:
/v1/**,/health - 내부 전용:
/admin/**,/dashboard,/dashboard/**
- 공개: