라이브러리 사용법¶
Continuum Router는 독립 실행형 CLI 바이너리 외에도 임베딩 가능한 Rust 라이브러리 크레이트로 사용할 수 있습니다. 별도 프로세스를 실행하지 않고 LLM 라우팅을 Rust 애플리케이션에 직접 통합할 수 있습니다.
사용 사례¶
- 기존 애플리케이션에 임베딩: Axum/Tokio 애플리케이션에 LLM 라우팅 통합
- 프로그래밍 방식 설정: YAML 파일 없이 Rust 코드로 라우터 설정
- 커스텀 확장: 라우터 주변에 커스텀 미들웨어, 라우트 추가 또는 동작 수정
- 통합 테스트: 테스트 스위트를 위한 라우터 인스턴스 프로그래밍 방식 생성
의존성 추가¶
Cargo.toml에 continuum-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() 사용 시, 라우터는 SIGINT 및 SIGTERM 신호를 자동으로 처리하고 그레이스풀 셧다운을 수행합니다 (연결 드레이닝, Unix 소켓 정리, 백그라운드 작업 중지).
into_router()로 임베딩할 때는 서버 라이프사이클을 직접 관리합니다. 완료 시 ContinuumRouter 인스턴스에서 shutdown()을 호출하거나, 단순히 드롭하여 리소스를 해제하세요.
실행 가능한 예제¶
저장소의 examples/ 디렉토리에는 바로 실행할 수 있는 네 가지 예제가 포함되어 있습니다. 각 예제는 cargo run --example <이름>으로 실행할 수 있습니다.
standalone_server¶
설정 파일을 로드하여 서버를 시작하는 최소한의 예제입니다:
전체 소스는 examples/standalone_server.rs를 참조하세요.
axum_integration¶
더 큰 Axum 애플리케이션에 라우터를 /llm 경로 아래에 임베딩합니다:
시작 후, 커스텀 엔드포인트는 /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¶
파일 기반 핫 리로드 및 설정 변경 이벤트 구독을 시연합니다:
서버 실행 중에 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 아키텍처 문서를 참조하세요.
추가 참고¶
- 서버 모듈의 내부 설계에 대한 아키텍처 가이드
- 전체 YAML 설정 레퍼런스를 위한 설정 가이드
- stdio 전송을 통한 IDE 및 도구 통합을 위한 ACP 사용 가이드
- OpenAI 호환 HTTP API를 위한 API 레퍼런스
- 프로젝트 기여 및
examples/디렉토리에 대한 개발 가이드