refactor(core): 빈 re-export shim crate kebab-embed/kebab-llm → kebab-core 흡수
kebab-embed/kebab-llm 은 "새 type 없음"을 자처한 순수 re-export 셸이었다 (trait 은 이미 kebab-core 소유, mock + test helper 만 보유). "kebab-core 재구성 시 안정 surface" 라는 명분은 1인 RAG 엔 speculative YAGNI. 흡수: - kebab-core 에 default-OFF `mock` feature + src/mock.rs (MockEmbedder, MockLanguageModel, assert_vector_shape/assert_unit_norm/assert_finish_chunk 을 kebab_core:: → crate:: import 만 바꿔 verbatim 이동). - production import 2곳(kebab-embed-local, kebab-llm-local) + test import 다수(search/rag/parse-image/embed-local) 를 kebab_core 로 repoint. mock 쓰는 crate 는 dev-dep 에 features=["mock"] (default 빌드 무영향). - shim 자체 테스트: mock 동작 테스트는 kebab-core/tests/ 로 이동, reexports.rs(셸 재수출 테스트)는 폐기. - crates/kebab-embed, crates/kebab-llm 삭제 + workspace member/deps 정리. - ARCHITECTURE/HANDOFF/component README 의 crate 그래프·표·rationale 갱신 (22 → 20 crates). llm-local 의 broken intra-doc link 2건도 정리. trait surface·동작 불변 (test-only + import-rename). workspace build 는 mock default-OFF 라 mock 코드 미컴파일. 적대적 검증 3렌즈(build-test-integrity + behavior-identity[mock byte-identical] + dead-crate-completeness) 통과, clippy --workspace -D warnings 클린. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Mc6W1fgsrbFKTsqA6P8La
This commit is contained in:
@@ -6,7 +6,6 @@
|
||||
|
||||
| Crate | 역할 |
|
||||
|-------|------|
|
||||
| `kebab-llm` | `LanguageModel` trait re-export + `MockLanguageModel` (feature `mock`, default OFF). 새 type 추가 **금지** — 순수 facade. |
|
||||
| `kebab-llm-local` | `OllamaLanguageModel` — `reqwest::blocking` 기반 Ollama `POST /api/generate` 어댑터. line-delimited JSON streaming 디코드. |
|
||||
|
||||
## 구조
|
||||
@@ -86,7 +85,7 @@ flowchart LR
|
||||
|
||||
## 주요 type / trait / 함수
|
||||
|
||||
**Trait** (`kebab-core`, re-export `kebab-llm`):
|
||||
**Trait** (`kebab-core`):
|
||||
- `LanguageModel::model_ref() -> ModelRef` — provider/model/version 식별. `Answer.model_ref` 으로 흘려서 wire payload 가 자가 식별.
|
||||
- `LanguageModel::context_tokens() -> usize` — 모델 별 max prompt+completion 합. RAG 가 budget 계산에 사용.
|
||||
- `LanguageModel::generate_stream(req: GenerateRequest) -> Result<Box<dyn Iterator<Item = Result<TokenChunk>> + Send>>` — async 안 됨, 매 next() 가 blocking. 모든 stream 이 마지막에 `TokenChunk::Done` 으로 끝남 (error 케이스 포함, §0 Q5).
|
||||
@@ -106,20 +105,19 @@ flowchart LR
|
||||
**`LlmError`** (`kebab-llm-local::error`):
|
||||
- ConnectionRefused / HttpStatus(code) / Decode(json error) / Timeout / Aborted / 그 외 — `Err` 로 first chunk 전 surface 가능.
|
||||
|
||||
**테스트 도구** (`kebab-llm`):
|
||||
**테스트 도구** (`kebab-core`, feature `mock`):
|
||||
- `assert_finish_chunk(chunks: &[TokenChunk])` — 마지막이 `Done` 이어야 — 모든 stream contract pin.
|
||||
- `MockLanguageModel` (feature `mock`, default OFF) — deterministic test double. 실 adapter 만 `Err` 가능, mock 은 항상 stream 시작 후 yield.
|
||||
|
||||
## 외부 의존
|
||||
|
||||
- `kebab-llm` → `kebab-core` 만 (re-export crate).
|
||||
- `kebab-llm-local` → `kebab-llm` + `kebab-config`, `reqwest` (`blocking` feature, JSON), `serde` + `serde_json`, `thiserror`, `anyhow`.
|
||||
- `kebab-llm-local` → `kebab-core` + `kebab-config`, `reqwest` (`blocking` feature, JSON), `serde` + `serde_json`, `thiserror`, `anyhow`.
|
||||
- 외부 서비스: **Ollama HTTP** (default `http://127.0.0.1:11434`). default 모델 `gemma4:e4b` (OCR / caption / RAG 모두 같은 family — 단일 모델 다운로드면 전 시스템 동작).
|
||||
|
||||
## 핵심 결정
|
||||
|
||||
- **`kebab-llm` = trait re-export only, **새 type 금지****.
|
||||
**왜**: `kebab-rag` 등 downstream 이 `use kebab_llm::LanguageModel` 안정 surface 의존. 어댑터 (Ollama/llama.cpp/candle) 는 별 crate. swap config-only.
|
||||
- **`LanguageModel` trait + `MockLanguageModel` 가 `kebab-core` 에 직접 거주 (`mock` feature)**.
|
||||
**왜**: `kebab-rag` 등 downstream 은 `use kebab_core::LanguageModel` 로 의존 — 별도 re-export shim 불필요. 어댑터 (Ollama/llama.cpp/candle) 는 여전히 별 crate 라 swap config-only. 과거의 순수 facade `kebab-llm` 은 `kebab-core` 로 fold-in 되어 삭제됨 (crate 그래프는 [`docs/ARCHITECTURE.md`](../../ARCHITECTURE.md) 참조).
|
||||
|
||||
- **synchronous + blocking + stream iterator**.
|
||||
**왜**: §0 Q5 가 streaming 명시. `async` 가 trait object 와 잘 안 맞음 (Rust async-in-trait 안정성 + Send bound 복잡). `reqwest::blocking` + line-delimited frame 의 `Iterator` 가 caller 코드 단순. RAG 가 동기 소비 + UI thread 가 별도 worker 로 spawn.
|
||||
|
||||
Reference in New Issue
Block a user