콘텐츠로 이동

라이브러리 사용법

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 아키텍처 문서를 참조하세요.

추가 참고