# SDD: 사용자별 OpenAI-compatible 토큰 측정 MVP

## 위치

- Milestone: [usage-token-log-ops-mvp](../../../phase/operational-observability-provider-management/milestones/usage-token-log-ops-mvp.md)
- Phase: [PHASE.md](../../../phase/operational-observability-provider-management/PHASE.md)

## 상태

[승인됨]

## SDD 잠금

- 상태: 해제
- 사용자 리뷰: 없음
- 잠금 항목:
  - 없음

## 문제 / 비목표

- 문제: IOP dev-corp 환경에서 OpenAI-compatible 호출이 어떤 외부 principal id 또는 내부 alias의 토큰 사용량으로 귀속되는지 빠르게 측정할 기준이 필요하다. Control Plane 대시보드나 request-level ledger를 먼저 만들면 ROI 검증보다 제품 표면 구현이 커지므로, 1차는 Edge가 IOP bearer token 기반 principal mapping을 판별하고 Prometheus metric을 emit해 Grafana에서 집계하는 방향으로 고정한다.
- 비목표:
  - OpenAI-compatible request/response body 확장
  - `metadata.user` 기반 사용자 식별
  - 사용자 CRUD, tenant/org 모델, RBAC, SSO/OIDC 같은 완성된 사용자/테넌트 관리
  - provider token 또는 provider identity의 사용자-facing 노출
  - Control Plane/Client dashboard 구현
  - request-level ledger 저장소, Loki 연동, 요청별 상세 감사 조회
  - 사용자별 token 제한 warn/reject enforcement
  - Claude Opus, Gemma4 가격 baseline과 ROI 금액 산출
  - provider가 보고하지 않는 reasoning token 추정

## Source of Truth

| 영역 | 기준 | 메모 |
|------|------|------|
| Roadmap | [usage-token-log-ops-mvp](../../../phase/operational-observability-provider-management/milestones/usage-token-log-ops-mvp.md) | 1차 측정 범위, 제외 범위, 기능 Task 기준 |
| Contract | [openai-compatible-api.md](../../../../agent-contract/outer/openai-compatible-api.md) | 외부 호출 표면은 `Authorization: Bearer <token>`과 OpenAI-compatible 표준 field를 따른다 |
| Code | `apps/edge/internal/openai`, `apps/edge/internal/service`, `packages/go/observability`, `packages/go/config`, `configs/edge.yaml`, `agent-ops/skills/project/**` | principal-token alias 판별, usage 관측, metric emit, 운영 설정, project skill 기준 |
| External Provider | OpenAI-compatible provider usage payload | provider-reported usage가 있으면 token metric 원본으로 사용한다 |
| User Decision | 없음 | 사용자 결정은 채팅에서 해소되었고 추가 USER_REVIEW는 없다 |

## State Machine

| 상태 | 진입 조건 | 다음 상태 | 근거 |
|------|-----------|-----------|------|
| request_authenticated | OpenAI-compatible 요청이 `Authorization: Bearer <token>`으로 인증된다 | user_resolved 또는 metering_unattributed | Edge OpenAI auth 경계 |
| user_resolved | IOP token이 운영 매핑에서 `token_ref`, `principal_ref`, 내부 alias로 해석된다 | request_observed | Edge-owned token alias mapping |
| metering_unattributed | token 매핑이 없거나 auth가 비활성인 dev 경로다 | request_observed | raw token 없이 `unknown` 또는 configured fallback alias |
| request_observed | 요청이 provider tunnel 또는 normalized runtime path로 dispatch된다 | usage_observed 또는 request_terminal | Edge dispatch/run handle |
| usage_observed | provider/runtime usage frame 또는 complete usage가 관측된다 | metric_emitted | `Usage`/provider usage payload |
| request_terminal | usage 없이 complete/error/cancelled가 관측된다 | metric_emitted | status-only request count, unavailable usage source |
| metric_emitted | 낮은 cardinality label로 Prometheus counter가 증가한다 | 없음 | Prometheus scrape와 Grafana query |

## Interface Contract

- 계약 원문: [openai-compatible-api.md](../../../../agent-contract/outer/openai-compatible-api.md)
- 입력:
  - `Authorization: Bearer <token>`: IOP 사용자 식별의 유일한 OpenAI-compatible 호출 입력이다.
  - `principal_ref`: 별도 사용자/테넌트 프로젝트 또는 dev-corp 운영 id와 연결될 참조값이다.
  - `principal_alias`: 운영자가 Grafana에서 볼 내부 alias다.
  - `token_ref`: raw token을 저장하지 않고 metric label에 쓸 안정 식별자다.
  - `usage.input_tokens`: provider-reported input token count다.
  - `usage.output_tokens`: provider-reported output token count다.
  - `usage.reasoning_tokens`: provider가 별도 보고할 때만 사용하는 reasoning token count다.
  - `usage.cached_input_tokens`: provider가 별도 보고할 때만 사용하는 cached input token count다.
- 출력:
  - `iop_openai_requests_total`: OpenAI-compatible request count counter 후보.
  - `iop_openai_usage_tokens_total`: token usage counter 후보.
  - `iop_openai_reasoning_observed_total`: reasoning stream/content 관측 counter 후보.
  - `iop_openai_reasoning_chars_total`: reasoning text character count counter 후보.
  - project skill output: raw IOP token은 operator에게 1회 보여주고, tracked 파일에는 token reference 또는 hash와 `principal_ref`/alias만 남긴다.
- 금지:
  - pure `passthrough` response body에 IOP usage field 또는 sideband event를 섞지 않는다.
  - `metadata.user`를 사용자 식별 source로 쓰지 않는다.
  - raw token, provider token, raw prompt, raw response, request_id, session_id를 Prometheus label로 넣지 않는다.
  - provider가 별도 보고하지 않은 reasoning token을 token metric으로 추정하지 않는다.

## Acceptance Scenarios

| ID | Milestone Task | Given | When | Then |
|----|----------------|-------|------|------|
| S01 | `identity-scope` | OpenAI-compatible 요청이 bearer token으로 들어온다 | Edge가 metric context를 만든다 | 사용자 식별은 IOP token 운영 매핑에서만 파생되고 `metadata.user`는 사용되지 않는다 |
| S02 | `token-alias` | token alias 설정 또는 secret reference가 있다 | Edge가 요청을 인증/관측한다 | raw token 없이 `principal_ref`, 내부 alias, `token_ref`가 metric label 후보로 계산된다 |
| S03 | `token-issue-skill` | 운영자가 `principal_ref` 또는 내부 id를 등록한다 | 프로젝트용 skill이 IOP token을 발급한다 | raw token은 1회 전달되고 tracked 파일에는 token reference 또는 hash와 principal 매핑만 남는다 |
| S04 | `openai-scope` | CLI, A2A, local-only adapter 요청이 있다 | 1차 metering 범위를 적용한다 | OpenAI-compatible 호출만 MVP metric 대상이고 다른 표면은 후속으로 남는다 |
| S05 | `passthrough-safe` | provider route가 pure `passthrough`로 응답한다 | usage를 관측한다 | provider response body는 유지되고 metric만 Edge 내부에서 emit된다 |
| S06 | `token-breakdown` | provider가 input/output/reasoning/cached usage를 일부 또는 전부 보고한다 | usage metric을 emit한다 | 보고된 token type만 counter에 반영되고 source는 provider-reported 또는 unavailable로 구분된다 |
| S07 | `reasoning-observed` | reasoning stream/content는 보이지만 reasoning token usage가 없다 | usage metric을 emit한다 | reasoning token은 추정하지 않고 관측 여부와 character count만 보조 metric으로 남긴다 |
| S08 | `prometheus-metrics` | metric label을 구성한다 | Prometheus counter를 증가시킨다 | request_id, session_id, raw token, raw payload 같은 high-cardinality/secret 값이 label에 없다 |
| S09 | `grafana-surface` | 운영자가 사용량을 조회한다 | Grafana query/dashboard를 사용한다 | Control Plane/Client dashboard 없이 Prometheus metric으로 사용자별 사용량을 볼 수 있다 |
| S10 | `limit-followup` | 사용자별 제한 구현을 검토한다 | 후속 Milestone을 계획한다 | 1차 metric label과 rollup은 daily/monthly 제한 정책에 재사용 가능한 형태로 남는다 |

## Evidence Map

| Scenario | Required Evidence | `agent-task` 연결 | 완료 Evidence 기대 |
|----------|-------------------|------------------|---------------------------|
| S01 | bearer token 기반 alias 판별 테스트 또는 code evidence | `agent-task/m-usage-token-log-ops-mvp/...` | `Roadmap Completion`에 `identity-scope`와 S01 충족 근거 |
| S02 | raw token 미저장, principal_ref/internal alias/token_ref 산출 테스트 또는 설정 예시 | `agent-task/m-usage-token-log-ops-mvp/...` | `Roadmap Completion`에 `token-alias`와 S02 충족 근거 |
| S03 | project skill의 token 생성, raw token 1회 출력, tracked 파일 raw secret 미저장 검증 | `agent-task/m-usage-token-log-ops-mvp/...` | `Roadmap Completion`에 `token-issue-skill`와 S03 충족 근거 |
| S04 | OpenAI-compatible route만 metric 대상인 테스트 또는 문서 근거 | `agent-task/m-usage-token-log-ops-mvp/...` | `Roadmap Completion`에 `openai-scope`와 S04 충족 근거 |
| S05 | passthrough body 불변성과 metric emit 검증 | `agent-task/m-usage-token-log-ops-mvp/...` | `Roadmap Completion`에 `passthrough-safe`와 S05 충족 근거 |
| S06 | input/output/reasoning/cached token type별 metric 테스트 | `agent-task/m-usage-token-log-ops-mvp/...` | `Roadmap Completion`에 `token-breakdown`와 S06 충족 근거 |
| S07 | reasoning token 미보고 시 token 추정 없음과 보조 metric 검증 | `agent-task/m-usage-token-log-ops-mvp/...` | `Roadmap Completion`에 `reasoning-observed`와 S07 충족 근거 |
| S08 | Prometheus label allowlist 또는 cardinality/secret 제외 테스트 | `agent-task/m-usage-token-log-ops-mvp/...` | `Roadmap Completion`에 `prometheus-metrics`와 S08 충족 근거 |
| S09 | Grafana query/dashboard 예시 또는 Prometheus scrape evidence | `agent-task/m-usage-token-log-ops-mvp/...` | `Roadmap Completion`에 `grafana-surface`와 S09 충족 근거 |
| S10 | 제한 enforcement가 후속 범위로 남고 metric 재사용 기준이 문서화됨 | `agent-task/m-usage-token-log-ops-mvp/...` | `Roadmap Completion`에 `limit-followup`와 S10 충족 근거 |

## Cross-repo Dependencies

- 없음

## Drift Check

- [x] Milestone 기능 Task와 Acceptance Scenario가 일치한다.
- [x] Evidence Map이 code-review/complete.log에서 검증 가능하다.
- [x] agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
- [x] 사용자 리뷰가 필요한 항목은 `USER_REVIEW.md`에만 남겼다.

## 사용자 리뷰 이력

- 없음

## 작업 컨텍스트

- 표준선: OpenAI-compatible 호출 주체 식별은 `Authorization: Bearer <token>`의 IOP token을 Edge-owned 운영 매핑으로 `principal_ref`와 내부 alias에 연결한다.
- 표준선: IOP는 사용자/테넌트 source of truth가 아니며, `principal_ref`는 별도 사용자/테넌트 프로젝트 또는 dev-corp 운영 id에 대한 foreign-key-like 참조값이다.
- 표준선: project skill은 `principal_ref` 또는 내부 id를 받아 raw IOP token을 생성하되 raw token은 tracked 파일이나 metric/log에 남기지 않는다.
- 표준선: Prometheus는 숫자 집계 표면이고 Grafana가 1차 조회 표면이다. request-level 상세 감사는 후속 ledger/Loki 축에서 다룬다.
- 표준선: pure `passthrough` response body는 provider-original 경계를 유지하고, usage metric은 Edge 내부 관측으로만 emit한다.
- 표준선: reasoning token은 provider-reported 값만 token으로 인정한다. reasoning text 기반 tokenizer 추정은 하지 않는다.
- 후속 SDD: [request-execution-log-usage-ledger-foundation SDD](../request-execution-log-usage-ledger-foundation/SDD.md)
