본문으로 건너뛰기

ProviderManager 내부 구조

ProviderManager는 LLM 프로바이더의 라이프사이클을 담당하며, 생성·조회·수정·삭제(CRUD) 작업과 프리셋 템플릿 시스템 및 영속 저장소를 관리합니다.


파일 위치

파일경로
ProviderManagerpackages/desktop/app/main/services/capabilities/llm/config-service/ProviderManager.ts
LLMConfigServicepackages/desktop/app/main/services/capabilities/llm/config-service/LLMConfigService.ts
프로바이더 템플릿packages/desktop/app/shared/llm-config.ts (PROVIDER_TEMPLATES)
프로바이더 프리셋packages/desktop/app/shared/provider-presets.ts
스키마packages/desktop/app/main/services/capabilities/llm/config-service/schemas.ts
IPC 라우터packages/desktop/app/main/services/routers/llm/ProviderRouter.ts

아키텍처 컨텍스트

델리게이트 패턴

LLMConfigService는 델리게이트 패턴을 사용하여 책임을 네 개의 전문 매니저에 분산합니다. ProviderManager는 순환 의존성을 피하기 위해 ProviderManagerDelegate 인터페이스를 통해 호스트와 통신합니다.

graph TB
subgraph LLMConfigService
direction TB
PM[ProviderManager]
MDM[ModelDiscoveryManager]
CIOM[ConfigIOManager]
AMM[AgentModelsManager]
end

PM -->|Delegate| LLMConfigService
MDM -->|Delegate| LLMConfigService
CIOM -->|Delegate| LLMConfigService
AMM -->|Delegate| LLMConfigService

subgraph Storage ["저장소"]
SQLite[(SQLite 기본 저장소)]
JSON[(JSON 폴백)]
end

PM --> SQLite
PM --> JSON

subgraph Cache ["캐시"]
PI[Provider Index<br/>Map 인덱스]
end

PM --> PI

델리게이트 인터페이스

interface ProviderManagerDelegate {
loadConfig(): Promise<LLMsConfig>; // JSON 설정 불러오기
saveConfig(): Promise<void>; // JSON 설정 저장
getDb(): DbClient | null; // 데이터베이스 인스턴스 가져오기
waitForMigration(): Promise<void>; // 데이터 마이그레이션 완료 대기
useSQLite(): boolean; // SQLite 사용 여부
isMigrated(): Promise<boolean>; // 마이그레이션 완료 여부
invalidateProviderIndex(): void; // Provider 인덱스 무효화
invalidateConfig(): void; // 설정 캐시 무효화
buildProviderIndex(providers: LLMProvider[]): void; // 인덱스 빌드
getProviderFromIndex(id: string): LLMProvider | undefined; // 인덱스 조회
hasProviderIndex(): boolean; // 인덱스 존재 여부
}

데이터 구조

LLMProvider 전체 정의

interface LLMProvider {
id: string; // UUID 또는 프리셋 ID (예: 'openai', 'anthropic')
name: string; // 표시 이름
apiFormat?: ApiFormat; // 기본 API 포맷 필드
chatApiFormat?: ApiFormat; // 레거시 포맷 필드 (하위 호환성)
apiType?: string; // 레거시 타입 필드
api_base_url?: string; // API 베이스 URL
api_key: string; // 단일 키 값 (레거시; 신규 시스템은 ApiKeyPool 사용)
models: string[]; // 지원 모델 ID 목록
modelConfigs?: ModelConfig[]; // 모델별 세부 설정
modelGroups?: ModelGroup[]; // 모델 그룹 (카테고리 정보 포함)
modelsEndpoint?: string; // 모델 검색 API 엔드포인트
enabled: boolean; // 활성화 여부
transformer?: TransformerConfig; // 요청/응답 트랜스포머 체인
icon?: string; // 아이콘 경로 또는 이모지
website?: string; // 공식 웹사이트 링크
docsUrl?: string; // API 문서 링크
defaultSettings?: CompletionSettings; // 기본 완성 설정
codingPlan?: CodingPlanConfig; // Coding Plan 전용 설정
isSystem?: boolean; // 시스템 내장 플래그 (삭제 불가)
presetId?: string; // 소스 프리셋 템플릿 ID
headers?: Record<string, string>; // 커스텀 HTTP 헤더
createdAt: string; // ISO 타임스탬프
updatedAt: string; // ISO 타임스탬프
}

interface ModelConfig {
id: string;
name: string;
enabled: boolean;
category?: 'chat' | 'reasoning' | 'image' | 'video' | 'embedding' | 'code';
group?: string;
contextLength?: number;
maxTokens?: number;
completionSettings?: CompletionSettings;
vision?: boolean; // 비전 기능
functionCall?: boolean; // 함수 호출 기능
reasoning?: boolean; // 추론 기능
webSearch?: boolean; // 웹 검색 기능
}

알고리즘 및 로직

프로바이더 CRUD 작업 흐름

프로바이더 추가 (SQLite 경로)

1. LLMProviderInput 수신
2. UUID를 provider.id로 생성
3. 기본값 채우기 (enabled=false, defaultSettings=DEFAULT_COMPLETION_SETTINGS)
4. Zod 스키마로 유효성 검사 (LLMProviderSchema.parse)
5. 고유성 확인: SQLite 조회로 ID 미존재 확인
6. SQLite에 쓰기: db.llmProvidersInsert(provider)
7. 모델 목록을 llm_models 테이블에 동기화
8. 모델 그룹을 llm_model_groups 테이블에 동기화
9. Provider Index 무효화: invalidateProviderIndex()
10. { success: true, provider } 반환

프로바이더 수정

1. { id, ...updates } 수신
2. SQLite에서 기존 프로바이더 읽기
3. 수정된 필드 병합 (얕은 병합)
4. updatedAt 타임스탬프 업데이트
5. SQLite에 쓰기: db.llmProvidersUpdate(id, updates)
6. models 또는 modelConfigs 변경 시 → 모델 테이블 동기화
7. Provider Index 무효화
8. { success: true, provider } 반환

프로바이더 삭제

1. isSystem 플래그 확인 → 시스템 내장 프로바이더는 삭제 불가
2. SQLite에서 삭제: db.llmProvidersDelete(id)
3. 연관된 모델 및 그룹 계단식 삭제
4. Provider Index 무효화
5. { success: true } 반환

Provider Index (O(1) 조회)

graph LR
subgraph FirstQuery ["첫 번째 조회"]
Load[loadConfig 또는 SQLite 쿼리] --> Build[Map 빌드]
Build --> Cache[providerIndex: Map&lt;id, Provider&gt;]
end

subgraph SubsequentQuery ["이후 조회"]
Cache --> Lookup[O(1) Map.get]
end

subgraph Mutations ["변경 작업"]
CRUD[add/update/delete] --> Invalidate[providerIndex = null]
Invalidate --> Load
end

동작 규칙:

작업인덱스에 미치는 영향
getProvider(id)인덱스 히트 → 반환; 미스 → 인덱스 재빌드 후 조회
getProviders()전체 목록 반환 및 인덱스 재빌드
addProvider인덱스 무효화
updateProvider인덱스 무효화
deleteProvider인덱스 무효화
toggleProvider인덱스 무효화

이중 저장소 메커니즘 (SQLite + JSON 폴백)

flowchart TD
Start[시작] --> CheckDB{DbClient 사용 가능?}
CheckDB -->|Yes| Migrate[ensureMigrated]
CheckDB -->|No| JSONMode[JSON 모드]

Migrate --> CheckMigrated{이미 마이그레이션됨?}
CheckMigrated -->|Yes| SQLiteMode[SQLite 모드]
CheckMigrated -->|No| DoMigrate[JSON → SQLite 마이그레이션]
DoMigrate --> SQLiteMode

subgraph SQLiteMode ["SQLite 모드"]
SQLiteMode --> SQLiteRead[db.llmProvidersGetAll]
SQLiteMode --> SQLiteWrite[db.llmProvidersInsert/Update/Delete]
end

subgraph JSONMode ["JSON 모드"]
JSONMode --> JSONRead[fs.readFile + JSON.parse]
JSONMode --> JSONWrite[JSON.stringify + fs.writeFile]
end

마이그레이션 절차:

  1. 시작 시 SQLite의 llm_config_migrated 플래그 확인
  2. 아직 마이그레이션되지 않았으면 llms-config.json 읽기
  3. 각 프로바이더 레코드를 SQLite에 하나씩 기록
  4. 모델 및 modelGroups를 해당 테이블에 동기화
  5. 마이그레이션 완료 플래그 설정
  6. JSON 파일은 백업으로 유지 (더 이상 기본 저장소가 아님)

프리셋 / 템플릿 시스템

기본 프로바이더 목록

시스템에는 다음 내장 프로바이더 템플릿이 포함됩니다 (getDefaultProviders()에 정의된 순서):

#ID이름API 포맷
1deepseekDeepSeekopenai
2openrouterOpenRouteropenai
3siliconSiliconFlowopenai
4openaiOpenAIopenai
5anthropicAnthropicanthropic
6geminiGoogle Geminigoogle
7zhipuZhipu AIopenai
8moonshotMoonshotopenai
9dashscopeTongyi Qianwenopenai
10ollamaOllamaopenai
11groqGroqopenai
12claude-codeClaude Codeanthropic

템플릿으로 프로바이더 생성

createProviderFromTemplate(template):
1. 템플릿의 모든 설정 필드 복사
2. api_key = '' 설정 (사용자가 직접 입력해야 함)
3. enabled = false 설정
4. defaultSettings 채우기 (template.defaultSettings 또는 DEFAULT_COMPLETION_SETTINGS 사용)
5. createdAt / updatedAt 타임스탬프 생성
6. LLMProvider 인스턴스 반환

프로바이더 프리셋

프리셋 템플릿은 provider-presets.ts에서 제공되며, 내장 템플릿보다 더 풍부한 설정을 제공합니다. 주로 중국 클라우드 벤더를 대상으로 합니다. 프리셋 템플릿의 구조는 다음과 같습니다:

interface PresetProviderTemplate {
id: string; // 프리셋 ID
name: string; // 표시 이름
apiFormat: ApiFormat; // API 포맷
baseUrl: string; // 베이스 URL
models: string[]; // 모델 목록
modelConfigs: ModelConfig[]; // 모델별 세부 설정
features: string[]; // 기능 태그
// ... 추가 필드
}

프리셋으로 프로바이더 추가:

addFromPreset(presetId, apiKey?):
1. getPresetById(presetId)로 프리셋 탐색
2. 프리셋을 ProviderTemplate으로 변환
3. createProviderFromTemplate(template) 호출
4. apiKey가 제공된 경우 → 키 설정
5. addProvider()를 호출하여 영속화
6. 새 프로바이더 반환

IPC 통합 테이블

IPC 채널방향파라미터반환값Zod 유효성 검사
llmConfig:getProvidersR → M없음LLMProvider[]없음
llmConfig:getProviderR → M{ id: string }LLMProvider | nullllmProviderIdSchema
llmConfig:addProviderR → MLLMProviderInputLLMProviderResultllmProviderCreateSchema
llmConfig:updateProviderR → M{ id, ...fields }LLMProviderResultllmProviderUpdateSchema
llmConfig:deleteProviderR → M{ id: string }{ success, message? }llmProviderIdSchema
llmConfig:toggleProviderR → M{ id, enabled }LLMProviderResultid + boolean
llmConfig:discoverModelsR → M{ id, options? }ProviderModelDiscoveryResultmodelDiscoverOptionsSchema
llmConfig:getProviderPresetsR → M없음PresetProviderTemplate[]없음
llmConfig:addFromPresetR → M{ presetId, apiKey? }LLMProviderResultstring + string?
llmConfig:exportConfigR → M없음LLMsConfig없음
llmConfig:importConfigR → M설정 객체{ success, message? }Zod object

확장 포인트

새 내장 프로바이더 추가

  1. packages/desktop/app/shared/llm-config.tsPROVIDER_TEMPLATES 배열에 ProviderTemplate 추가
  2. ProviderManager.getDefaultProviders()defaultTemplateIds 배열에 새 ID 추가
  3. 특수 URL 빌드 로직이 필요한 경우 url-builder.ts에 처리 추가
  4. 검색 지원이 필요한 경우 provider-presets.tsPROVIDER_SEARCH_CONFIGS에 항목 추가
  5. Coding Plan 지원이 필요한 경우 CODING_PLAN_URL_PRESETS에 항목 추가
  6. Follow-Provider 지원이 필요한 경우 PROVIDER_MODEL_MAPPINGS에 항목 추가

새 프리셋 템플릿 추가

  1. provider-presets.tsPROVIDER_PRESETS 배열에 PresetProviderTemplate 추가
  2. 완전한 모델 설정 작성 (modelConfigs, contextLength, maxTokens, capabilities)
  3. 프론트엔드가 자동으로 프리셋 목록에 새 템플릿을 표시

관련 파일

파일관계
capabilities/llm/config-service/LLMConfigService.tsProviderManager를 초기화하고 Delegate를 제공하는 호스트 서비스
capabilities/llm/config-service/schemas.tsZod 유효성 검사 스키마 (LLMProviderSchema 등)
routers/llm/ProviderRouter.tsIPC 레이어: 프론트엔드 요청 수신 후 LLMConfigService 호출
routers/llm/schemas.tsIPC 파라미터 유효성 검사 스키마
shared/llm-config.ts공유 타입 정의 및 내장 템플릿
shared/provider-presets.ts프리셋 템플릿, 검색 설정, Coding Plan URL
workers/DbClient.tsSQLite 데이터베이스 작업
workers/db/apiKeys.tsAPI 키 테이블 CRUD
capabilities/llm/completion/CompletionService.tsAPI 호출을 위해 프로바이더 설정을 소비