콘텐츠로 이동
OmniRoute source

Usage, Quota & Spend Tracking (한국어)

OmniRoute를 통과하는 모든 요청은 다음 정보를 담은 사용량 레코드를 생성합니다.

  • 식별 정보: API 키, 제공자, 모델, 콤보
  • 토큰: 프롬프트 토큰, 완료 토큰, 캐시된 토큰, 총합
  • 비용: USD 금액(가격 데이터를 기반으로 계산)
  • 시간 정보: 지연 시간, 시작/종료 타임스탬프
  • 상태: 성공, 오류, 속도 제한 등

이러한 레코드는 분석 데이터로 집계되고, 할당량 스냅샷으로 영구 저장되며, 키별 예산 한도를 적용하는 데 사용됩니다.

요청 ──▶ chatCore ──▶ usage.record() ──▶ SQLite
│
┌───────┼───────┐
▼ ▼ ▼
분석 할당량 청구
(대시보드) (적용) (내보내기)

usage.ts 서비스는 모든 요청에 대해 사용량 이벤트를 기록합니다.

필드 유형 출처
id string 기록 시 생성되는 UUID
apiKeyId string 요청을 시작한 API 키
provider string 제공자 ID (openai, anthropic 등)
model string 모델 ID (gpt-5, claude-opus-4-6 등)
comboId string? 콤보를 통해 라우팅된 경우의 콤보 ID
promptTokens number 업스트림 응답에서 가져옴
completionTokens number 업스트림 응답에서 가져옴
cachedTokens number 캐시 적중 토큰(Anthropic 프롬프트 캐싱 등)
totalTokens number 프롬프트 + 완료
costUsd number 가격 데이터를 기반으로 계산
latencyMs number 엔드투엔드 요청 소요 시간
status enum success, error, rate_limited, timeout, cancelled
errorClass string? status != success인 경우의 오류 클래스
timestamp string ISO 8601 UTC
metadata object 사용자 정의 플러그인이 삽입한 데이터

토큰은 응답 핸들러에서 업스트림 제공자의 응답으로부터 추출됩니다.

// open-sse/handlers/chatCore.ts에서 가져옴
const response = await providerExecutor.execute(provider, request);
const usage = response.usage || {
prompt_tokens: 0,
completion_tokens: 0,
cached_tokens: 0,
};

사용량을 반환하지 않는 제공자(일부 웹 쿠키 제공자)의 경우 OmniRoute는 토큰당 약 4자 휴리스틱을 사용하여 토큰 수를 추정합니다(open-sse/services/autoCombo/pipelineRouter.ts 참조).

OmniRoute는 다음과 같은 이유로 cached_tokens를 prompt_tokens와 별도로 추적합니다.

  • Anthropic 프롬프트 캐싱에서는 캐시된 토큰에 대해 할인된 요금(정상 요금의 10%)을 부과합니다.
  • 일부 제공자는 별도로 가격을 책정해야 하는 cache_read_input_tokens를 반환합니다.
  • 분석에서는 캐시 적중률 = cached_tokens / prompt_tokens를 표시할 수 있습니다.

비용은 LiteLLM에서 동기화된 가격 데이터(src/lib/pricingSync.ts)를 사용하여 계산됩니다.

모델 입력 $/1M 출력 $/1M 캐시 $/1M
gpt-5 $2.50 $10.00 —
claude-opus-4-6 $15.00 $75.00 $1.50
claude-sonnet-4-5 $3.00 $15.00 $0.30
gemini-2.5-pro $1.25 $10.00 —

비용 계산식(src/lib/usage/costCalculator.ts):

cost =
(prompt_tokens - cached_tokens) * input_price +
cached_tokens * cached_price +
completion_tokens * output_price;

캐시된 토큰을 프롬프트 토큰에서 빼는 이유는 무엇인가요? 캐시된 부분은 별도로 가격이 책정되므로, 전체 프롬프트에 입력 가격을 적용하면 비용이 중복 계산됩니다.

가격 데이터는 /api/pricing/sync 엔드포인트를 통해 LiteLLM에서 자동으로 동기화됩니다(사용자용 환경 변수가 아닌 내장 cron 작업에 의해 실행됨).

터미널 창
# 수동 실행
curl -X POST http://localhost:20128/api/pricing/sync

가격 데이터가 없는 모델의 경우 OmniRoute는 LiteLLM의 가격 데이터에서 가져온 내부 평균 요율을 사용하여 비용을 추정합니다.


usageAnalytics.ts 모듈은 원시 사용량 데이터로부터 대시보드 위젯을 계산합니다. 다음 7개의 시간 범위를 지원합니다.

범위 기간 사용 사례
1d 최근 24시간 시간별 비용 급증 감지
7d 최근 7일 주간 검토
30d 최근 30일 월별 청구
90d 최근 90일 분기별 분석
ytd 현재 연도의 1월 1일부터 연간 예산 추적
all 전체 기간 누적 통계
custom 사용자가 정의한 시작/종료 기간 감사, 임시 쿼리

모든 날짜 범위에 대해 분석 계층은 다음을 계산합니다.

위젯 설명
요약 카드 총 요청 수, 총비용, 총 토큰 수, 성공률
일별 추세 차트 모델별로 누적 표시된 일별 비용 및 토큰 수
활동 히트맵 시간대 × 요일 그리드, 색상 = 요청 수
모델 분석 모델별 비용을 나타내는 원형 차트
제공자 분석 제공자별 요청 수를 나타내는 막대 차트
상위 API 키 비용 기준 상위 10개 키를 보여주는 표
오류 분석 시간 경과에 따른 오류율, 주요 오류 클래스
import { computeAnalytics } from "@/lib/usageAnalytics";
const analytics = await computeAnalytics(
history, // 사용량 기록
"7d", // 시간 범위: "1d" | "7d" | "30d" | "90d" | "ytd" | "all" | "custom"
connectionMap, // 제공자 연결 맵(connectionId → 계정 이름)
{
startDate: "2025-01-01", // 선택 사항: "custom" 범위에 사용
endDate: "2025-06-01", // 선택 사항: "custom" 범위에 사용
}
);
console.log(analytics.summary.totalCost); // 12.34(센트)
console.log(analytics.byModel[0]); // { model, cost, requests, promptTokens, completionTokens }
---
## 할당량 적용
API 키별 할당량은 다음 두 지점에서 적용됩니다.
1. **소프트 제한** (`quotaWarnAt`): 사용량이 임계값을 초과하면 대시보드에 경고 표시
2. **하드 제한** (`quotaLimit`): 사용량이 초과되면 HTTP 429로 요청 거부
### 구성
```ts
// API 키별
await updateApiKey(keyId, {
quotaWarnAt: 5_00, // $5.00 — 경고 표시
quotaLimit: 10_00, // $10.00 — 하드 중단
quotaWindow: "month", // "day" | "week" | "month" | "all"
});
요청 ──▶ quotaCheck()
│
├── 제한 이내? ──▶ 허용
│
└── 제한 초과? ──▶ 429 Too Many Requests
Retry-After 헤더 포함

quotaSnapshots 테이블은 추세 분석을 위한 과거 할당량 상태를 저장합니다.

| 필드 | 설명 | | ———– | –––––––––––––––– | —— | —–– | | apiKeyId | 추적 중인 키 | | window | “day” | “week” | “month” | | used | 이 기간에 사용된 비용(센트) | | limit | 제한(센트) | | resetAt | 기간이 재설정되는 시점 | | createdAt | 스냅샷이 생성된 시점 |

스냅샷은 비용이 0보다 큰 모든 요청에서 생성되며, 다음 용도로 사용됩니다.

  • 대시보드에 할당량 진행률 표시줄 렌더링
  • 30일 할당량 추세 차트 표시
  • 사용량이 제한에 근접하면 알림 트리거

터미널 창
GET /api/usage?range=7d&limit=100
GET /api/usage?apiKeyId=key-123&range=30d
GET /api/usage?provider=openai&range=1d

응답:

{
"records": [
{
"id": "uuid",
"apiKeyId": "key-123",
"provider": "openai",
"model": "gpt-5",
"promptTokens": 1234,
"completionTokens": 567,
"totalTokens": 1801,
"costUsd": 0.005,
"latencyMs": 1234,
"status": "success",
"timestamp": "2026-06-08T12:00:00Z"
}
],
"total": 1234,
"nextCursor": "..."
}
터미널 창
GET /api/usage/analytics?range=7d&groupBy=model

응답:

{
"summary": {
"totalCost": 12.34,
"totalRequests": 5678,
"totalTokens": 12345678,
"successRate": 0.987,
"avgLatencyMs": 1234
},
"models": [
{ "model": "gpt-5", "cost": 8.5, "requests": 1234, "tokens": 4567890 },
{
"model": "claude-opus-4-6",
"cost": 3.84,
"requests": 234,
"tokens": 234567
}
],
"daily": [
{ "date": "2026-06-01", "cost": 1.5, "requests": 800 },
{ "date": "2026-06-02", "cost": 2.0, "requests": 1000 }
]
}

사용량 데이터는 직접 REST 내보내기 엔드포인트가 아니라 대시보드 또는 MCP 도구를 통해 접근합니다. 사용 가능한 분석은 다음과 같습니다.

  • /api/usage/analytics — 집계된 사용량 지표(모델, 제공업체, 키별 그룹화)
  • /api/usage/quota — API 키별 현재 할당량 상태
  • /api/usage/history — 요청 기록 로그

두 개의 MCP 도구가 에이전트에 사용량 데이터를 제공합니다(open-sse/mcp-server/tools/ 참조).

도구 설명
omniroute_cost_report 지정된 기간에 대한 키별 비용 보고서 생성
omniroute_check_quota API 키의 현재 할당량 상태 반환

에이전트 호출 예시:

{
"tool": "omniroute_cost_report",
"args": { "period": "week" }
}

사용량 데이터는 요청당 약 1~10KB씩 증가합니다. 대규모 환경에서는 상당한 용량이 될 수 있습니다.

사용량 기록 보존 기간은 UI의 데이터베이스 설정 또는 /api/settings/database를 통해 구성합니다.

기본적으로 사용량 기록은 90일 동안 보존됩니다.

오래된 레코드는 src/lib/db/cleanup.ts에 의해 정리됩니다.

  • 백그라운드 cron 프로세스에 의해 실행됨
  • 구성된 usageHistory 보존 설정보다 오래된 usage_history 레코드를 삭제함
요청 빈도 30일 저장 공간 90일 저장 공간
일 100회 요청 ~3MB ~9MB
일 1,000회 요청 ~30MB ~90MB
일 10,000회 요청 ~300MB ~900MB
일 100,000회 요청 ~3GB ~9GB

트래픽이 매우 많은 경우 다음을 고려하세요.

  • 데이터베이스 설정에서 보존 기간 단축
  • 원시 레코드 대신 aggregated_metrics 사용(분석 전용)

터미널 창
# 빠른 답변 — 저렴하고 빠른 모델 사용
curl -d '{"model":"auto/fast","messages":[...]}'
# 복잡한 작업 — 고품질 모델 사용
curl -d '{"model":"auto/smart","messages":[...]}'

Anthropic 프롬프트 캐싱은 반복되는 컨텍스트의 비용을 90% 절감합니다.

// 캐싱은 자동으로 수행됩니다. 동일한 대용량 시스템 프롬프트를 포함하기만 하면 됩니다.
const response = await openai.chat({
model: "claude-sonnet-4-5",
system: longSystemPrompt, // 자동으로 캐시됨
messages: [{ role: "user", content: "..." }],
});

RTK + Caveman 압축은 도구 사용이 많은 세션에서 15~95%를 절감합니다.

const config = {
compression: {
engine: "rtk",
intensity: "aggressive",
},
};

과도한 비용 발생을 방지하려면 항상 quotaLimit을 설정하세요.

await updateApiKey(keyId, { quotaLimit: 10_00 }); // 월 $10 한도

대시보드 또는 **/api/usage/analytics**를 사용하여 API 키별로 그룹화하고 비용순으로 정렬하세요.

터미널 창
GET /api/usage/analytics?groupBy=apiKey

  1. **/api/usage/analytics?groupBy=model**을 확인하여 비용이 많이 드는 모델을 찾습니다.
  2. **/api/usage/analytics?groupBy=apiKey**를 확인하여 사용량이 많은 소비자를 찾습니다.
  3. 가격 데이터가 최신인지 확인합니다: POST /api/pricing/sync
  • Dashboard → Database → Cleanup에서 DB 보존 설정을 확인하세요. 오래된 레코드는 주기적인 정리 작업(src/lib/db/cleanup.ts)에 의해 삭제됩니다.
  • src/lib/db/usage*.ts의 오류를 확인하세요. DB 쓰기 실패는 로그에 기록되지만 사용자에게 표시되지는 않습니다.
  • 요청이 실제로 chatCore에 도달했는지 확인하세요. 콤보 라우팅을 점검합니다.
  • 키의 quotaLimit 설정을 확인하세요.
  • quotaWindow가 올바르게 설정되어 있는지 확인하세요.
  • quotaSnapshots 레코드를 확인하세요. 모든 요청에서 생성되어야 합니다.

  • DATABASE_GUIDE.md — 사용량 테이블 스키마
  • ENVIRONMENT.md — 가격 동기화 환경 변수
  • AUTO-COMBO.md — auto/fast, auto/cheap이 비용을 절감하는 방법
  • API_REFERENCE.md — 전체 /api/usage/* 참조 문서
  • 소스: open-sse/services/usage.ts, src/lib/usageAnalytics.ts, src/lib/db/usage*.ts

OmniRoute 소스 코드 (a58000c7685f)

HagiCode

HagiCode는 구조화된 워크플로, 다중 에이전트 실행, Hero Dungeon 뷰를 갖춘 에이전트 코딩 작업 공간입니다.

더 스마트하고 빠르며 즐거운 에이전트 워크플로로 유용한 소프트웨어를 만드세요.

HagiCode 라이트 테마 메인 화면
  • Smart구조화된 워크플로는 의도를 아이디어부터 배포까지 실행 가능한 경로로 바꿉니다.
  • Efficient다중 에이전트 워크플로로 조사, 구현, 검토를 병렬로 진행합니다.
  • FunHero Dungeon은 긴 코딩 세션을 시각적이고 협업적인 경험으로 만듭니다.
HagiCode 방문