272 lines
28 KiB
Markdown
272 lines
28 KiB
Markdown
---
|
|
spec_doc_type: spec
|
|
spec_id: input/openai-compatible-surface
|
|
status: 부분
|
|
source_evidence:
|
|
- type: contract
|
|
path: agent-contract/outer/openai-compatible-api.md
|
|
notes: OpenAI-compatible 외부 HTTP 계약
|
|
- type: contract
|
|
path: agent-contract/outer/anthropic-compatible-api.md
|
|
notes: Anthropic-compatible Messages 외부 HTTP 계약
|
|
- type: code
|
|
path: apps/edge/internal/openai/routes.go
|
|
notes: OpenAI-compatible route와 bearer auth 처리
|
|
- type: code
|
|
path: apps/edge/internal/openai/chat_handler.go
|
|
notes: Chat Completions request validation, route dispatch, tool/reasoning 정책
|
|
- type: code
|
|
path: apps/edge/internal/openai/route_resolution.go
|
|
notes: model catalog attribution policy와 direct provider id 해석
|
|
- type: code
|
|
path: apps/edge/internal/openai/principal_routes.go
|
|
notes: Managed projected route resolution and no-fallback candidate predicate
|
|
- type: code
|
|
path: apps/edge/internal/service/provider_tunnel.go
|
|
notes: Credential binding validation, lease attachment, pre-send fence, safe dispatch attribution
|
|
- type: code
|
|
path: apps/edge/internal/openai/stream_gate_ingress.go
|
|
notes: body 첫 read 전 ingress 상한과 request-local snapshot
|
|
- type: code
|
|
path: apps/edge/internal/openai/stream_gate_runtime.go
|
|
notes: runtime-enabled Chat과 provider tunnel response lifecycle
|
|
- type: code
|
|
path: apps/edge/internal/openai/chat_decode.go
|
|
notes: Chat role/content/reasoning-alias repeat history decoder
|
|
- type: code
|
|
path: apps/edge/internal/openai/responses_decode.go
|
|
notes: Responses message/reasoning/function-call repeat history decoder
|
|
- type: code
|
|
path: apps/edge/internal/openai/normalized_sse.go
|
|
notes: normalized Chat Completions SSE stream과 terminal event 처리
|
|
- type: code
|
|
path: apps/edge/internal/openai/provider_tunnel.go
|
|
notes: provider raw tunnel status/header/body passthrough 처리
|
|
- type: code
|
|
path: apps/edge/internal/openai/responses_handler.go
|
|
notes: Responses API request validation, bounded metadata 처리, non-stream completion
|
|
- type: code
|
|
path: apps/edge/internal/openai/anthropic_handler.go
|
|
notes: Anthropic Messages/CountTokens handler, protocol profile capability admission, native/bridge routing
|
|
- type: code
|
|
path: apps/edge/internal/openai/anthropic_native.go
|
|
notes: Anthropic native tunnel response relay with header allowlist
|
|
- type: code
|
|
path: apps/edge/internal/openai/anthropic_bridge.go
|
|
notes: Anthropic Messages ↔ Chat Completions bidirectional bridge
|
|
- type: code
|
|
path: apps/edge/internal/openai/anthropic_types.go
|
|
notes: Anthropic request/response types, header validation, content block decode
|
|
- type: code
|
|
path: apps/edge/internal/openai/principal.go
|
|
notes: Shared principal token hash auth for both OpenAI and Anthropic surfaces
|
|
- type: code
|
|
path: apps/edge/internal/openai/provider_tunnel.go
|
|
notes: Shared provider tunnel auth headers and passthrough
|
|
- type: code
|
|
path: packages/go/config/protocol_profile.go
|
|
notes: ConcreteProtocolProfile, ProtocolOperation, ProtocolDriver, capability admission, model mapping
|
|
- type: code
|
|
path: apps/edge/internal/openai/run_result.go
|
|
notes: RunEvent stream을 OpenAI-compatible result로 수집
|
|
- type: code
|
|
path: apps/edge/internal/openai/principal.go
|
|
notes: principal token hash auth와 authenticated principal metadata 구성
|
|
- type: code
|
|
path: apps/edge/internal/openai/usage_metrics.go
|
|
notes: Request-local terminal and actual-provider attempt usage recording
|
|
- type: code
|
|
path: apps/edge/internal/openai/stream_gate_dispatcher.go
|
|
notes: Attempt ownership and exactly-once usage finalization on close or abort
|
|
- type: test
|
|
path: apps/edge/internal/openai/chat_handler_test.go
|
|
notes: Chat Completions route와 target dispatch 검증
|
|
- type: test
|
|
path: apps/edge/internal/openai/provider_tunnel_test.go
|
|
notes: provider-pool raw tunnel passthrough 검증
|
|
- type: test
|
|
path: apps/edge/internal/openai/provider_dispatch_test.go
|
|
notes: provider/model-group attribution route binding과 strict provider identity 검증
|
|
- type: test
|
|
path: apps/edge/internal/service/model_queue_admission_test.go
|
|
notes: 공유 provider cross-model capacity와 no-candidate unavailable 검증
|
|
- type: test
|
|
path: apps/edge/internal/openai/usage_metrics_test.go
|
|
notes: Canonical provider series, request-terminal deduplication, and provider-switch attribution
|
|
- type: docs
|
|
path: docs/openai-usage-grafana.md
|
|
notes: Grafana query, daily/monthly rollup, usage origin, cloud-equivalent cost, avoided-cost ROI 조회 가이드
|
|
---
|
|
|
|
# 스펙: OpenAI-Compatible 입력 표면
|
|
|
|
## 목적
|
|
|
|
Edge가 OpenAI-compatible HTTP 요청을 받아 내부 `adapter + target` 실행으로 넘기는 현재 동작을 설명한다.
|
|
|
|
## 기능 목록
|
|
|
|
| 기능 | 설명 |
|
|
|------|------|
|
|
| OpenAI-compatible HTTP server | `openai.enabled=true`이면 Edge input manager가 `/healthz`, `/v1/models`, `/v1/chat/completions`, `/v1/responses`, `/api/` route를 제공한다. |
|
|
| bearer auth | `openai.bearer_token`이 있으면 matching bearer authorization header를 요구한다. |
|
|
| principal token auth | `openai.principal_tokens[]`가 설정된 경우 raw token의 SHA-256 hash를 `token_hash_sha256`과 매칭하고, 매칭 시 `iop_principal_ref`, `iop_principal_alias`, `iop_token_ref`, `iop_principal_source` metadata를 채운다. |
|
|
| multi-token principal | 같은 `principal_ref`에 여러 `token_ref`를 연결할 수 있으며, 사용량 metric은 사용자 합산과 token/app별 breakdown을 모두 가능하게 한다. |
|
|
| managed projection auth | `credential_plane.enabled=true` uses the fresh Control Plane projection for inbound token auth and principal route discovery. Static principal/bearer fallback is disabled. |
|
|
| managed slot route | Public model id/alias resolves to one projected route, exact slot/profile/upstream model/resource selector, and immutable revisions/generation. Unknown, cross-principal, stale, revoked, or ambiguous bindings fail closed. |
|
|
| managed provider credential | After candidate selection, Edge obtains a short-lived Node-targeted lease on the authenticated CP connection, fences it immediately before send, and never accepts caller provider credentials or same-model slot fallback. |
|
|
| legacy provider auth forwarding | Only when managed mode is disabled, `openai.provider_auth` can read a raw provider token from the configured caller header and forward it to the selected provider. |
|
|
| model catalog | `/v1/models`는 provider-pool `models[]`, legacy `openai.model_routes[]`, `openai.models` 또는 `openai.target` 순서로 노출 모델을 만든다. |
|
|
| model dispatch | request `model`은 provider-pool catalog, legacy model route, single target fallback 순서로 해석된다. |
|
|
| attribution route binding | provider-pool model은 `models[].usage_attribution`의 effective policy와 선택된 actual provider를 보존한다. direct route는 route-level `provider_id`를 top-level fallback보다 우선하며 adapter/node text를 provider identity로 대체하지 않는다. |
|
|
| provider-pool handoff | provider-pool catalog에 model이 있으면 service 요청은 `ProviderPool=true`로 전달되고 adapter/target은 provider selection 이후 확정된다. |
|
|
| cross-model provider admission | 서로 다른 외부 model key가 같은 provider id를 참조하면 Edge의 provider resource lease 하나에서 일반·long capacity를 합산한다. |
|
|
| provider-pool queue/unavailable | root provider-pool queue policy를 모든 model group에 공통 적용하고, pending request의 live candidate가 모두 사라지면 timeout을 기다리지 않고 기존 `502 node_dispatch_error` envelope로 종료한다. |
|
|
| mixed provider dispatch | model group 안에 OpenAI-compatible provider와 Ollama/native provider가 함께 있어도 request field가 아니라 selected provider capability가 passthrough 또는 normalized 실행 경로를 결정한다. |
|
|
| legacy route 변환 | legacy route는 외부 `model`을 route entry의 `adapter`, `target`, `node`, queue policy로 변환한다. |
|
|
| bounded metadata 처리 | metadata는 최대 16개 string key/value만 허용하며 실행 디렉터리나 runtime/session 소유권을 선택하지 않는다. |
|
|
| Chat Completions | `/v1/chat/completions`는 non-streaming과 streaming SSE를 지원한다. |
|
|
| Anthropic ingress | `POST /v1/messages` and `POST /anthropic/v1/messages` share one handler; the corresponding count-tokens paths share another. `/anthropic/v1/models`, and `/v1/models` with `anthropic-version`, return the Anthropic model-list shape. Wrong methods return `405 invalid_request_error`. |
|
|
| Anthropic caller auth | Anthropic ingress accepts `Authorization: Bearer <token>` or `X-Api-Key: <token>`. If both are present they must match; shared principal-token and legacy bearer fallback apply after this validation. |
|
|
| Anthropic provider-pool dispatch | Messages and count-tokens require a provider-pool model route. Native Messages requires `messages` capability and operation, while the Chat bridge requires `chat` capability and `chat_completions` operation; streaming and tools add their own capability checks. |
|
|
| bounded ingress와 Stream Evidence Gate | Chat/Responses body를 첫 read 전에 최대 16 MiB로 제한한다. `openai.stream_evidence_gate.enabled=true`인 지원 경로는 response-start staging, filter arbitration, bounded recovery와 단일 terminal을 `runtime/stream-evidence-gate`에 위임한다. |
|
|
| repeat-resume request shape | A selected continuation uses only request-local assistant content/reasoning plus a fixed English directive. Chat emits assistant provenance followed by the directive; Responses emits assistant output/reasoning items and places the directive in `instructions`. Caller messages, `input`, and original `instructions` are excluded. |
|
|
| repeat history boundary | Chat and Responses use separate endpoint decoders to create a bounded raw-free role/channel/action snapshot from the current request only. User occurrences exclude assistant anchors; missing reasoning does not infer lineage or TTL state. |
|
|
| model-driven response path | request `model`이 가리키는 provider capability가 provider raw tunnel 또는 normalized RunEvent path를 결정한다. caller metadata는 route나 response shape를 선택하지 않는다. OpenAI와 Anthropic ingress는 같은 model catalog와 provider-pool dispatch를 공유한다. |
|
|
| provider raw passthrough | `passthrough`는 provider status/header/body bytes를 기존 Edge-Node tunnel로 relay하고 pure response body에 IOP 확장 envelope를 섞지 않는다. |
|
|
| provider-native field 보존 | provider raw tunnel route는 `model` served target rewrite와 auth/header 처리 외에 selected provider가 지원하는 표준 field와 provider extension field를 보존한다. OpenAI route는 OpenAI-compatible field를, Anthropic native route는 Anthropic field를 보존한다. |
|
|
| OpenAI usage metering | OpenAI handlers emit one request terminal and canonical token/reasoning series for each actual provider attempt that reports usage. Anthropic handlers do not currently emit this metric series; native tunnel `USAGE` frames are ignored. |
|
|
| safe credential attribution | Managed OpenAI attempt metrics include only stable `credential_slot_ref` and immutable `credential_revision`; request terminals omit them, and slot alias, lease id, raw credential/key, target URL, request IDs, and payload content are forbidden labels. |
|
|
| reasoning observation metric | provider가 reasoning token을 보고하지 않고 reasoning text만 관측되면 관측 횟수와 character count 보조 metric을 emit하고, 별도 estimated-token counter(`iop_openai_reasoning_estimated_tokens_total`)로 `estimation_method="chars_div_4"` 추정을 제공한다. |
|
|
| Grafana usage surface | 1차 조회 표면은 Prometheus/Grafana query guide이며 actual `provider_id`·`served_model` 기준 daily/monthly rollup과 `usage_attribution=model_group`으로 승인된 `route_model` query-time rollup, usage origin breakdown, operator-managed cloud price baseline, cloud-equivalent cost, avoided-cost ROI 기준을 문서로 제공한다. Control Plane/Client dashboard와 request-level ledger는 후속 범위다. |
|
|
| Responses API | normalized(non-provider) `/v1/responses` supports only non-streaming string input. A provider model-group route relays `/v1/responses` to the selected provider when that candidate declares the Responses operation/capability; this is not exclusive to one driver. |
|
|
| Responses provider passthrough | provider-pool model group route와 direct OpenAI-compatible provider route의 `/v1/responses`는 provider raw tunnel을 사용한다. Edge는 `model`만 served target으로 rewrite하고 unknown/Codex field와 `stream:true` raw SSE를 provider로 relay한다. Usage is recorded with endpoint=`responses`, response_mode=`passthrough`, route_model=request alias, and the selected actual provider/served model. Responses는 선택적 기능이다. |
|
|
| strict output | strict output이 켜져 있으면 XML completion contract 기반 instruction 또는 prompt prefix를 추가할 수 있다. |
|
|
| tool call 처리 | Chat Completions `tools`는 provider native metadata 복원 또는 text tool-call synthesis/validation 경로를 사용한다. Anthropic Messages `tools`는 Chat bridge를 통해 OpenAI `tools`로 변환되거나, native Anthropic tunnel로 직접 전달된다. |
|
|
| cancel 전파 | HTTP caller timeout/cancel이 cancel-worthy error이면 Node `CancelRun`으로 전파한다. |
|
|
|
|
## 범위
|
|
|
|
- 포함: OpenAI-compatible HTTP auth, bounded ingress, request validation, route resolution, bounded metadata 처리, chat/responses 변환, provider-pool dispatch handoff, tool/reasoning/strict output 처리.
|
|
- 제외: OpenAI 원문 API 전체 호환, legacy `/v1/completions`, A2A JSON-RPC, Node adapter별 provider HTTP 세부, Control Plane 운영 API.
|
|
|
|
## 주요 흐름
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Caller
|
|
participant OpenAI as OpenAI handler
|
|
participant Service as Edge service
|
|
participant Runtime as Edge-Node runtime
|
|
|
|
Caller->>OpenAI: chat/responses request(model)
|
|
OpenAI->>OpenAI: auth, immutable projection route/binding validation
|
|
alt selected provider supports OpenAI-compatible passthrough
|
|
OpenAI->>Service: SubmitProviderTunnel(ProviderPool/direct, binding)
|
|
Service->>Service: candidate selection, lease acquire, pre-send fence
|
|
Service->>Runtime: ProviderTunnelRequest(binding, sealed lease)
|
|
Runtime-->>Service: ProviderTunnelFrame stream
|
|
Service-->>OpenAI: tunnel frames
|
|
OpenAI-->>Caller: provider status/header/body bytes
|
|
else selected provider uses normalized execution
|
|
OpenAI->>Service: SubmitRun(adapter/target or ProviderPool)
|
|
Service->>Runtime: RunRequest
|
|
Runtime-->>Service: RunEvent stream
|
|
Service-->>OpenAI: run stream
|
|
OpenAI-->>Caller: OpenAI-compatible response or SSE
|
|
end
|
|
```
|
|
|
|
## 계약
|
|
|
|
- `iop.openai-compatible-api`: `agent-contract/outer/openai-compatible-api.md`
|
|
- 내부 실행 wire: `agent-contract/inner/edge-node-runtime-wire.md`
|
|
- config/provider pool: `agent-contract/inner/edge-config-runtime-refresh.md`
|
|
|
|
## 설정/데이터/이벤트
|
|
|
|
- `configs/edge.yaml`의 `openai` 섹션이 listener, bearer token, legacy adapter/target, model routes, strict output을 제공한다.
|
|
- `credential_plane.enabled` is the startup-only managed/legacy switch. Managed mode requires TLS on OpenAI ingress, CP-Edge, and Edge-Node hops; config validation rejects legacy principal/provider-auth and static provider credential sources.
|
|
- Managed authentication and model resolution use one immutable projection view per request. Trusted principal/route/slot/revision metadata overwrites caller spoofing and remains bound across recovery admission.
|
|
- `openai.stream_evidence_gate`는 기본 비활성이고, recovery cap 0..3과 16 MiB 이하 ingress snapshot 상한을 설정한다. 변경은 현재 restart-required다.
|
|
- When `repeat_guard` is configured, Chat accepts plain `content`, `reasoning_content`, `reasoning`, and `reasoning_text` provenance for fingerprinting; Responses accepts its own text/reasoning/function-call item provenance. Signed, encrypted, and unknown values are canonical-only and never sanitation or observation payloads.
|
|
- Completed action/result fingerprints provide the only request-history progress boundary. An identical consecutive action/result is no-progress; a changed completed result is progress, while a different action alone is insufficient. No caller product, session metadata, inferred TTL, or cross-request cache participates.
|
|
- top-level `models[]`가 있으면 OpenAI model list와 provider-pool dispatch에서 legacy route보다 우선한다.
|
|
- provider-pool model의 `usage_attribution`은 생략 시 `provider`이고 `model_group`은 명시적 opt-in이다. direct dispatch는 `openai.model_routes[].provider_id`를 우선하고 없으면 `openai.provider_id`를 사용한다.
|
|
- normalized run과 provider tunnel의 성공 dispatch는 actual `provider_id`, served target, resolved node id, effective attribution policy를 Edge-local result에 보존한다. strict attempt binding은 `provider_id`만 actual provider로 인정하고 adapter 또는 node id로 대체하지 않는다.
|
|
- provider-pool model group은 capacity + priority + availability 기준으로 provider candidate를 먼저 선택하고, 선택된 provider가 OpenAI-compatible 호출 방식을 지원하면 raw tunnel passthrough로 dispatch한다. Ollama/native provider가 선택되면 normalized `RunRequest` path로 dispatch한다.
|
|
- Anthropic Messages and count-tokens do not use legacy direct-route or single-target fallback. Native responses preserve provider status, allowed headers, and body/SSE bytes; bridge responses are converted between Anthropic Messages and Chat Completions shapes.
|
|
- provider capacity와 long-context slot은 model alias별이 아니라 `node_id + provider_id`별로 공유한다. queue pending 상한과 timeout은 Edge root `provider_pool` policy이며, lease 반환·refresh·disconnect/reconnect가 모든 model group waiter를 global enqueue 순서로 재평가한다.
|
|
- provider가 full이면 queue policy에 따라 대기하지만 live candidate가 모두 사라지면 즉시 unavailable로 수렴한다. Chat Completions와 Responses provider-pool 표면은 새 public status/field 없이 HTTP 502 `node_dispatch_error`를 유지한다.
|
|
- In legacy mode, `openai.provider_auth` stores only a forwarding rule and reads raw provider material from its request-time header; inbound IOP authorization is never reused. Managed mode rejects that rule and the caller header and uses only the sealed slot lease.
|
|
- OpenAI request metadata is bounded caller context. Workspace, runtime, and session ownership are outside this input surface.
|
|
- Chat Completions와 Responses request는 caller metadata로 provider raw tunnel과 normalized response shape를 선택하지 않는다. route/provider capability만 실행 경로를 결정한다.
|
|
- run metadata에는 `openai_model`, `openai_stream`, `strict_output`, `estimated_input_tokens`, `context_class`가 들어갈 수 있다.
|
|
- provider tunnel metadata에는 routing context와 관측 후보가 들어갈 수 있으며, provider body에는 합쳐지지 않는다.
|
|
- Node complete event metadata의 `openai_tool_calls`와 `openai_text_tool_fallback`은 response tool call 복원에 쓰인다.
|
|
- OpenAI handlers emit `iop_openai_requests_total`, `iop_openai_usage_tokens_total`, `iop_openai_reasoning_observed_total`, `iop_openai_reasoning_chars_total`, and `iop_openai_reasoning_estimated_tokens_total`. Anthropic handlers currently do not emit these series.
|
|
- The request terminal uses `route_model`, `endpoint`, final `response_mode`, `status`, and `usage_source` with the stable caller labels. Provider token/reasoning series additionally use `usage_attribution`, strict actual `provider_id`, and actual `served_model` for each attempt.
|
|
- A request terminal is emitted exactly once. Each actual attempt is finalized exactly once by the attempt owner on graceful close or abort, so a provider switch records both the replaced and final providers without duplicating the request count.
|
|
- `usage_attribution="model_group"` is a query-time rollup instruction over canonical provider series grouped by `route_model`; it does not emit a duplicate model-group token counter.
|
|
- `usage_source="provider_reported"` requires provider token fields from at least one actual attempt. Reasoning characters alone may advance reasoning observation/estimate counters but leave the request source unavailable.
|
|
- `principal_ref`는 사용자/테넌트 참조값이고 `token_ref`는 앱/통합/용도별 token 참조값이다. 같은 principal에 여러 token이 있으면 `principal_ref` 기준 합산과 `token_ref` 기준 분해를 함께 사용할 수 있다.
|
|
- `node_id`, attempt/run/request/session ids, raw bearer token, provider token, and raw prompt/response content are not public metric labels. The node id remains internal attempt evidence only.
|
|
- Managed attempt labels may include `credential_slot_ref` and `credential_revision` from the verified lease scope. Slot alias, lease id, ciphertext, keys, auth header values, recipient data, and target URL are never public metric labels.
|
|
- For OpenAI passthrough, provider body usage takes precedence over tunnel `USAGE` values and proto-only reasoning/cached input may supplement it. The Anthropic native relay ignores tunnel `USAGE` frames.
|
|
|
|
## 검증
|
|
|
|
- `go test ./apps/edge/internal/openai`
|
|
- `go test -race -count=1 ./packages/go/streamgate ./apps/edge/internal/openai ./packages/go/config`
|
|
- `go test ./apps/edge/internal/service`
|
|
- `go test ./apps/edge/internal/openai -run 'Tunnel|UsageMetrics|ToolValidation|Dispatch|Reasoning|Retry'`
|
|
- `rg --fixed-strings "cloud_equivalent_cost" docs/openai-usage-grafana.md`
|
|
- `make test-openai-ollama`
|
|
- provider별 실제 runtime smoke는 환경별 test profile을 따른다.
|
|
|
|
## 한계와 주의사항
|
|
|
|
- normalized(non-provider) `/v1/responses`는 non-streaming string input만 지원한다. provider model group route의 `/v1/responses`는 raw passthrough로 streaming과 Codex/unknown field를 그대로 provider에 전달한다.
|
|
- Stream Evidence Gate 활성화만으로 반복, missing tool-call, schema 같은 semantic filter가 자동 활성화되지는 않는다. 해당 mechanics와 현재 지원 경로는 `agent-spec/runtime/stream-evidence-gate.md`를 따른다.
|
|
- A repeat-resume rebuild requires the request-start model catalog context window. Unknown or insufficient context fails before a replacement dispatch, preserving the recovery budget; it does not use a translator, local model, or `RecoveryPlanPreparer`.
|
|
- `/v1/completions`는 제공하지 않는다.
|
|
- OpenAI-compatible request에 provider/Ollama 전용 root field를 추가하지 않는다.
|
|
- workspace와 session 실행 제어를 request metadata 또는 prompt에 추가하지 않는다.
|
|
- pure `passthrough` body는 provider-original byte stream이며 IOP 확장 envelope나 normalized label을 포함하지 않는다.
|
|
- provider route와 non-provider normalized route의 차이는 selected provider capability에서 파생되며 caller metadata selector로 고르지 않는다.
|
|
- Grafana guide는 actual provider 기준 canonical query와 승인된 model-group rollup을 분리한다. request ledger, billing, chargeback은 이 구현 범위 밖이다.
|
|
- text tool-call synthesis는 요청 `tools[]` schema를 기준으로만 수행한다. 자연어 추론으로 tool call을 만들지 않는다.
|
|
- private token이나 endpoint 원문은 tracked spec/docs에 남기지 않는다.
|
|
- `metadata.user`는 identity source가 아니며 사용되지 않는다.
|
|
- caller가 `metadata.iop_principal_*`를 보내도 authenticated context 값이 overwrite한다.
|
|
- `openai.principal_tokens[]` 변경은 restart-required로 분류된다.
|
|
- principal token auth가 실패하면 legacy `openai.bearer_token`이 unmapped fallback으로 동작한다.
|
|
- The preceding legacy fallback applies only when the credential plane is disabled. Managed mode never returns to it after projection expiry or route/lease failure.
|
|
- provider가 별도 reasoning token을 보고하지 않으면 provider-reported `token_type="reasoning"`은 증가하지 않고, 별도 estimated token counter(`iop_openai_reasoning_estimated_tokens_total`, `estimation_method="chars_div_4"`)로 ceil(chars/4) 추정을 제공하되 billing-grade 확정값이 아니다.
|
|
- Grafana guide는 metric 조회와 operator-managed price baseline 예시이며 live cloud pricing, billing, chargeback, long-term ledger, 사용자별 제한 enforcement의 source of truth가 아니다.
|
|
- Seulgivibe Claude/OpenAI proxy는 별도 OpenAI-compatible provider family label로 보존될 수 있지만, HTTP body shape는 provider tunnel passthrough 경계를 따른다.
|
|
- Anthropic metrics are not inferred from native responses or tunnel frames; adding them requires a separate runtime change.
|
|
- Managed API-key profiles qualify end to end: the Control Plane canonicalizes the resolved auth header (for example lowercase `x-api-key` to `X-Api-Key`) before signing the lease scope, so lease issuance and consumption succeed and the Node injects only that exact header upstream. A lease failure fails closed with a sanitized provider-dispatch error and no Node/upstream call, never a fallback to a bearer slot or caller auth. This outbound provider-auth canonicalization is separate from inbound IOP `X-Api-Key`/Bearer caller-auth equivalence.
|
|
|
|
## 변경 기록
|
|
|
|
- 2026-07-07: 현재 코드와 OpenAI-compatible 계약 기준으로 bootstrap spec 작성.
|
|
- 2026-07-07: 기능 목록 중심으로 축소하고 주요 흐름을 Mermaid sequence diagram으로 정리.
|
|
- 2026-07-08: Chat Completions provider raw tunnel과 normalized execution semantics를 현재 코드와 계약 기준으로 반영.
|
|
- 2026-07-10: principal token 기반 usage metering, Prometheus metric, Grafana query guide, reasoning/cached token breakdown을 Milestone completion evidence 기준으로 반영.
|
|
- 2026-07-10: 일별 Usage 비용/ROI 리포트 MVP 종료 검토에서 daily/monthly rollup, usage origin breakdown, cloud-equivalent cost, avoided-cost ROI 문서 표면을 반영.
|
|
- 2026-07-11: provider model group `/v1/responses` raw passthrough 동작(모델 rewrite, unknown/Codex field 보존, streaming relay, provider auth forwarding)과 endpoint=`responses` usage metric label을 현재 코드 기준으로 반영.
|
|
- 2026-07-11: Responses provider route의 provider raw tunnel 동작을 반영했다.
|
|
- 2026-07-11: provider auth forwarding과 Seulgivibe OpenAI-compatible provider family surface를 종료 검토 기준으로 보강.
|
|
- 2026-07-12: Model Group Mixed Provider Dispatch 종료 검토 기준으로 selected provider capability 기반 passthrough/normalized 실행 경로를 반영.
|
|
- 2026-07-14: OpenAI-compatible Provider Passthrough 계약 동기화 종료 검토 기준으로 caller-facing response selector 설명을 제거하고, `model` 기반 route와 provider-native field 보존 기준을 반영.
|
|
- 2026-07-14: provider 미보고 reasoning text에 estimated-token counter(`iop_openai_reasoning_estimated_tokens_total`, `estimation_method="chars_div_4"`) 추가와 provider-reported reasoning 우선 기준을 반영.
|
|
- 2026-07-18: 저장소 구조 분해 뒤 streaming, provider tunnel, split test의 `source_evidence`를 현재 경로로 동기화.
|
|
- 2026-07-22: cross-model provider resource admission, provider-pool 공통 queue policy와 live candidate 소진 시 502 unavailable 의미를 현재 service/OpenAI 구현과 계약 기준으로 동기화.
|
|
- 2026-07-28: bounded ingress와 Stream Evidence Gate 활성 경로·한계·검증 포인터를 현재 구현 기준으로 반영.
|
|
- 2026-07-31: provider-default/model-group opt-in attribution policy, direct provider id precedence, actual Edge-local dispatch binding을 반영했다.
|
|
- 2026-07-31: Added request-local exactly-once terminal emission and actual-provider usage emission for every observed attempt, including recovery replacement and legacy tool-validation retry paths.
|
|
- 2026-07-31: Grafana query guide의 actual provider 집계와 승인된 model-group query-time rollup migration 완료 상태를 반영했다.
|
|
- 2026-08-01: Synchronized Anthropic ingress, provider-pool admission, usage boundaries, and Responses capability admission with the current handlers.
|
|
- 2026-08-02: Synchronized active managed projection auth, exact slot-route binding, lease acquisition/fencing, managed-versus-legacy credentials, safe slot/revision attribution, and the repaired managed API-key lease header canonicalization with source and deterministic two-profile qualification evidence.
|
|
- 2026-08-02: Removed IOP-owned workspace and Agent/CLI runtime semantics while preserving bounded metadata, managed projection, and credential lease behavior.
|