128 lines
9.9 KiB
Markdown
128 lines
9.9 KiB
Markdown
# Feature Specification: 토큰 수·비용 표시 (token-cost)
|
|
|
|
**Feature Branch**: `feat/token-cost` (미생성 — spec 단계)
|
|
|
|
**Created**: 2026-08-09
|
|
|
|
**Status**: Draft
|
|
|
|
**Input**: User description: "토큰 수, 토큰 비용" (`docs/orders/20260727.md` 미완료 항목)
|
|
|
|
## 배경 (현재 상태)
|
|
|
|
이미 있는 것:
|
|
|
|
- 답변(assistant) 말풍선 아래에 `총 3.2k tok · 4.1s` — 그 호출의 전체 토큰(input+output)과 소요시간
|
|
- 챗 헤더에 세션 컨텍스트 게이지 — `현재 점유 / 한도`(기본 128k), 70%↑ 주황·90%↑ 빨강
|
|
|
|
없는 것:
|
|
|
|
- **돈이 얼마 나갔는지가 어디에도 안 보임.** 메시지 계약에 비용 필드(`costUsd`)가 자리는 있는데 화면에서 한 번도 안 씀
|
|
- 입력 토큰 / 출력 토큰이 따로 안 보임 — 합계만 보임
|
|
- 대화 하나를 통틀어 **누적** 얼마 썼는지 안 보임 (헤더 게이지는 "직전 호출 점유량"이지 누적이 아님)
|
|
|
|
그래서 이 feature 의 본체는 **비용 노출**이고, 토큰 표시는 거기에 붙는 보강.
|
|
|
|
## User Scenarios & Testing *(mandatory)*
|
|
|
|
### User Story 1 - 답변마다 얼마 썼는지 보기 (Priority: P1)
|
|
|
|
사용자가 질문을 하고 답변을 받으면, 그 답변 바로 아래에 토큰 수와 함께 **이 한 번의 답변에 든 비용**이 보인다. 답변이 길어지거나 컨텍스트가 커졌을 때 "방금 게 비쌌구나"를 그 자리에서 안다.
|
|
|
|
**Why this priority**: 비용을 아예 못 보는 게 지금의 유일한 진짜 결핍. 이거 하나만 있어도 "내가 쓰는 만큼 돈이 나간다"는 감각이 생겨서 MVP 로 성립함.
|
|
|
|
**Independent Test**: 대화 하나 보내고 답변 말풍선 아래에 비용이 찍히는지, 새로고침 후 그 대화를 다시 열어도 같은 값이 남아있는지로 단독 검증 가능.
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
1. **Given** 사용자가 챗에 질문을 보내 답변이 끝난 상태, **When** 답변 말풍선 아래를 보면, **Then** 토큰 수·비용·소요시간이 한 줄로 보인다
|
|
2. **Given** 예전에 나눈 대화, **When** 그 대화를 다시 열면, **Then** 각 답변에 그때의 비용이 그대로 보인다 (다시 계산하지 않음)
|
|
3. **Given** 비용 정보가 없는 답변(옛 데이터·서버가 값을 안 줌), **When** 그 답변을 보면, **Then** 비용 칸만 빠지고 토큰·시간은 그대로 보인다 (빈칸이나 0 을 억지로 안 보여줌)
|
|
|
|
---
|
|
|
|
### User Story 2 - 이 대화에 지금까지 얼마 썼는지 보기 (Priority: P2)
|
|
|
|
사용자가 대화창 위쪽에서 **이 대화방 전체의 누적 토큰과 누적 비용**을 본다. 길게 이어온 대화가 얼마나 비싼지 한눈에 판단하고, 새 대화로 갈아탈지 정한다.
|
|
|
|
**Why this priority**: 답변별 비용(US1)만 있으면 사용자가 머리로 더해야 함. 누적이 있어야 "이 대화 접고 새로 팔까"라는 결정을 할 수 있음. 다만 US1 없이 이것만 있으면 어디서 비쌌는지 못 짚어서 후순위.
|
|
|
|
**Independent Test**: 한 대화에서 3번 질문한 뒤 헤더의 누적값이 세 답변 비용의 합과 맞는지로 검증.
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
1. **Given** 답변이 3개 쌓인 대화, **When** 헤더를 보면, **Then** 누적 토큰과 누적 비용이 보이고, 그 값은 각 답변에 찍힌 값의 합과 같다
|
|
2. **Given** 대화 도중 새 답변이 끝남, **When** 그 순간, **Then** 누적값이 바로 늘어난다 (새로고침 필요 없음)
|
|
3. **Given** 새 대화를 시작, **When** 아직 답변이 없으면, **Then** 누적값은 0 이거나 표시되지 않는다
|
|
4. **Given** 비용을 모르는 답변이 섞인 대화, **When** 누적을 보면, **Then** 아는 것만 더한 값임을 사용자가 알 수 있다 (예: `≥` 표기 또는 안내 문구)
|
|
|
|
---
|
|
|
|
### User Story 3 - 입력/출력 나눠 보기 (Priority: P3)
|
|
|
|
사용자가 토큰 표시에 마우스를 올리면 **입력 토큰과 출력 토큰이 따로** 보인다. 비용이 컨텍스트(입력) 때문인지 긴 답변(출력) 때문인지 구분한다.
|
|
|
|
**Why this priority**: 진단용 정보라 없어도 기능은 성립. 화면을 안 어지럽히려면 평소엔 숨기고 올렸을 때만 보이는 게 맞음.
|
|
|
|
**Independent Test**: 답변의 토큰 표시에 hover 해서 입력·출력 값이 나뉘어 나오는지로 검증.
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
1. **Given** 답변에 토큰 수가 찍혀 있음, **When** 그 위에 마우스를 올리면, **Then** 입력 N / 출력 M 이 따로 보인다
|
|
2. **Given** 입력·출력 중 하나만 알려진 답변, **When** hover 하면, **Then** 아는 쪽만 보인다
|
|
|
|
---
|
|
|
|
### Edge Cases
|
|
|
|
- 답변 도중 사용자가 **중지**를 눌러 스트림이 끊긴 경우 — 그때까지의 비용을 보여줄지, 아무것도 안 보여줄지
|
|
- **비용이 아주 작을 때**(반올림하면 0 이 되는 값) — `$0.00` 으로 보이면 공짜처럼 오해되므로 `<$0.01` 같은 표기 필요
|
|
- **비용이 0 인 게 진짜일 때**(무료 모델·캐시 히트) — 위와 구분되어야 함
|
|
- 서버가 비용을 **안 내려주는 경우** — 전체 기능이 조용히 빈칸이 되어야 하고 에러나 깨진 UI 가 되면 안 됨
|
|
- 대화 도중 **모델이 바뀐 경우** — 답변마다 단가가 다르므로 누적은 답변별 값의 단순 합이어야 함(대표 단가로 재계산 금지)
|
|
- 통신이 끊겼다가 **폴링으로 답변이 뒤늦게 복구**된 경우 — 비용도 같이 복구되어야 하고 중복 합산되면 안 됨
|
|
|
|
## Requirements *(mandatory)*
|
|
|
|
### Functional Requirements
|
|
|
|
- **FR-001**: 시스템은 완료된 각 답변에 대해 그 호출의 **비용**을 답변 옆(아래)에 보여줘야 한다
|
|
- **FR-002**: 시스템은 각 답변의 **토큰 수**를 계속 보여줘야 한다 (기존 표시 유지)
|
|
- **FR-003**: 시스템은 비용 값을 **서버가 알려준 값 그대로** 보여줘야 하며, 화면에서 임의로 다시 계산하지 않는다 [NEEDS CLARIFICATION: 비용을 서버가 실제로 내려주는가? 안 내려주면 화면에서 모델별 단가표로 계산해야 하는데 그건 범위가 달라짐]
|
|
- **FR-004**: 비용을 모르는 답변은 비용 부분만 생략하고 나머지 정보는 정상 표시해야 한다 (0 으로 대체 금지)
|
|
- **FR-005**: 시스템은 반올림하면 0 이 되는 아주 작은 비용을 "0" 이 아닌 별도 표기(예: 최소 표시 단위 미만)로 보여줘야 한다
|
|
- **FR-006**: 시스템은 대화방 단위 **누적 토큰·누적 비용**을 대화 화면 상단에서 보여줘야 한다
|
|
- **FR-007**: 누적값은 새 답변이 끝나는 즉시 갱신되어야 한다
|
|
- **FR-008**: 누적값은 개별 답변 값들의 단순 합이어야 하며, 값이 없는 답변은 합에서 빠지되 사용자가 "일부 누락"임을 알 수 있어야 한다
|
|
- **FR-009**: 예전 대화를 다시 열면 저장된 토큰·비용이 그대로 복원되어야 한다
|
|
- **FR-010**: 사용자는 토큰 표시에서 **입력 토큰과 출력 토큰을 나눠** 확인할 수 있어야 한다
|
|
- **FR-011**: 비용 표시 화폐 단위는 일관되어야 한다 [NEEDS CLARIFICATION: 달러 그대로 보여줄지, 원화로 바꿔 보여줄지 — 원화면 환율 출처와 갱신 주기가 추가 범위]
|
|
- **FR-012**: 이 기능은 기존 컨텍스트 점유 게이지(현재 점유 / 한도)와 **공존**해야 하며, 둘의 의미가 헷갈리지 않게 구분되어 보여야 한다
|
|
- **FR-013**: [NEEDS CLARIFICATION: 대화방을 넘어선 기간 단위 사용량(오늘/이번달 총 얼마)까지 이번 범위인지 — 이건 화면·저장·집계가 따로 필요해서 범위가 크게 달라짐]
|
|
|
|
### Key Entities
|
|
|
|
- **답변 사용량**: 답변 하나에 딸린 값 — 입력 토큰, 출력 토큰, 비용, 소요시간. 답변과 함께 저장되어 다시 열어도 남음
|
|
- **대화 누적 사용량**: 한 대화방 안 모든 답변 사용량의 합. 저장되는 값이 아니라 그때그때 더해서 보여주는 값
|
|
- **컨텍스트 점유량**: (기존) 다음 호출에 실릴 대화 길이 / 한도. 누적 사용량과 **다른 개념** — 누적은 계속 늘고, 점유량은 늘었다 줄 수 있음
|
|
|
|
## Success Criteria *(mandatory)*
|
|
|
|
### Measurable Outcomes
|
|
|
|
- **SC-001**: 답변이 끝난 뒤 사용자가 **추가 조작 없이** 그 답변의 비용을 확인할 수 있다 (클릭·이동 0회)
|
|
- **SC-002**: 사용자가 대화 하나의 총 지출을 확인하는 데 **3초 이내**, 화면 이동 없이 가능하다
|
|
- **SC-003**: 표시된 답변별 비용의 합과 화면에 표시된 누적 비용이 **100% 일치**한다
|
|
- **SC-004**: 비용 정보가 전혀 없는 환경에서도 대화 기능은 **아무 문제 없이** 동작하고, 화면에 깨진 표시나 오류 문구가 나오지 않는다
|
|
- **SC-005**: 예전 대화를 다시 열었을 때 표시되는 토큰·비용이 당시 값과 **100% 일치**한다
|
|
- **SC-006**: 비용 표시가 추가되어도 답변 렌더링 체감 속도에 **눈에 띄는 지연이 없다**
|
|
|
|
## Assumptions
|
|
|
|
- 이 기능은 **읽기 전용 표시**다. 예산 한도 설정·초과 시 차단·경고 알림 같은 건 이번 범위 밖
|
|
- 사용량 값의 진실원천은 **서버**다. 화면은 받은 값을 보여주기만 하고 스스로 토큰을 세지 않는다
|
|
- 사용자는 **자기 대화만** 본다. 다른 사람 사용량이나 팀 전체 집계는 범위 밖
|
|
- 기존 컨텍스트 점유 게이지는 **그대로 둔다**. 이번 작업은 거기에 비용을 얹는 것이지 갈아엎는 게 아님
|
|
- 표시 위치·모양은 기존 답변 하단 메타 줄과 헤더를 **재사용**한다. 새 패널·새 화면은 만들지 않음
|
|
- 비용이 화면에서 계산되어야 한다면(FR-003 이 아니라고 판명되면) 모델별 단가표를 어딘가 두고 관리해야 하며, 그건 **별도 범위**로 다시 잡는다
|