콘텐츠로 이동

Admin REST API 레퍼런스

이 문서는 Continuum Router의 Admin REST API를 사용하여 설정 제어 애플리케이션을 구축하는 개발자를 위한 포괄적인 가이드를 제공합니다. 설정 관리 API를 사용하면 서버 재시작 없이 런타임에 설정을 보고, 수정하고, 관리할 수 있습니다.


개요

Admin REST API는 Continuum Router의 설정 시스템에 프로그래밍 방식으로 액세스할 수 있게 하며 다음을 지원합니다:

  • 실시간 설정 보기: 민감한 데이터가 자동으로 마스킹된 현재 설정 조회
  • 동적 설정 업데이트: 서버 재시작 없이 설정 섹션 수정
  • 설정 버전 관리: 전체 히스토리 및 롤백 기능으로 변경 사항 추적
  • 백엔드 관리: 백엔드를 동적으로 추가, 제거, 수정
  • 내보내기/가져오기: 여러 형식 (YAML, JSON, TOML)으로 설정 저장 및 복원

주요 기능

기능 설명
핫 리로드 섹션 타입에 따라 변경 사항이 즉시 또는 점진적으로 적용됨
민감 정보 마스킹 API 키, 비밀번호, 토큰이 응답에서 자동으로 마스킹됨
검증 dry-run 지원으로 적용 전 모든 변경 사항 검증
감사 로깅 보안 및 규정 준수를 위해 모든 수정 사항 로깅
히스토리 추적 롤백을 위해 최대 100개의 설정 버전 유지

인증

모든 Admin API 엔드포인트는 Admin Auth 시스템을 통한 인증이 필요합니다.

인증 방법

1. Bearer 토큰

Authorization: Bearer <admin-token>
curl -H "Authorization: Bearer your-admin-token" \
  http://localhost:8080/admin/config/full

2. Basic 인증

Authorization: Basic <base64(username:password)>
curl -u admin:password http://localhost:8080/admin/config/full

3. API 키 헤더

X-API-Key: <admin-api-key>
curl -H "X-API-Key: your-admin-key" http://localhost:8080/admin/config/full

설정

config.yaml에서 admin 인증을 설정합니다:

admin:
  auth:
    method: bearer_token  # 옵션: none, bearer_token, basic, api_key
    token: "${ADMIN_TOKEN}"  # 환경 변수 지원
    # Basic auth의 경우:
    # username: admin
    # password: "${ADMIN_PASSWORD}"

  # IP 화이트리스트 (선택 사항)
  ip_whitelist:
    - "127.0.0.1"
    - "10.0.0.0/8"

  # 설정 가능한 제한
  max_history_entries: 100
  max_backend_name_length: 256

기본 URL 및 헤더

기본 URL

http://localhost:8080/admin

일반 요청 헤더

Content-Type: application/json
Accept: application/json
Authorization: Bearer <token>

일반 응답 헤더

Content-Type: application/json
X-Request-Id: <unique-request-id>

설정 쿼리 API

전체 설정 가져오기

민감한 정보가 마스킹된 전체 설정을 조회합니다.

GET /admin/config/full

응답

{
  "config": {
    "server": {
      "bind_address": "0.0.0.0:8080",
      "workers": 4
    },
    "backends": [
      {
        "name": "openai",
        "url": "https://api.openai.com",
        "api_key": "sk-***abcd",
        "weight": 1
      }
    ],
    "logging": {
      "level": "info"
    },
    "rate_limiting": {
      "enabled": true,
      "requests_per_minute": 100
    }
  },
  "hot_reload_enabled": true,
  "last_modified": "2025-12-13T10:30:00Z"
}

예제

curl -s http://localhost:8080/admin/config/full \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq

설정 섹션 목록

핫 리로드 기능과 함께 사용 가능한 모든 설정 섹션을 가져옵니다.

GET /admin/config/sections

응답

{
  "sections": [
    {
      "name": "server",
      "description": "Server configuration including bind address and workers",
      "hot_reload_capability": "requires_restart"
    },
    {
      "name": "backends",
      "description": "Backend server configurations",
      "hot_reload_capability": "gradual"
    },
    {
      "name": "logging",
      "description": "Logging configuration",
      "hot_reload_capability": "immediate"
    },
    {
      "name": "rate_limiting",
      "description": "Rate limiting configuration",
      "hot_reload_capability": "immediate"
    },
    {
      "name": "circuit_breaker",
      "description": "Circuit breaker configuration",
      "hot_reload_capability": "immediate"
    },
    {
      "name": "retry",
      "description": "Retry policy configuration",
      "hot_reload_capability": "immediate"
    },
    {
      "name": "timeouts",
      "description": "Timeout configuration",
      "hot_reload_capability": "gradual"
    },
    {
      "name": "health_checks",
      "description": "Health check configuration",
      "hot_reload_capability": "gradual"
    },
    {
      "name": "global_prompts",
      "description": "Global prompt injection configuration",
      "hot_reload_capability": "immediate"
    },
    {
      "name": "fallback",
      "description": "Model fallback configuration",
      "hot_reload_capability": "gradual"
    },
    {
      "name": "files",
      "description": "Files API configuration",
      "hot_reload_capability": "gradual"
    },
    {
      "name": "api_keys",
      "description": "API keys configuration",
      "hot_reload_capability": "immediate"
    },
    {
      "name": "metrics",
      "description": "Metrics and monitoring configuration",
      "hot_reload_capability": "gradual"
    },
    {
      "name": "admin",
      "description": "Admin API configuration",
      "hot_reload_capability": "gradual"
    },
    {
      "name": "routing",
      "description": "Request routing configuration",
      "hot_reload_capability": "gradual"
    }
  ]
}

예제

curl -s http://localhost:8080/admin/config/sections \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq '.sections[].name'

섹션 설정 가져오기

특정 섹션의 설정을 조회합니다.

GET /admin/config/{section}

경로 파라미터

파라미터 타입 필수 설명
section string 섹션 이름 (위 목록 참조)

응답

{
  "section": "logging",
  "config": {
    "level": "info",
    "format": "json",
    "file": "/var/log/continuum-router.log"
  },
  "hot_reload_capability": "immediate",
  "description": "Logging configuration"
}

예제

# 로깅 설정 가져오기
curl -s http://localhost:8080/admin/config/logging \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq

# 백엔드 설정 가져오기
curl -s http://localhost:8080/admin/config/backends \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq

설정 스키마 가져오기

설정 검증을 위한 JSON Schema를 조회합니다.

GET /admin/config/schema

쿼리 파라미터

파라미터 타입 필수 설명
section string 아니오 특정 섹션에 대한 스키마만 가져오기

응답

{
  "schema": {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "properties": {
      "server": {
        "type": "object",
        "properties": {
          "bind_address": {
            "type": "string",
            "pattern": "^[^:]+:[0-9]+$",
            "description": "Server bind address in host:port format"
          },
          "workers": {
            "type": "integer",
            "minimum": 1,
            "description": "Number of worker threads"
          }
        }
      },
      "logging": {
        "type": "object",
        "properties": {
          "level": {
            "type": "string",
            "enum": ["trace", "debug", "info", "warn", "error"]
          }
        }
      }
    }
  }
}

예제

# 전체 스키마 가져오기
curl -s http://localhost:8080/admin/config/schema \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq

# 특정 섹션 스키마 가져오기
curl -s "http://localhost:8080/admin/config/schema?section=logging" \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq

설정 수정 API

섹션 설정 대체

전체 섹션 설정을 새 값으로 대체합니다.

PUT /admin/config/{section}

요청 본문

{
  "config": {
    "level": "debug",
    "format": "json"
  }
}

응답

{
  "success": true,
  "message": "Configuration updated successfully",
  "version": 5,
  "hot_reload_capability": "immediate",
  "applied": true,
  "warnings": []
}

예제

# 로깅 레벨을 debug로 업데이트
curl -X PUT http://localhost:8080/admin/config/logging \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "config": {
      "level": "debug"
    }
  }'

섹션 부분 업데이트

JSON 병합 패치 의미론을 사용하여 부분 업데이트를 적용합니다.

PATCH /admin/config/{section}

요청 본문

{
  "config": {
    "level": "warn"
  }
}

지정된 필드만 업데이트되고 다른 필드는 변경되지 않습니다.

응답

{
  "success": true,
  "message": "Configuration partially updated",
  "version": 6,
  "hot_reload_capability": "immediate",
  "applied": true,
  "merged_config": {
    "level": "warn",
    "format": "json",
    "file": "/var/log/continuum-router.log"
  }
}

예제

# 속도 제한 값만 업데이트
curl -X PATCH http://localhost:8080/admin/config/rate_limiting \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "config": {
      "requests_per_minute": 200
    }
  }'

설정 검증

변경 사항을 적용하지 않고 설정을 검증합니다.

POST /admin/config/validate

요청 본문

{
  "section": "server",
  "config": {
    "bind_address": "0.0.0.0:9090",
    "workers": 8
  },
  "dry_run": true
}

응답 (유효)

{
  "valid": true,
  "errors": [],
  "warnings": [
    {
      "field": "bind_address",
      "message": "Changing bind_address requires server restart"
    }
  ],
  "hot_reload_capability": "requires_restart"
}

응답 (유효하지 않음)

{
  "valid": false,
  "errors": [
    {
      "field": "workers",
      "message": "workers must be greater than 0",
      "code": "VALIDATION_ERROR"
    }
  ],
  "warnings": []
}

예제

# 적용 전 검증
curl -X POST http://localhost:8080/admin/config/validate \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "section": "rate_limiting",
    "config": {
      "enabled": true,
      "requests_per_minute": 500
    }
  }'

설정 적용

보류 중인 설정 변경 사항을 즉시 적용합니다 (핫 리로드 트리거).

POST /admin/config/apply

요청 본문

{
  "sections": ["logging", "rate_limiting"],
  "force": false
}
필드 타입 필수 설명
sections array 아니오 적용할 특정 섹션 (기본값: 모든 보류 중)
force boolean 아니오 경고가 있어도 강제 적용 (기본값: false)

응답

{
  "success": true,
  "applied_sections": ["logging", "rate_limiting"],
  "version": 7,
  "results": {
    "logging": {
      "status": "applied",
      "hot_reload_type": "immediate"
    },
    "rate_limiting": {
      "status": "applied",
      "hot_reload_type": "immediate"
    }
  }
}

예제

curl -X POST http://localhost:8080/admin/config/apply \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sections": ["logging"]
  }'

설정 저장/복원 API

설정 내보내기

지정된 형식으로 현재 설정을 내보냅니다.

POST /admin/config/export

요청 본문

{
  "format": "yaml",
  "sections": ["server", "backends", "logging"],
  "include_sensitive": false,
  "include_defaults": true
}
필드 타입 필수 설명
format string 출력 형식: yaml, json, 또는 toml
sections array 아니오 내보낼 섹션 (기본값: 전체)
include_sensitive boolean 아니오 마스킹 안 된 민감 데이터 포함 (기본값: false)
include_defaults boolean 아니오 기본값 포함 (기본값: true)

응답

{
  "format": "yaml",
  "content": "server:\n  bind_address: \"0.0.0.0:8080\"\n  workers: 4\n\nbackends:\n  - name: openai\n    url: https://api.openai.com\n    api_key: \"sk-***abcd\"\n",
  "exported_at": "2025-12-13T10:30:00Z",
  "sections_exported": ["server", "backends", "logging"]
}

예제

# YAML로 내보내기
curl -X POST http://localhost:8080/admin/config/export \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"format": "yaml"}' | jq -r '.content' > config-backup.yaml

# JSON으로 내보내기
curl -X POST http://localhost:8080/admin/config/export \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"format": "json"}' | jq -r '.content' > config-backup.json

# 특정 섹션 내보내기
curl -X POST http://localhost:8080/admin/config/export \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "format": "yaml",
    "sections": ["backends", "rate_limiting"]
  }'

설정 가져오기

콘텐츠에서 설정을 가져오고 적용합니다.

POST /admin/config/import

요청 본문

{
  "format": "yaml",
  "content": "logging:\n  level: info\n  format: json\n",
  "apply": true,
  "dry_run": false,
  "merge": true
}
필드 타입 필수 설명
format string 콘텐츠 형식: yaml, json, 또는 toml
content string 설정 콘텐츠 (최대 1MB)
apply boolean 아니오 검증 후 적용 (기본값: true)
dry_run boolean 아니오 적용 없이 검증만 (기본값: false)
merge boolean 아니오 기존 설정과 병합 (기본값: false)

응답

{
  "success": true,
  "message": "Configuration imported and applied",
  "version": 8,
  "validation": {
    "valid": true,
    "errors": [],
    "warnings": []
  },
  "sections_imported": ["logging"],
  "applied": true
}

예제

# 파일에서 가져오기
curl -X POST http://localhost:8080/admin/config/import \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"format\": \"yaml\",
    \"content\": $(cat config-backup.yaml | jq -Rs .),
    \"apply\": true
  }"

# 가져오기 미리보기 (dry run)
curl -X POST http://localhost:8080/admin/config/import \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "format": "yaml",
    "content": "logging:\n  level: debug\n",
    "dry_run": true
  }'

설정 히스토리 가져오기

설정 변경 히스토리를 확인합니다.

GET /admin/config/history

쿼리 파라미터

파라미터 타입 필수 설명
limit integer 아니오 반환할 항목 수 (기본값: 20, 최대: 100)
offset integer 아니오 건너뛸 항목 수 (기본값: 0)
section string 아니오 섹션 이름으로 필터링

응답

{
  "history": [
    {
      "version": 8,
      "timestamp": "2025-12-13T10:30:00Z",
      "sections_changed": ["logging"],
      "source": "api",
      "user": "admin",
      "description": "Updated logging level to debug",
      "rollback_available": true
    },
    {
      "version": 7,
      "timestamp": "2025-12-13T10:25:00Z",
      "sections_changed": ["rate_limiting"],
      "source": "api",
      "user": "admin",
      "description": "Increased rate limit to 200 rpm",
      "rollback_available": true
    },
    {
      "version": 6,
      "timestamp": "2025-12-13T09:00:00Z",
      "sections_changed": ["backends"],
      "source": "file_reload",
      "user": "system",
      "description": "Configuration file changed",
      "rollback_available": true
    }
  ],
  "total_entries": 8,
  "current_version": 8
}

예제

# 최근 히스토리 가져오기
curl -s http://localhost:8080/admin/config/history \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq

# 특정 섹션 히스토리 가져오기
curl -s "http://localhost:8080/admin/config/history?section=backends&limit=10" \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq

설정 롤백

이전 설정 버전으로 롤백합니다.

POST /admin/config/rollback/{version}

경로 파라미터

파라미터 타입 필수 설명
version integer 롤백할 버전 번호

요청 본문

{
  "sections": ["logging", "rate_limiting"],
  "dry_run": false
}
필드 타입 필수 설명
sections array 아니오 롤백할 특정 섹션 (기본값: 변경된 모든 섹션)
dry_run boolean 아니오 적용 없이 미리보기 (기본값: false)

응답

{
  "success": true,
  "message": "Rolled back to version 5",
  "previous_version": 8,
  "new_version": 9,
  "sections_rolled_back": ["logging", "rate_limiting"],
  "changes": {
    "logging": {
      "level": {
        "from": "debug",
        "to": "info"
      }
    }
  }
}

예제

# 버전 5로 롤백
curl -X POST http://localhost:8080/admin/config/rollback/5 \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

# 롤백 미리보기 (dry run)
curl -X POST http://localhost:8080/admin/config/rollback/5 \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"dry_run": true}'

백엔드 관리 API

백엔드 추가

새 백엔드를 동적으로 추가합니다.

POST /admin/backends

요청 본문

{
  "name": "new-ollama",
  "url": "http://192.168.1.100:11434",
  "weight": 1,
  "models": ["llama3.2", "mistral"],
  "api_key": "optional-key",
  "enabled": true,
  "health_check": {
    "enabled": true,
    "path": "/v1/models"
  }
}
필드 타입 필수 설명
name string 고유한 백엔드 이름 (영숫자, -, _)
type string 아니오 백엔드 타입: openai, azure, vllm, ollama, anthropic, gemini, llamacpp, generic. 기본값: generic (자동 감지)
url string 백엔드 URL (http:// 또는 https://)
weight integer 아니오 로드 밸런싱 가중치 (기본값: 1)
models array 아니오 이 백엔드가 제공하는 모델 목록
api_key string 아니오 백엔드 인증용 API 키
enabled boolean 아니오 백엔드 활성화 여부 (기본값: true)

백엔드 타입 자동 감지

type이 지정되지 않거나 generic으로 설정된 경우, 라우터는 백엔드의 /v1/models 엔드포인트를 자동으로 탐색하여 백엔드 타입을 감지합니다. 현재 자동 감지가 지원되는 백엔드:

  • llama.cpp: 응답의 owned_by: "llamacpp" 또는 llama.cpp 전용 메타데이터 필드로 식별

이를 통해 명시적인 타입 설정 없이도 llama.cpp 백엔드를 원활하게 통합할 수 있습니다:

# llama.cpp 백엔드 - 타입 자동 감지
curl -X POST http://localhost:8080/admin/backends \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "local-llama",
    "url": "http://localhost:8080"
  }'

응답

{
  "success": true,
  "message": "Backend 'new-ollama' added successfully",
  "backend": {
    "name": "new-ollama",
    "url": "http://192.168.1.100:11434",
    "weight": 1,
    "models": ["llama3.2", "mistral"],
    "enabled": true,
    "health_status": "unknown"
  }
}

예제

curl -X POST http://localhost:8080/admin/backends \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "new-backend",
    "url": "http://192.168.1.100:11434",
    "weight": 2,
    "models": ["llama3.2"]
  }'

백엔드 가져오기

특정 백엔드의 설정을 가져옵니다.

GET /admin/backends/{name}

응답

{
  "name": "openai",
  "url": "https://api.openai.com",
  "api_key": "sk-***abcd",
  "weight": 1,
  "models": ["gpt-4", "gpt-3.5-turbo"],
  "enabled": true,
  "health_status": "healthy",
  "stats": {
    "total_requests": 1250,
    "failed_requests": 12,
    "average_latency_ms": 150,
    "last_used": "2025-12-13T10:29:55Z"
  }
}

예제

curl -s http://localhost:8080/admin/backends/openai \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq

백엔드 업데이트

백엔드 설정을 업데이트합니다.

PUT /admin/backends/{name}

요청 본문

{
  "url": "https://api.openai.com",
  "weight": 2,
  "models": ["gpt-4", "gpt-4-turbo", "gpt-3.5-turbo"],
  "enabled": true
}

응답

{
  "success": true,
  "message": "Backend 'openai' updated successfully",
  "backend": {
    "name": "openai",
    "url": "https://api.openai.com",
    "weight": 2,
    "models": ["gpt-4", "gpt-4-turbo", "gpt-3.5-turbo"],
    "enabled": true
  }
}

예제

curl -X PUT http://localhost:8080/admin/backends/openai \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "weight": 3,
    "models": ["gpt-4", "gpt-4-turbo"]
  }'

백엔드 삭제

라우터에서 백엔드를 제거합니다.

DELETE /admin/backends/{name}

쿼리 파라미터

파라미터 타입 필수 설명
force boolean 아니오 활성 연결이 있어도 강제 삭제

응답

{
  "success": true,
  "message": "Backend 'old-backend' removed successfully",
  "removed_backend": "old-backend"
}

참고사항

  • 마지막 백엔드 삭제 허용: 라우터는 백엔드가 없는 상태로도 운영할 수 있습니다. 마지막 백엔드가 삭제되면:
    • /v1/models는 빈 목록을 반환
    • 라우팅 요청은 503 "No backends available" 반환
    • POST /admin/backends를 통해 새 백엔드 추가 가능

예제

curl -X DELETE http://localhost:8080/admin/backends/old-backend \
  -H "Authorization: Bearer $ADMIN_TOKEN"

# 강제 삭제
curl -X DELETE "http://localhost:8080/admin/backends/old-backend?force=true" \
  -H "Authorization: Bearer $ADMIN_TOKEN"

백엔드 가중치 업데이트

로드 밸런싱을 위한 백엔드 가중치만 업데이트합니다.

PUT /admin/backends/{name}/weight

요청 본문

{
  "weight": 5
}

응답

{
  "success": true,
  "message": "Backend 'openai' weight updated to 5",
  "previous_weight": 2,
  "new_weight": 5
}

예제

curl -X PUT http://localhost:8080/admin/backends/openai/weight \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"weight": 5}'

백엔드 모델 업데이트

백엔드의 모델 목록을 업데이트합니다.

PUT /admin/backends/{name}/models

요청 본문

{
  "models": ["gpt-4", "gpt-4-turbo", "gpt-4o", "gpt-3.5-turbo"],
  "append": false
}
필드 타입 필수 설명
models array 모델 이름 목록
append boolean 아니오 기존 목록에 추가 (기본값: false, 대체)

응답

{
  "success": true,
  "message": "Backend 'openai' models updated",
  "models": ["gpt-4", "gpt-4-turbo", "gpt-4o", "gpt-3.5-turbo"]
}

예제

# 모델 대체
curl -X PUT http://localhost:8080/admin/backends/openai/models \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"models": ["gpt-4", "gpt-4o"]}'

# 모델 추가
curl -X PUT http://localhost:8080/admin/backends/openai/models \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"models": ["gpt-4.5-turbo"], "append": true}'

데이터 모델

설정 섹션

섹션 설명 핫 리로드
server 바인드 주소, 워커, 연결 풀 재시작 필요
backends 백엔드 URL, 가중치, 모델 점진적
health_checks 간격, 임계값 점진적
logging 로그 레벨, 형식, 출력 즉시
retry 최대 시도, 지연, 백오프 즉시
timeouts 연결, 요청, 유휴 타임아웃 점진적
rate_limiting 제한, 스토리지, 화이트리스트 즉시
circuit_breaker 임계값, 복구 시간 즉시
global_prompts 시스템 프롬프트 주입 즉시
fallback 폴백 체인, 정책 점진적
files Files API 설정 점진적
api_keys API 키 설정 즉시
metrics Prometheus, 레이블 점진적
admin Admin API 설정 점진적
routing 모델 라우팅 규칙 점진적

백엔드 객체

{
  "name": "string",
  "url": "string (http:// 또는 https://)",
  "api_key": "string (선택 사항, 응답에서 마스킹)",
  "weight": "integer (1-100)",
  "models": ["string"],
  "enabled": "boolean",
  "health_check": {
    "enabled": "boolean",
    "path": "string",
    "interval": "string (duration)"
  }
}

히스토리 항목 객체

{
  "version": "integer",
  "timestamp": "string (ISO 8601)",
  "sections_changed": ["string"],
  "source": "string (api|file_reload|initial|rollback)",
  "user": "string",
  "description": "string (선택 사항)",
  "rollback_available": "boolean"
}

검증 결과 객체

{
  "valid": "boolean",
  "errors": [
    {
      "field": "string",
      "message": "string",
      "code": "string"
    }
  ],
  "warnings": [
    {
      "field": "string",
      "message": "string"
    }
  ]
}

핫 리로드 동작

업데이트 타입

타입 동작 섹션
즉시 즉시 적용, 중단 없음 logging, ratelimiting, circuitbreaker, retry, globalprompts, apikeys
점진적 기존 연결 유지, 새 연결이 새 설정 사용 backends, health_checks, timeouts, fallback, files, metrics, admin, routing
재시작 필요 경고로 로깅, 서버 재시작 필요 server.bind_address, server.workers

예제 워크플로우

# 1. 현재 설정 확인
curl -s http://localhost:8080/admin/config/logging | jq

# 2. 변경 검증
curl -X POST http://localhost:8080/admin/config/validate \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"section": "logging", "config": {"level": "debug"}}'

# 3. 변경 적용 (즉시 효과)
curl -X PATCH http://localhost:8080/admin/config/logging \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"config": {"level": "debug"}}'

# 4. 변경 확인
curl -s http://localhost:8080/admin/config/logging | jq '.config.level'

오류 처리

오류 응답 형식

{
  "error_code": "string",
  "message": "string",
  "details": {}
}

오류 코드

코드 HTTP 상태 설명
VALIDATION_ERROR 400 설정 검증 실패
INVALID_SECTION 400 알 수 없는 설정 섹션
PARSE_ERROR 400 설정 콘텐츠 파싱 실패
SECTION_NOT_FOUND 404 섹션을 찾을 수 없음
VERSION_NOT_FOUND 404 히스토리 버전을 찾을 수 없음
BACKEND_NOT_FOUND 404 백엔드를 찾을 수 없음
BACKEND_EXISTS 409 해당 이름의 백엔드가 이미 존재함
CONTENT_TOO_LARGE 413 설정 콘텐츠가 1MB 제한 초과
INTERNAL_ERROR 500 내부 서버 오류

오류 예제

// 검증 오류
{
  "error_code": "VALIDATION_ERROR",
  "message": "Configuration validation failed",
  "details": {
    "errors": [
      {"field": "workers", "message": "workers must be greater than 0"}
    ]
  }
}

// 섹션을 찾을 수 없음
{
  "error_code": "SECTION_NOT_FOUND",
  "message": "Configuration section 'invalid' not found",
  "details": {
    "available_sections": ["server", "backends", "logging", "..."]
  }
}

// 백엔드가 이미 존재함
{
  "error_code": "BACKEND_EXISTS",
  "message": "Backend 'openai' already exists",
  "details": {
    "existing_backend": "openai"
  }
}

클라이언트 SDK 예제

Python

import requests
from typing import Optional, Dict, Any, List
from dataclasses import dataclass


@dataclass
class ContinuumAdminClient:
    """Continuum Router Admin API 클라이언트"""

    base_url: str
    token: str

    def __post_init__(self):
        self.session = requests.Session()
        self.session.headers.update({
            "Authorization": f"Bearer {self.token}",
            "Content-Type": "application/json"
        })

    # 설정 쿼리 API

    def get_full_config(self) -> Dict[str, Any]:
        """마스킹된 민감 데이터가 포함된 전체 설정 가져오기"""
        resp = self.session.get(f"{self.base_url}/admin/config/full")
        resp.raise_for_status()
        return resp.json()

    def get_sections(self) -> List[Dict[str, Any]]:
        """모든 설정 섹션 가져오기"""
        resp = self.session.get(f"{self.base_url}/admin/config/sections")
        resp.raise_for_status()
        return resp.json()["sections"]

    def get_section(self, section: str) -> Dict[str, Any]:
        """특정 섹션의 설정 가져오기"""
        resp = self.session.get(f"{self.base_url}/admin/config/{section}")
        resp.raise_for_status()
        return resp.json()

    def get_schema(self, section: Optional[str] = None) -> Dict[str, Any]:
        """검증을 위한 JSON 스키마 가져오기"""
        params = {"section": section} if section else {}
        resp = self.session.get(
            f"{self.base_url}/admin/config/schema",
            params=params
        )
        resp.raise_for_status()
        return resp.json()

    # 설정 수정 API

    def update_section(self, section: str, config: Dict[str, Any]) -> Dict[str, Any]:
        """섹션 설정 대체"""
        resp = self.session.put(
            f"{self.base_url}/admin/config/{section}",
            json={"config": config}
        )
        resp.raise_for_status()
        return resp.json()

    def patch_section(self, section: str, config: Dict[str, Any]) -> Dict[str, Any]:
        """섹션 설정 부분 업데이트"""
        resp = self.session.patch(
            f"{self.base_url}/admin/config/{section}",
            json={"config": config}
        )
        resp.raise_for_status()
        return resp.json()

    def validate_config(
        self,
        section: str,
        config: Dict[str, Any],
        dry_run: bool = True
    ) -> Dict[str, Any]:
        """적용 없이 설정 검증"""
        resp = self.session.post(
            f"{self.base_url}/admin/config/validate",
            json={"section": section, "config": config, "dry_run": dry_run}
        )
        resp.raise_for_status()
        return resp.json()

    def apply_config(
        self,
        sections: Optional[List[str]] = None,
        force: bool = False
    ) -> Dict[str, Any]:
        """보류 중인 설정 변경 적용"""
        body = {"force": force}
        if sections:
            body["sections"] = sections
        resp = self.session.post(
            f"{self.base_url}/admin/config/apply",
            json=body
        )
        resp.raise_for_status()
        return resp.json()

    # 설정 저장/복원 API

    def export_config(
        self,
        format: str = "yaml",
        sections: Optional[List[str]] = None,
        include_sensitive: bool = False
    ) -> str:
        """지정된 형식으로 설정 내보내기"""
        body = {"format": format, "include_sensitive": include_sensitive}
        if sections:
            body["sections"] = sections
        resp = self.session.post(
            f"{self.base_url}/admin/config/export",
            json=body
        )
        resp.raise_for_status()
        return resp.json()["content"]

    def import_config(
        self,
        content: str,
        format: str = "yaml",
        apply: bool = True,
        dry_run: bool = False
    ) -> Dict[str, Any]:
        """콘텐츠에서 설정 가져오기"""
        resp = self.session.post(
            f"{self.base_url}/admin/config/import",
            json={
                "format": format,
                "content": content,
                "apply": apply,
                "dry_run": dry_run
            }
        )
        resp.raise_for_status()
        return resp.json()

    def get_history(
        self,
        limit: int = 20,
        offset: int = 0,
        section: Optional[str] = None
    ) -> Dict[str, Any]:
        """설정 변경 히스토리 가져오기"""
        params = {"limit": limit, "offset": offset}
        if section:
            params["section"] = section
        resp = self.session.get(
            f"{self.base_url}/admin/config/history",
            params=params
        )
        resp.raise_for_status()
        return resp.json()

    def rollback(
        self,
        version: int,
        sections: Optional[List[str]] = None,
        dry_run: bool = False
    ) -> Dict[str, Any]:
        """이전 버전으로 롤백"""
        body = {"dry_run": dry_run}
        if sections:
            body["sections"] = sections
        resp = self.session.post(
            f"{self.base_url}/admin/config/rollback/{version}",
            json=body
        )
        resp.raise_for_status()
        return resp.json()

    # 백엔드 관리 API

    def list_backends(self) -> List[Dict[str, Any]]:
        """모든 백엔드 목록"""
        resp = self.session.get(f"{self.base_url}/admin/backends")
        resp.raise_for_status()
        return resp.json()["backends"]

    def get_backend(self, name: str) -> Dict[str, Any]:
        """백엔드 설정 가져오기"""
        resp = self.session.get(f"{self.base_url}/admin/backends/{name}")
        resp.raise_for_status()
        return resp.json()

    def add_backend(
        self,
        name: str,
        url: str,
        weight: int = 1,
        models: Optional[List[str]] = None
    ) -> Dict[str, Any]:
        """새 백엔드 추가"""
        body = {"name": name, "url": url, "weight": weight}
        if models:
            body["models"] = models
        resp = self.session.post(
            f"{self.base_url}/admin/backends",
            json=body
        )
        resp.raise_for_status()
        return resp.json()

    def update_backend(self, name: str, **kwargs) -> Dict[str, Any]:
        """백엔드 설정 업데이트"""
        resp = self.session.put(
            f"{self.base_url}/admin/backends/{name}",
            json=kwargs
        )
        resp.raise_for_status()
        return resp.json()

    def delete_backend(self, name: str, force: bool = False) -> Dict[str, Any]:
        """백엔드 삭제"""
        params = {"force": str(force).lower()} if force else {}
        resp = self.session.delete(
            f"{self.base_url}/admin/backends/{name}",
            params=params
        )
        resp.raise_for_status()
        return resp.json()

    def update_backend_weight(self, name: str, weight: int) -> Dict[str, Any]:
        """백엔드 가중치 업데이트"""
        resp = self.session.put(
            f"{self.base_url}/admin/backends/{name}/weight",
            json={"weight": weight}
        )
        resp.raise_for_status()
        return resp.json()

    def update_backend_models(
        self,
        name: str,
        models: List[str],
        append: bool = False
    ) -> Dict[str, Any]:
        """백엔드 모델 업데이트"""
        resp = self.session.put(
            f"{self.base_url}/admin/backends/{name}/models",
            json={"models": models, "append": append}
        )
        resp.raise_for_status()
        return resp.json()


# 사용 예제
if __name__ == "__main__":
    client = ContinuumAdminClient(
        base_url="http://localhost:8080",
        token="your-admin-token"
    )

    # 현재 로깅 설정 가져오기
    logging_config = client.get_section("logging")
    print(f"현재 로그 레벨: {logging_config['config']['level']}")

    # 로깅 레벨 업데이트
    result = client.patch_section("logging", {"level": "debug"})
    print(f"업데이트됨: {result['success']}")

    # 새 백엔드 추가
    client.add_backend(
        name="new-ollama",
        url="http://192.168.1.100:11434",
        weight=2,
        models=["llama3.2", "mistral"]
    )

    # 설정 백업 내보내기
    backup = client.export_config(format="yaml")
    with open("config-backup.yaml", "w") as f:
        f.write(backup)

모범 사례

1. 적용 전 항상 검증

# 1단계: 검증
curl -X POST http://localhost:8080/admin/config/validate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"section": "logging", "config": {"level": "debug"}}'

# 2단계: 유효한 경우에만 적용
curl -X PATCH http://localhost:8080/admin/config/logging \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"config": {"level": "debug"}}'

2. 가져오기에 Dry Run 사용

# 가져오기 변경 미리보기
curl -X POST http://localhost:8080/admin/config/import \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "format": "yaml",
    "content": "...",
    "dry_run": true
  }'

3. 정기적인 설정 백업

# 일일 백업 스크립트
#!/bin/bash
DATE=$(date +%Y%m%d)
curl -s -X POST http://localhost:8080/admin/config/export \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"format": "yaml"}' | jq -r '.content' > "config-backup-$DATE.yaml"

4. 설정 히스토리 모니터링

# 최근 변경 확인
curl -s http://localhost:8080/admin/config/history?limit=5 \
  -H "Authorization: Bearer $TOKEN" | jq '.history[] | {version, timestamp, sections_changed}'

5. 최소 변경에 부분 업데이트 (PATCH) 사용

# 필요한 것만 업데이트
curl -X PATCH http://localhost:8080/admin/config/rate_limiting \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"config": {"requests_per_minute": 200}}'

6. 프로덕션 전 스테이징에서 설정 변경 테스트

# 예제: 프로덕션 전 스테이징에서 설정 테스트
staging_client = ContinuumAdminClient("http://staging:8080", staging_token)
production_client = ContinuumAdminClient("http://production:8080", prod_token)

# 먼저 스테이징에 적용
staging_client.patch_section("rate_limiting", {"requests_per_minute": 500})

# 스테이징에서 확인
staging_config = staging_client.get_section("rate_limiting")
assert staging_config["config"]["requests_per_minute"] == 500

# 그 다음 프로덕션에 적용
production_client.patch_section("rate_limiting", {"requests_per_minute": 500})

보안 고려 사항

1. 민감 데이터 처리

  • 모든 API 응답은 민감한 필드 (API 키, 비밀번호, 토큰)를 자동으로 마스킹합니다
  • include_sensitive: true는 절대적으로 필요한 경우에만 export에서 사용하세요
  • 감사 로그는 민감 데이터 액세스 시 기록합니다

2. 인증 모범 사례

admin:
  auth:
    method: bearer_token
    token: "${ADMIN_TOKEN}"  # 환경 변수 사용

  # IP로 액세스 제한
  ip_whitelist:
    - "10.0.0.0/8"      # 내부 네트워크만
    - "192.168.1.0/24"  # 사무실 네트워크

3. 감사 로깅

모든 설정 변경은 다음과 함께 로깅됩니다:

  • 타임스탬프
  • 사용자/소스
  • 변경된 섹션
  • 이전 및 새 값 (민감 데이터 마스킹)

4. Admin 엔드포인트 속도 제한

남용 방지를 위해 admin 엔드포인트 속도 제한을 고려하세요:

admin:
  rate_limit:
    requests_per_minute: 60
    burst: 10

5. 주요 변경 전 백업

# 주요 변경 전 항상 백업
backup=$(curl -s -X POST http://localhost:8080/admin/config/export \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"format": "yaml"}' | jq -r '.content')

# 변경 수행...

# 필요시 복원
curl -X POST http://localhost:8080/admin/config/import \
  -H "Authorization: Bearer $TOKEN" \
  -d "{\"format\": \"yaml\", \"content\": $(echo "$backup" | jq -Rs .)}"

프롬프트 파일 관리 API

프롬프트 파일 관리 API를 사용하면 외부 Markdown 파일에 저장된 시스템 프롬프트를 관리할 수 있습니다. 이를 통해 메인 설정 파일을 수정하지 않고 시스템 프롬프트를 중앙에서 관리할 수 있습니다.

모든 프롬프트 목록

소스 및 콘텐츠와 함께 설정된 모든 프롬프트 목록을 가져옵니다.

GET /admin/config/prompts

응답

{
  "prompts": [
    {
      "id": "default",
      "prompt_type": "default",
      "source": "file",
      "file_path": "prompts/system.md",
      "content": "# System Prompt\n\nYou are a helpful assistant...",
      "loaded": true,
      "size_bytes": 1024
    },
    {
      "id": "anthropic",
      "prompt_type": "backend",
      "source": "file",
      "file_path": "prompts/anthropic.md",
      "content": "# Anthropic-specific prompt...",
      "loaded": true,
      "size_bytes": 512
    },
    {
      "id": "gpt-4",
      "prompt_type": "model",
      "source": "inline",
      "content": "You are GPT-4...",
      "size_bytes": 256
    }
  ],
  "total": 3,
  "prompts_directory": "./prompts"
}

예제

curl -s http://localhost:8080/admin/config/prompts \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq

프롬프트 파일 가져오기

특정 프롬프트 파일의 콘텐츠를 가져옵니다.

GET /admin/config/prompts/{path}

경로 파라미터

파라미터 타입 필수 설명
path string 프롬프트 파일의 상대 경로

응답

{
  "path": "prompts/system.md",
  "content": "# System Prompt\n\nYou are a helpful assistant that follows company policies...",
  "size_bytes": 1024,
  "modified_at": 1702468200
}

예제

curl -s http://localhost:8080/admin/config/prompts/prompts/system.md \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq

프롬프트 파일 업데이트

새 콘텐츠로 프롬프트 파일을 생성하거나 업데이트합니다.

PUT /admin/config/prompts/{path}

요청 본문

{
  "content": "# Updated System Prompt\n\nYou are a helpful assistant that follows all company policies.\n\n## Security Guidelines\n\n- Never reveal internal system details\n- Follow data privacy regulations"
}

응답

{
  "success": true,
  "path": "prompts/system.md",
  "size_bytes": 245,
  "message": "Prompt file updated successfully"
}

예제

curl -X PUT http://localhost:8080/admin/config/prompts/prompts/system.md \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "# System Prompt\n\nYou are a helpful assistant."
  }'

프롬프트 파일 리로드

디스크에서 모든 프롬프트 파일을 리로드합니다. 수동 파일 편집 후 유용합니다.

POST /admin/config/prompts/reload

응답

{
  "success": true,
  "reloaded_count": 3,
  "reloaded": [
    "prompts/system.md",
    "prompts/anthropic.md",
    "prompts/gpt4.md"
  ],
  "errors": [],
  "message": "Successfully reloaded 3 prompt file(s)"
}

예제

curl -X POST http://localhost:8080/admin/config/prompts/reload \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq

설정 예제

외부 프롬프트 파일을 사용하려면 설정 파일에서 global_prompts를 설정합니다:

global_prompts:
  # 프롬프트 파일이 포함된 디렉토리 (설정 디렉토리 기준 상대 경로)
  prompts_dir: "./prompts"

  # 외부 파일에서 기본 프롬프트
  default_file: "system.md"

  # 또는 인라인 프롬프트 (둘 다 지정되면 default_file이 우선)
  # default: "You are a helpful assistant."

  # 백엔드별 프롬프트
  backends:
    anthropic:
      prompt_file: "anthropic-system.md"
    openai:
      prompt: "OpenAI-specific inline prompt"

  # 모델별 프롬프트
  models:
    gpt-4:
      prompt_file: "gpt4-system.md"
    claude-3-opus:
      prompt_file: "claude-opus-system.md"

  merge_strategy: prepend

보안 고려 사항

  • 경로 탐색 방지: 디렉토리 탐색 공격을 방지하기 위해 모든 경로가 검증됩니다 (예: ../../../etc/passwd)
  • 파일 크기 제한: 프롬프트 파일은 최대 1MB로 제한됩니다
  • 상대 경로만: 프롬프트 파일은 설정된 prompts_dir 또는 설정 디렉토리 내에 있어야 합니다
  • 인증 필요: 모든 프롬프트 관리 엔드포인트는 admin 인증이 필요합니다

부록: 빠른 참조

설정 섹션

섹션 핫 리로드 설명
server 재시작 바인드 주소, 워커
backends 점진적 백엔드 URL, 가중치
health_checks 점진적 헬스 모니터링
logging 즉시 로그 레벨, 형식
retry 즉시 재시도 정책
timeouts 점진적 요청 타임아웃
rate_limiting 즉시 속도 제한
circuit_breaker 즉시 서킷 브레이커
global_prompts 즉시 시스템 프롬프트
fallback 점진적 모델 폴백
files 점진적 Files API
api_keys 즉시 API 키
metrics 점진적 Prometheus 메트릭
admin 점진적 Admin 설정
routing 점진적 라우팅 규칙

HTTP 상태 코드

코드 의미
200 성공
400 Bad Request (검증 오류)
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict
413 Payload Too Large
500 Internal Server Error

일반적인 curl 명령어

# 전체 설정 가져오기
curl -s http://localhost:8080/admin/config/full -H "Authorization: Bearer $TOKEN"

# 로깅 레벨 업데이트
curl -X PATCH http://localhost:8080/admin/config/logging \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"config": {"level": "debug"}}'

# 백엔드 추가
curl -X POST http://localhost:8080/admin/backends \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"name": "new", "url": "http://host:port", "weight": 1}'

# 설정 내보내기
curl -X POST http://localhost:8080/admin/config/export \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"format": "yaml"}'

# 히스토리 보기
curl -s http://localhost:8080/admin/config/history -H "Authorization: Bearer $TOKEN"

# 롤백
curl -X POST http://localhost:8080/admin/config/rollback/5 \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{}'