llm-bridge-spec
LLM 서비스의 스펙, 타입, 그리고 공통 에러 클래스를 정의하는 패키지입니다.
📋 목차
🎯 개요
llm-bridge-spec은 다양한 LLM 서비스들을 통합하여 사용할 수 있도록 표준화된 인터페이스와 에러 처리 체계를 제공합니다.
주요 구성 요소
- 표준 인터페이스: 모든 LLM 브릿지가 따라야 하는 공통 인터페이스
- 타입 정의: TypeScript 타입 안전성을 보장하는 타입 시스템
- 에러 클래스: 일관된 에러 처리를 위한 계층적 에러 클래스 체계
- 매니페스트 스펙: 브릿지 메타데이터 및 설정 스키마 정의
🧭 철학
핵심 철학
LLM 연결은 단순해야 한다.
각 모델마다 제각각인 SDK나 API 구조에 묶이지 않고, 선언적이고 표준화된 방식으로 모델을 연결하고 실행할 수 있어야 한다.
설계 원칙
- 추상화의 균형: Bridge는 LLM 연결만 담당하고, 프롬프트 최적화는 Agent 또는 사용자에게 맡긴다.
- 구현의 자유, 구조는 선언적으로: Node.js, Python, CLI 등 다양한 방식으로 실행 가능하지만, config는 JSON 기반으로 통일
- 언어 독립성: Node.js, Python, Java, Rust 등 어떤 언어에서도 구현 가능
- 일관된 에러 처리: 모든 브릿지에서 동일한 에러 클래스를 사용하여 예측 가능한 에러 처리
- 작고 명확한 시작: 큰 스펙보다 작지만 분명한 구조로 시작하여 선택받도록 한다
핵심 메시지
LLM Bridge는 프롬프트를 최적화하지 않습니다.
단지 다양한 모델을 구조적으로 선언하고, 교체 가능한 구조를 제공할 뿐입니다.
모델 연결은 Bridge에 맡기고, 사용자 경험은 각 Agent에 맡기세요.
📦 설치
pnpm add llm-bridge-spec
npm install llm-bridge-spec
yarn add llm-bridge-spec
🧩 인터페이스 명세
LlmBridge
모든 LLM 브릿지가 구현해야 하는 기본 인터페이스입니다.
export interface LlmBridge {
invoke(prompt: LlmBridgePrompt, option?: InvokeOption): Promise<LlmBridgeResponse>;
invokeStream?(prompt: LlmBridgePrompt, option?: InvokeOption): AsyncIterable<LlmBridgeResponse>;
getMetadata(): Promise<LlmMetadata>;
}
LlmBridgePrompt
LLM에 전달할 프롬프트를 정의합니다.
export interface LlmBridgePrompt {
messages: LlmBridgeMessage[];
}
export interface LlmBridgeMessage {
role: 'user' | 'assistant' | 'system';
content: LlmBridgeContent;
}
InvokeOption
LLM 호출 시 추가 옵션을 정의합니다.
export interface InvokeOption {
temperature?: number;
maxTokens?: number;
topP?: number;
topK?: number;
stopSequence?: string[];
tools?: LlmBridgeTool[];
}
LlmBridgeResponse
LLM의 응답을 정의합니다.
export interface LlmBridgeResponse {
content: LlmBridgeContent;
usage?: LlmUsage;
toolCalls?: LlmBridgeToolCall[];
}
⚠️ 에러 클래스
llm-bridge-spec은 일관된 에러 처리를 위한 계층적 에러 클래스 체계를 제공합니다.
기본 에러 클래스
LlmBridgeError
모든 브릿지 에러의 기본 클래스입니다.
import { LlmBridgeError } from 'llm-bridge-spec';
throw new LlmBridgeError('Something went wrong', originalError);
ConfigurationError
설정 검증 실패 시 사용합니다.
import { ConfigurationError } from 'llm-bridge-spec';
try {
const config = ConfigSchema.parse(userConfig);
} catch (error) {
throw new ConfigurationError('Invalid configuration provided', error);
}
API 에러 클래스
APIError
기본 API 에러 클래스입니다.
import { APIError } from 'llm-bridge-spec';
throw new APIError('API call failed', 500, 'internal_server_error');
RateLimitError
Rate limit 초과 시 사용합니다.
import { RateLimitError } from 'llm-bridge-spec';
if (response.status === 429) {
const retryAfter = parseInt(response.headers['retry-after'] || '60');
const resetTime = new Date(response.headers['x-ratelimit-reset-requests'] * 1000);
throw new RateLimitError(
'Rate limit exceeded',
retryAfter,
100,
0,
resetTime
);
}
QuotaExceededError
월간/일간 quota 초과 시 사용합니다.
import { QuotaExceededError } from 'llm-bridge-spec';
throw new QuotaExceededError(
'Monthly token quota exceeded',
'monthly',
1000000,
1000000,
new Date('2024-02-01')
);
InvalidRequestError
잘못된 요청 시 사용합니다.
import { InvalidRequestError } from 'llm-bridge-spec';
throw new InvalidRequestError(
'Missing required parameters',
['model', 'messages']
);
InsufficientCreditsError
크레딧 부족 시 사용합니다.
import { InsufficientCreditsError } from 'llm-bridge-spec';
throw new InsufficientCreditsError(
'Not enough credits for this request',
10,
50
);
ServiceUnavailableError
서비스 일시 중단 시 사용합니다.
import { ServiceUnavailableError } from 'llm-bridge-spec';
throw new ServiceUnavailableError(
'Service is under maintenance',
3600
);
기타 에러 클래스
NetworkError - 네트워크 연결 문제
AuthenticationError - 인증 실패
ModelNotSupportedError - 지원하지 않는 모델
ResponseParsingError - 응답 파싱 실패
TimeoutError - 요청 타임아웃
🚀 사용 예시
브릿지 구현체에서의 에러 처리
OpenAI Bridge 예시
import {
RateLimitError,
InvalidRequestError,
AuthenticationError,
ServiceUnavailableError,
QuotaExceededError,
LlmBridgeError,
} from 'llm-bridge-spec';
export class OpenAIBridge implements LlmBridge {
async invoke(prompt: LlmBridgePrompt): Promise<LlmBridgeResponse> {
try {
const response = await this.client.chat.completions.create({
model: this.model,
messages: this.toMessages(prompt),
});
return this.toLlmBridgeResponse(response);
} catch (error: any) {
if (error.status === 401) {
throw new AuthenticationError('Invalid API key', error);
}
if (error.status === 429) {
if (error.type === 'insufficient_quota') {
throw new QuotaExceededError(
'OpenAI quota exceeded',
'monthly',
undefined,
undefined,
undefined,
error
);
}
throw new RateLimitError('Rate limit exceeded', 60, undefined, undefined, undefined, error);
}
if (error.status === 400) {
throw new InvalidRequestError(error.message, undefined, error);
}
if (error.status >= 500) {
throw new ServiceUnavailableError(
'OpenAI service temporarily unavailable',
undefined,
error
);
}
throw new LlmBridgeError(`OpenAI API error: ${error.message}`, error);
}
}
}
클라이언트에서의 에러 처리
import {
RateLimitError,
QuotaExceededError,
AuthenticationError,
NetworkError,
} from 'llm-bridge-spec';
async function handleLLMRequest(bridge: LlmBridge, prompt: LlmBridgePrompt) {
try {
return await bridge.invoke(prompt);
} catch (error) {
if (error instanceof RateLimitError) {
console.log(`Rate limited. Retry after ${error.retryAfter} seconds`);
await new Promise(resolve => setTimeout(resolve, error.retryAfter! * 1000));
return handleLLMRequest(bridge, prompt);
}
if (error instanceof QuotaExceededError) {
console.log(`Quota exceeded (${error.quotaType}). Reset at: ${error.resetTime}`);
throw error;
}
if (error instanceof AuthenticationError) {
console.log('Authentication failed. Please check your API key');
throw error;
}
if (error instanceof NetworkError) {
console.log('Network error occurred. Retrying...');
return handleLLMRequest(bridge, prompt);
}
console.error('Unexpected error:', error.message);
throw error;
}
}
로깅 및 모니터링
import { LlmBridgeError, RateLimitError, QuotaExceededError } from 'llm-bridge-spec';
function logError(error: Error, context: any) {
if (error instanceof LlmBridgeError) {
logger.error({
errorType: error.name,
message: error.message,
statusCode: 'statusCode' in error ? error.statusCode : undefined,
apiErrorCode: 'apiErrorCode' in error ? error.apiErrorCode : undefined,
context,
cause: error.cause?.message,
});
if (error instanceof RateLimitError) {
metrics.increment('llm_bridge.rate_limit_errors');
} else if (error instanceof QuotaExceededError) {
metrics.increment('llm_bridge.quota_exceeded_errors');
}
} else {
logger.error({ message: error.message, context });
}
}
🤝 기여하기
이 프로젝트는 Git Workflow Guide를 따릅니다.
📄 라이선스
이 프로젝트는 MIT 라이선스 하에 있습니다.