# 공통 규칙

> 모든 LMSP 엔드포인트가 공유하는 규칙. 버전 헤더와 확장, 멱등성 키, 오류 코드를 가진 problem+json 오류 형식, 상태 점검을 정의합니다.

Source: https://madeforagent.com/ko/lmsp/1.0/common-rules/
Language: ko
Specification: LMSP 1.0 (draft)
Updated: 2026-11-08

## 9.1 버전과 확장 {#versioning}

각 LMSP 요청은 다음 헤더를 실을 **수 있다**.

```http
LMSP-Version: 1.0
LMSP-Client: example-client/1.2.0
```

`LMSP-Version`은 클라이언트가 대상으로 하는 프로토콜 버전이다. `LMSP-Client`는 진단을 위해 클라이언트 소프트웨어를 `name/version` 형식으로 식별한다. 서비스는 이를 근거로 프로토콜 동작을 바꾸어서는 **안 된다**.

규칙:

- 경로의 `v1`은 주 버전을 뜻한다.
- 알 수 없는 주 버전을 지정한 요청에는 `426 Upgrade Required`와 오류 코드 `unsupported_protocol_version`으로 응답한다.
- 새로운 선택적 필드는 1.x 호환성을 깨뜨려서는 **안 된다**.
- 서비스와 클라이언트는 알지 못하는 필드를 무시해**야 한다**.
- 비공개 확장은 `extensions` 객체에 넣고 역도메인 표기로 이름을 짓는다.

```json
{
  "extensions": {
    "com.example.cache": {
      "enabled": true
    }
  }
}
```

## 9.2 멱등성 {#idempotency}

응답 생성, 작업 생성, 모델 로드처럼 무언가를 만드는 요청은 멱등성 키를 받는다.

```http
Idempotency-Key: <client-generated-unique-value>
```

서비스는 키를 요청 본문의 다이제스트 및 결과와 함께 저장한다.

- 같은 키에 같은 본문이면 원래 결과를 반환한다.
- 같은 키에 다른 본문이면 `409 Conflict`와 `idempotency_conflict`로 응답한다.

키의 범위는 클라이언트와 엔드포인트이다. 키는 ULID나 UUID처럼 최대 255자의 고유한 값인 **것이 좋다**. 서비스는 키를 최소 24시간 보관하는 **것이 좋다**.

서비스는 스트리밍 요청에서는 키를 무시할 **수 있다**. 안전한 재시도가 필요한 클라이언트는 [작업](https://madeforagent.com/ko/lmsp/1.0/tasks.md)을 사용한다.

## 9.3 오류 {#errors}

LMSP 인터페이스의 모든 오류는 `application/problem+json`([RFC 9457](https://www.rfc-editor.org/rfc/rfc9457))을 사용한다.

```json
{
  "type": "urn:lmsp:error:model-not-found",
  "title": "Model not found",
  "status": 404,
  "code": "model_not_found",
  "detail": "No local model with id 'qwen3:14b-q4_k_m'",
  "request_id": "req_01J8M9S6Y4W8C3P5R1V7Z2A0DH",
  "retryable": false
}
```

| 필드 | 필수 | 의미 |
| --- | --- | --- |
| `type` | 예 | 오류의 종류를 식별하는 URI. LMSP는 `urn:lmsp:error:` 뒤에 밑줄을 하이픈으로 바꾼 코드를 붙여 쓴다. |
| `title` | 예 | 사람이 읽을 수 있는 짧은 요약. |
| `status` | 예 | HTTP 상태 코드. |
| `code` | 예 | 아래 목록에 있는, 기계 판독 가능한 오류 코드. |
| `detail` | 아니요 | 이번 오류에 대한 사람이 읽을 수 있는 설명. 비밀 값, 프롬프트, 생성된 콘텐츠를 담아서는 **안 된다**. |
| `request_id` | 아니요 | 서비스 로그와 대조하기 위한 요청의 식별자. |
| `retryable` | 예 | 나중에 요청을 반복하면 성공할 수 있는지 여부. |

일반적인 오류 코드:

| 코드 | HTTP 상태 | 재시도 가능 | 의미 |
| --- | --- | --- | --- |
| `invalid_request` | 400 | 아니요 | 요청 형식이 잘못되었거나 매개변수가 올바르지 않다. |
| `unauthorized` | 401 | 아니요 | 토큰이 없거나, 올바르지 않거나, 만료되었거나, 폐기되었다. 또는 페어링 코드가 틀렸다. |
| `forbidden` | 403 | 아니요 | 토큰에 필요한 스코프가 없거나, `Origin` 또는 `Host` 검사에 실패했다. |
| `model_not_found` | 404 | 아니요 | 이 id의 로컬 모델이 없다. |
| `model_not_ready` | 409 | 예 | 모델이 로드되어 있지 않고 요청 시 로드되지도 않는다. |
| `model_load_failed` | 500 | 예 | 모델 로드에 실패했다. |
| `context_length_exceeded` | 400 | 아니요 | 입력이 모델의 컨텍스트 윈도우에 들어가지 않는다. |
| `unsupported_capability` | 400 또는 501 | 아니요 | 모델 또는 서비스가 요청에 필요한 것을 지원하지 않는다. 구현되지 않은 선택적 엔드포인트는 501로 응답한다. |
| `unsupported_protocol_version` | 426 | 아니요 | 서비스가 요청한 주 버전을 지원하지 않는다. |
| `response_not_found` | 404 | 아니요 | 이 id의 저장된 응답이 없다. |
| `task_not_found` | 404 | 아니요 | 이 id의 작업이 없다. |
| `task_not_cancelable` | 409 | 아니요 | 작업이 이미 성공했거나 실패했다. |
| `idempotency_conflict` | 409 | 아니요 | 그 멱등성 키가 이미 다른 요청 본문과 함께 쓰였다. |
| `event_history_expired` | 410 | 아니요 | 요청한 이벤트가 보존 기간보다 오래되었다. |
| `rate_limited` | 429 | 예 | 요청이 너무 많다. |
| `resource_exhausted` | 503 | 예 | 기기의 메모리나 다른 리소스가 부족하다. |
| `backend_unavailable` | 503 | 예 | 모델 런타임에 연결할 수 없다. |
| `server_restarted` | 503 | 예 | 작업이 끝나지 않은 상태에서 서비스가 재시작되었다. |
| `internal_error` | 500 | 경우에 따라 다름 | 서비스 내부의 예기치 못한 실패. |

`retryable` 필드가 기준이다. 위 열은 일반적인 값을 보여 준다. 서비스는 코드를 더 정의할 **수 있다**. 클라이언트는 알지 못하는 코드를 HTTP 상태와 `retryable` 필드로 처리해**야 한다**.

## 9.4 상태 점검 {#health}

```http
GET /lmsp/v1/health
```

```json
{
  "status": "ready",
  "service_id": "svc_01J8M4Y8K6M5E6K6Z7Z0Y8W3M2",
  "uptime_seconds": 86420,
  "loaded_models": 1,
  "active_tasks": 2,
  "storage": "ok"
}
```

| 필드 | 의미 |
| --- | --- |
| `status` | `starting`, `ready`, `degraded`, `stopping`. |
| `service_id` | 서비스 설명에 있는 서비스 id. |
| `uptime_seconds` | 서비스가 시작된 뒤 지난 초. |
| `loaded_models` | `ready` 상태인 모델의 수. |
| `active_tasks` | `queued`, `loading`, `running`, `cancelling` 상태인 작업의 수. |
| `storage` | `ok` 또는 `error`. |

`status`가 `ready` 또는 `degraded`이면 `200 OK`, 그렇지 않으면 `503 Service Unavailable`이다.

상태 점검 엔드포인트는 운영 상태만 반환한다. 모델 경로, 프롬프트, 액세스 토큰 또는 그 밖의 민감한 정보를 반환해서는 **안 된다**.
