콘텐츠로 이동

라이브러리 사용법

Continuum Router는 독립 실행형 CLI 바이너리 외에도 임베딩 가능한 Rust 라이브러리 크레이트로 쓸 수 있습니다. 별도 프로세스를 띄우지 않고 LLM 라우팅을 Rust 애플리케이션 안에 직접 넣을 수 있습니다.

사용 사례

  • 기존 애플리케이션에 임베딩: Axum/Tokio 애플리케이션에 LLM 라우팅 통합
  • 프로그래밍 방식 설정: YAML 파일 없이 Rust 코드로 라우터 설정
  • 커스텀 확장: 라우터 주변에 커스텀 미들웨어, 라우트 추가 또는 동작 수정
  • 통합 테스트: 테스트 스위트를 위한 라우터 인스턴스 프로그래밍 방식 생성

의존성 추가

Cargo.tomlcontinuum-router를 라이브러리 의존성으로 추가합니다:

[dependencies]
continuum-router = { git = "https://github.com/lablup/continuum-router.git" }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
axum = "0.7"

컴파일 시간과 바이너리 크기를 줄이려면, 기본 기능을 비활성화하고 필요한 것만 선택할 수 있습니다:

[dependencies]
continuum-router = { git = "https://github.com/lablup/continuum-router.git", default-features = false, features = ["metrics", "hot-reload"] }

사용 가능한 전체 기능 목록은 Cargo 기능 플래그를 참조하세요.

빠른 시작

설정 파일로 독립 실행 서버

라이브러리를 사용하는 가장 간단한 방법은 YAML 설정 파일을 로드하고 서버를 실행하는 것입니다:

use continuum_router::ContinuumRouter;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 트레이싱 초기화 (선택사항 - 라이브러리는 트레이싱을 초기화하지 않음)
    tracing_subscriber::fmt::init();

    let router = ContinuumRouter::from_config_file("config.yaml")
        .await?
        .enable_hot_reload(true)
        .build()
        .await?;

    router.serve("0.0.0.0:8080").await?;
    Ok(())
}

프로그래밍 방식 설정으로 독립 실행 서버

코드에서 라우터를 완전히 설정할 수도 있습니다:

use continuum_router::{ContinuumRouter, Config};
use continuum_router::config::{BackendConfig, BackendTypeConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    tracing_subscriber::fmt::init();

    let mut config = Config::default();
    config.backends = vec![
        BackendConfig {
            name: "openai".to_string(),
            url: "https://api.openai.com/v1".to_string(),
            backend_type: BackendTypeConfig::Openai,
            api_key: std::env::var("OPENAI_API_KEY").ok(),
            models: vec!["gpt-4o".to_string()],
            ..BackendConfig::default()
        },
        BackendConfig {
            name: "ollama".to_string(),
            url: "http://localhost:11434".to_string(),
            backend_type: BackendTypeConfig::Ollama,
            models: vec![],  // 자동 검색
            ..BackendConfig::default()
        },
    ];

    let router = ContinuumRouter::from_config(config)
        .enable_health_checks(true)
        .enable_circuit_breaker(true)
        .build()
        .await?;

    router.serve("0.0.0.0:8080").await?;
    Ok(())
}

타입 안전 설정 빌더

BackendConfig 구조체를 수동으로 구성하는 대신, 타입 안전 빌더 API를 사용하면 더 깔끔합니다. 빌더는 빌드 시점에 모든 설정을 검증하고 프로바이더별 생성자를 제공합니다:

use continuum_router::config::builder::{BackendConfigBuilder, ConfigBuilder};
use continuum_router::ContinuumRouter;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    tracing_subscriber::fmt::init();

    let openai = BackendConfigBuilder::openai("https://api.openai.com/v1", "sk-...")
        .name("primary-openai")
        .models(vec!["gpt-4o", "gpt-4o-mini"])
        .build()?;

    let ollama = BackendConfigBuilder::ollama("http://localhost:11434")
        .build()?;

    let config = ConfigBuilder::new()
        .add_backend(openai)
        .add_backend(ollama)
        .bind_address("0.0.0.0:8080")
        .enable_health_checks(true)
        .logging_level("info")
        .build()?;

    let router = ContinuumRouter::from_config(config)
        .build()
        .await?;

    router.serve("0.0.0.0:8080").await?;
    Ok(())
}

모든 빌더 메서드, 프로바이더 생성자, 오류 타입에 대한 전체 레퍼런스는 Rust Builder API를 참조하세요.

기존 Axum 애플리케이션에 임베딩

into_router()를 사용하여 기존 애플리케이션에 중첩할 수 있는 Axum Router를 가져옵니다:

use axum::{routing::get, Router};
use continuum_router::ContinuumRouter;

async fn custom_handler() -> &'static str {
    "Custom endpoint"
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    tracing_subscriber::fmt::init();

    // Continuum Router 빌드
    let llm_router = ContinuumRouter::from_config_file("config.yaml")
        .await?
        .build()
        .await?;

    // /llm 하위에 중첩하고 커스텀 라우트와 함께 사용
    let app = Router::new()
        .route("/custom", get(custom_handler))
        .nest("/llm", llm_router.into_router());

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
    axum::serve(listener, app).await?;
    Ok(())
}

임베딩 시 라우터의 엔드포인트는 중첩 프리픽스 하위에서 사용할 수 있습니다. 예: /llm/v1/chat/completions, /llm/v1/models, /llm/admin/backends 등.

빌더 API 레퍼런스

빌더는 ContinuumRouter::from_config() 또는 ContinuumRouter::from_config_file()로 생성하고, .build().await 호출 전에 메서드 체인으로 설정합니다.

진입점

ContinuumRouter::from_config(config: Config) -> ContinuumRouterBuilder

프로그래밍 방식의 Config 구조체에서 빌더를 생성합니다. with_config_manager()ConfigManager를 수동으로 연결하지 않는 한 파일 감시 및 핫 리로드를 사용할 수 없습니다.

ContinuumRouter::from_config_file(path: &str) -> Result<ContinuumRouterBuilder, RouterError>

YAML/TOML 파일에서 설정을 로드하여 빌더를 생성합니다. 내부적으로 ConfigManager를 생성하며, 활성화 시 핫 리로드를 지원합니다. 프롬프트 파일 해석을 위한 설정 디렉터리는 파일 경로에서 자동으로 유도됩니다.

빌더 메서드

모든 빌더 메서드는 체이닝을 위해 Self를 반환합니다.

메서드 설명 기본값
enable_hot_reload(bool) 설정 파일 감시 및 자동 리로드 활성화/비활성화 설정에 따름
enable_health_checks(bool) 백엔드 백그라운드 헬스 모니터링 활성화/비활성화 활성화
enable_circuit_breaker(bool) 서킷 브레이커 상태 머신 활성화/비활성화 설정에 따름
enable_metrics(bool) Prometheus 메트릭 수집 활성화/비활성화 설정에 따름
enable_files_api(bool) /v1/files 업로드 API 활성화/비활성화 설정에 따름
with_http_client(client) 사전 설정된 reqwest::Client 주입 자동 생성
with_config_dir(path) 프롬프트 파일 해석을 위한 기본 디렉터리 설정 설정 파일 경로에서 유도
with_config_manager(manager) 외부에서 생성한 ConfigManager 연결 None

오버라이드 규칙

빌더 메서드를 호출하지 않으면, 설정 파일 또는 Config::default()에 지정된 값을 따릅니다. 메서드를 호출하면, 설정 소스에 관계없이 명시적 값이 우선합니다.

ContinuumRouter 메서드

빌드 후 다음 메서드를 가진 ContinuumRouter 인스턴스를 얻습니다:

메서드 설명
serve(addr: &str) 독립 실행 서버로 실행; 종료 신호(SIGINT/SIGTERM)까지 블로킹
into_router() -> Router 다른 애플리케이션에 임베딩하기 위한 Axum Router 반환
state() -> Arc<AppState> 공유 애플리케이션 상태 접근
config_handle() -> Option<&ConfigManager> ConfigManager 접근 (파일에서 로드한 경우에만)
shutdown() 라우터를 소비하고 드롭하여 모든 리소스 해제

라이브러리 모드에서의 핫 리로드

Continuum Router의 핫 리로드 인프라는 CLI 바이너리와 동일하게 라이브러리 모드에서도 작동합니다.

파일 기반 핫 리로드

from_config_file()에 핫 리로드를 활성화하면, 라우터가 설정 파일의 변경을 감시하고 자동으로 업데이트를 적용합니다:

let router = ContinuumRouter::from_config_file("config.yaml")
    .await?
    .enable_hot_reload(true)
    .build()
    .await?;

config.yaml의 변경 사항은 파일시스템 알림으로 감지되어 모든 컴포넌트(백엔드, 헬스 체커, 서킷 브레이커, 레이트 리미터, 모델 서비스)에 전파됩니다.

프로그래밍 방식 런타임 업데이트

ConfigManager를 통해 런타임에 설정을 업데이트할 수도 있습니다:

let router = ContinuumRouter::from_config_file("config.yaml")
    .await?
    .enable_hot_reload(true)
    .build()
    .await?;

if let Some(config_handle) = router.config_handle() {
    // 현재 설정 가져오기
    let mut new_config = config_handle.get_config().await;

    // 수정
    new_config.backends.push(BackendConfig {
        name: "new-backend".to_string(),
        url: "http://localhost:8000".to_string(),
        ..BackendConfig::default()
    });

    // 업데이트 적용 (HotReloadService 트리거)
    config_handle.set_config(new_config).await?;
}

설정 변경 구독

watch 채널 패턴을 사용하여 설정 변경 알림을 구독할 수 있습니다:

if let Some(config_handle) = router.config_handle() {
    let mut rx = config_handle.subscribe();

    tokio::spawn(async move {
        while rx.changed().await.is_ok() {
            let config = rx.borrow().clone();
            println!("설정 업데이트됨: {} 백엔드", config.backends.len());
        }
    });
}

핫 리로드 가능 설정

모든 설정이 런타임에 변경 가능한 것은 아닙니다. 핫 리로드 지원 항목은 다음과 같습니다:

즉시 적용:

  • 로깅 레벨
  • 속도 제한 설정
  • 서킷 브레이커 설정
  • 재시도 설정
  • 글로벌 프롬프트

점진적 적용 (새 연결에 새 설정 사용):

  • 백엔드 추가/제거/수정 (그레이스풀 드레이닝)
  • 헬스 체크 설정
  • 타임아웃 설정
  • 선택 전략

재시작 필요:

  • 서버 바인드 주소
  • 워커 수
  • 연결 풀 크기

내부 상태 접근

state() 메서드는 모든 내부 컴포넌트에 대한 참조를 가진 공유 AppState에 대한 접근을 제공합니다:

let router = ContinuumRouter::from_config_file("config.yaml")
    .await?
    .build()
    .await?;

let state = router.state();

// 헬스 체커 상태 확인
if let Some(ref checker) = state.health_checker {
    let status = checker.get_backend_health_status().await;
    for backend in &status {
        println!("{}: {:?}", backend.name, backend.status);
    }
}

// 현재 설정 접근
let config = state.current_config();
println!("선택 전략: {:?}", config.selection_strategy);

커스텀 HTTP 클라이언트

백엔드 통신을 위해 사전 설정된 reqwest::Client를 제공할 수 있습니다. 특정 TLS 설정, 프록시 구성, 또는 연결 풀 튜닝이 필요할 때 유용합니다:

let client = reqwest::Client::builder()
    .timeout(std::time::Duration::from_secs(60))
    .connect_timeout(std::time::Duration::from_secs(5))
    .pool_max_idle_per_host(32)
    .build()?;

let router = ContinuumRouter::from_config(config)
    .with_http_client(client)
    .build()
    .await?;

주의 사항

트레이싱 초기화

라이브러리는 트레이싱을 초기화하지 않습니다. 로그 출력을 원하면 라우터를 빌드하기 전에 tracing_subscriber::fmt::init() (또는 선호하는 트레이싱 설정)을 호출해야 합니다.

메트릭 싱글턴

Prometheus 메트릭 레지스트리(RouterMetrics)는 프로세스 전역 싱글턴입니다. 동일 프로세스에 여러 ContinuumRouter 인스턴스를 임베딩하면 동일한 메트릭 레지스트리를 공유합니다.

그레이스풀 셧다운

serve() 사용 시, 라우터는 SIGINTSIGTERM 신호를 자동으로 처리하고 그레이스풀 셧다운을 수행합니다 (연결 드레이닝, Unix 소켓 정리, 백그라운드 작업 중지).

into_router()로 임베딩할 때는 서버 라이프사이클을 직접 관리합니다. 완료 시 ContinuumRouter 인스턴스에서 shutdown()을 호출하거나, 단순히 드롭하여 리소스를 해제하세요.

실행 가능한 예제

저장소의 examples/ 디렉토리에는 바로 실행할 수 있는 네 가지 예제가 포함되어 있습니다. 각 예제는 cargo run --example <이름>으로 실행할 수 있습니다.

standalone_server

설정 파일을 로드하여 서버를 시작하는 최소한의 예제입니다:

cargo run --example standalone_server -- --config config.yaml

전체 소스는 examples/standalone_server.rs를 참조하세요.

axum_integration

더 큰 Axum 애플리케이션에 라우터를 /llm 경로 아래에 임베딩합니다:

cargo run --example axum_integration -- --config config.yaml

시작 후, 커스텀 엔드포인트는 /health/info에서, LLM 엔드포인트는 /llm/v1/...에서 사용할 수 있습니다.

전체 소스는 examples/axum_integration.rs를 참조하세요.

programmatic_config

YAML 파일 없이 Rust 코드만으로 라우터를 설정합니다:

OPENAI_API_KEY=sk-... cargo run --example programmatic_config
# 또는 로컬 Ollama 인스턴스 사용 시:
OLLAMA_URL=http://localhost:11434 cargo run --example programmatic_config

전체 소스는 examples/programmatic_config.rs를 참조하세요.

hot_reload

파일 기반 핫 리로드 및 설정 변경 이벤트 구독을 시연합니다:

cargo run --example hot_reload -- --config config.yaml

서버 실행 중에 config.yaml을 편집하면 변경 사항이 자동으로 적용됩니다.

전체 소스는 examples/hot_reload.rs를 참조하세요.

ACP 지원

ACP (Agent Communication Protocol) 모듈은 항상 라이브러리 크레이트에 컴파일됩니다 (pub mod acp). 커스텀 애플리케이션을 빌드할 때 ACP 타입, 핸들러, 전송 구현에 직접 접근할 수 있습니다:

use continuum_router::acp::config::AcpConfig;
use continuum_router::acp::router::AcpMethodRouter;
use continuum_router::acp::session::SessionStore;
use continuum_router::acp::transport::stdio::StdioTransport;

CLI 바이너리의 --mode stdio 구현(src/main.rs)은 ACP 핸들러를 커스텀 애플리케이션에 연결하는 방법에 대한 참조입니다. 프로토콜 세부 사항 및 모듈 구조에 대해서는 ACP 아키텍처 문서를 참조하세요.

추가 참고