Files
kebab/tasks/p4/p4-1-llm-trait.md
altair823 f9714aa5cb docs(rename): kb → kebab — README, tasks/, docs/, design doc, report
마지막 commit. 모든 .md 안의 `kb` 단어 일괄 갱신.

- 19 개 crate 이름 (`kb-core`, `kb-app`, …) → `kebab-*` (Rust 모듈
  path 표기 `kb_*` → `kebab_*` 포함).
- 미래 component (`kb-tui`, `kb-desktop`, `kb-asr-whisper`, `kb-ocr`,
  `kb-mcp`, `kb-vlm`, `kb-rerank`, `kb-vision-ocr`, `kb-index`,
  `kb-smoke`, `kb-architecture`) → `kebab-*` (P6+ 가 시작될 때
  같은 prefix 사용).
- CLI 명령 예제: `kb ingest` / `kb search` / `kb ask` / `kb init` /
  `kb doctor` / `kb inspect` / `kb list` / `kb eval` →
  `kebab <verb>`. fenced code block + 인라인 backtick 모두.
- XDG paths + env vars + binary 경로 (`target/release/kb` →
  `target/release/kebab`) 동기화.
- design doc / 최초 보고서 / SMOKE / HOTFIXES / phase epic / task
  spec 모든 reference 통일.
- task-decomposition.md 의 `git -c user.name=kb` 는 과거 git history
  기록용 author 정보라 그대로 유지 (실제 git history 의 author 는
  변경 불가).
- `tasks/phase-5-evaluation.md` 의 `status: planned` →
  `completed` 도 같이 (P5-1 + P5-2 PR 머지 후 미반영분).

## 검증

- `grep -rEn "\bkb-[a-z]|\bkb_[a-z]|\.config/kb\b|kb\.sqlite|\bKB_[A-Z]"
   --include="*.md"` 0 hits (task-decomposition.md 의 git author
  제외).
- 모든 file path reference 살아있음 (renamed file 들 모두 새 path
  로 update).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 04:01:55 +00:00

4.2 KiB

phase, component, task_id, title, status, depends_on, unblocks, contract_source, contract_sections
phase component task_id title status depends_on unblocks contract_source contract_sections
P4 kebab-llm (trait crate) p4-1 LanguageModel trait + GenerateRequest/TokenChunk completed
p0-1
p4-2
p4-3
../../docs/superpowers/specs/2026-04-27-kebab-final-form-design.md
§7.1 GenerateRequest/TokenChunk
§7.2 LanguageModel
§0 Q5 streaming
§3.8 ModelRef

p4-1 — LanguageModel trait crate

Goal

Provide the kebab-llm crate that re-exports the LanguageModel trait and helper types (GenerateRequest, TokenChunk, FinishReason, TokenUsage, ModelRef), plus a MockLanguageModel for downstream tests.

Why now / why this size

kebab-rag (p4-3) consumes a LanguageModel trait object. Owning the trait + a deterministic mock here lets RAG tests run with no Ollama dependency. Real adapters (Ollama, llama.cpp, candle) live in p4-2 and beyond.

Allowed dependencies

  • kebab-core
  • kebab-config
  • serde
  • thiserror
  • tracing
  • [features] mock = [] — opt-in feature flag exposing MockLanguageModel. Default OFF. Release builds compile mock out entirely.

Forbidden dependencies

  • reqwest, ureq, tokio, whisper-rs, kebab-source-fs, kebab-parse-md, kebab-normalize, kebab-chunk, kebab-store-*, kebab-embed*, kebab-search, kebab-rag, kebab-tui, kebab-desktop

Inputs

input type source
GenerateRequest kebab_core::GenerateRequest RAG pipeline
concrete adapter at runtime dyn LanguageModel p4-2+

Outputs

output type downstream
streaming TokenChunk iterator Box<dyn Iterator<Item=anyhow::Result<TokenChunk>> + Send> RAG pipeline
ModelRef identity kebab_core::ModelRef Answer.model

Public surface (signatures only — no new types)

pub use kebab_core::{LanguageModel, GenerateRequest, TokenChunk, FinishReason, TokenUsage, ModelRef};

/// Test-only deterministic mock. Compiled only when `mock` feature is on.
#[cfg(feature = "mock")]
pub struct MockLanguageModel {
    pub model_id: String,
    pub provider: String,
    pub context_tokens: usize,
    pub canned_response: String,                 // emitted token-by-token
    pub canned_finish: kebab_core::FinishReason,
    pub canned_usage:  kebab_core::TokenUsage,
}

#[cfg(feature = "mock")]
impl kebab_core::LanguageModel for MockLanguageModel { /* per §7.2 */ }

Behavior contract

  • MockLanguageModel::generate_stream produces a Box<dyn Iterator> that yields the canned response one Unicode character at a time as TokenChunk::Token, then a final TokenChunk::Done { finish_reason, usage }.
  • The mock honors GenerateRequest.stop: if any stop string appears in the canned response, truncate before emitting.
  • model_ref() returns ModelRef { id, provider, dimensions: None }.
  • The mock must NOT touch the network or filesystem.
  • Real adapters (p4-2+) MUST NOT live in this crate.

Storage / wire effects

  • None.

Test plan

kind description fixture / data
unit mock streams 5 tokens then Done inline
unit mock honors stop strings inline
unit trait dyn dispatch via Box<dyn LanguageModel> works inline
unit concatenation of streamed TokenChunk::Token equals canned text (truncated by stop strings) inline
contract model_ref() populates provider and leaves dimensions = None inline

All tests under cargo test -p kebab-llm.

Definition of Done

  • cargo check -p kebab-llm passes
  • cargo test -p kebab-llm passes
  • No HTTP / async runtime deps present
  • PR links design §7.2 LanguageModel, §0 Q5

Out of scope

  • Real adapter (p4-2).
  • Token counting against the actual tokenizer (best-effort via usage.prompt_tokens reported by the adapter).
  • Server-side cancellation / abort signals (P+).

Risks / notes

  • Real adapters return Unicode-incomplete byte sequences mid-stream; the trait emits TokenChunk::Token(String) so adapters must handle UTF-8 boundary buffering internally.
  • TokenChunk::Done { usage } must always fire, even on error — adapters convert errors into FinishReason::Error(msg) and a final Done.