refactor(config): ExpansionCfg + [ingest.expansion] 제거
IngestExpansionCfg struct + IngestCfg.expansion 필드 + Default + KEBAB_INGEST_EXPANSION_* env 파싱 + 테스트 제거. migrate.rs 의 ingest.expansion 섹션 주석 제거 — config migrate 테스트는 ingest.code 앵커로 정정(forward-compat: 기존 [ingest.expansion] 섹션은 serde 가 무시). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -606,8 +606,6 @@ impl UiCfg {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct IngestCfg {
|
pub struct IngestCfg {
|
||||||
pub code: IngestCodeCfg,
|
pub code: IngestCodeCfg,
|
||||||
#[serde(default)]
|
|
||||||
pub expansion: IngestExpansionCfg,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// p10-1A-1: settings for the code ingest pipeline. All fields have
|
/// p10-1A-1: settings for the code ingest pipeline. All fields have
|
||||||
@@ -648,34 +646,6 @@ impl Default for IngestCodeCfg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// doc-side expansion config. Default: disabled (requires explicit opt-in).
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
#[serde(default)]
|
|
||||||
pub struct IngestExpansionCfg {
|
|
||||||
/// Whether doc-side alias expansion is enabled during ingest.
|
|
||||||
pub enabled: bool,
|
|
||||||
/// Ollama model used for alias generation (empty = use LLM default).
|
|
||||||
pub model: String,
|
|
||||||
/// Maximum aliases generated per chunk.
|
|
||||||
pub max_aliases_per_chunk: usize,
|
|
||||||
/// Prompt template version tag.
|
|
||||||
pub prompt_version: String,
|
|
||||||
/// Whether alias embeddings are stored as separate dense vectors.
|
|
||||||
pub embed_aliases: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for IngestExpansionCfg {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
enabled: false,
|
|
||||||
model: String::new(),
|
|
||||||
max_aliases_per_chunk: 8,
|
|
||||||
prompt_version: "expansion-v1".to_string(),
|
|
||||||
embed_aliases: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
/// Defaults per design §6.4.
|
/// Defaults per design §6.4.
|
||||||
pub fn defaults() -> Self {
|
pub fn defaults() -> Self {
|
||||||
@@ -1166,25 +1136,6 @@ impl Config {
|
|||||||
self.pdf.ocr.lang_hint = if v.is_empty() { None } else { Some(v.clone()) };
|
self.pdf.ocr.lang_hint = if v.is_empty() { None } else { Some(v.clone()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// ingest.expansion
|
|
||||||
"KEBAB_INGEST_EXPANSION_ENABLED" => {
|
|
||||||
self.ingest.expansion.enabled = parse_bool(v);
|
|
||||||
}
|
|
||||||
"KEBAB_INGEST_EXPANSION_MODEL" => {
|
|
||||||
self.ingest.expansion.model = v.clone();
|
|
||||||
}
|
|
||||||
"KEBAB_INGEST_EXPANSION_MAX_ALIASES" => {
|
|
||||||
if let Ok(n) = v.parse::<usize>() {
|
|
||||||
self.ingest.expansion.max_aliases_per_chunk = n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"KEBAB_INGEST_EXPANSION_PROMPT_VERSION" => {
|
|
||||||
self.ingest.expansion.prompt_version = v.clone();
|
|
||||||
}
|
|
||||||
"KEBAB_INGEST_EXPANSION_EMBED_ALIASES" => {
|
|
||||||
self.ingest.expansion.embed_aliases = parse_bool(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unknown KEBAB_* keys are silently ignored — see
|
// Unknown KEBAB_* keys are silently ignored — see
|
||||||
// `env_unknown_key_is_ignored` test.
|
// `env_unknown_key_is_ignored` test.
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -1913,41 +1864,6 @@ max_context_tokens = 8000
|
|||||||
assert_eq!(cfg.ingest.code.max_file_bytes, 524_288);
|
assert_eq!(cfg.ingest.code.max_file_bytes, 524_288);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn expansion_defaults_off() {
|
|
||||||
let cfg = Config::defaults();
|
|
||||||
assert!(!cfg.ingest.expansion.enabled);
|
|
||||||
assert_eq!(cfg.ingest.expansion.max_aliases_per_chunk, 8);
|
|
||||||
assert_eq!(cfg.ingest.expansion.prompt_version, "expansion-v1");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn expansion_env_override() {
|
|
||||||
let mut env = HashMap::new();
|
|
||||||
env.insert("KEBAB_INGEST_EXPANSION_ENABLED".into(), "true".into());
|
|
||||||
env.insert("KEBAB_INGEST_EXPANSION_MODEL".into(), "gemma3:4b".into());
|
|
||||||
env.insert("KEBAB_INGEST_EXPANSION_MAX_ALIASES".into(), "12".into());
|
|
||||||
env.insert("KEBAB_INGEST_EXPANSION_PROMPT_VERSION".into(), "expansion-v2".into());
|
|
||||||
let c = Config::defaults().apply_env(&env);
|
|
||||||
assert!(c.ingest.expansion.enabled);
|
|
||||||
assert_eq!(c.ingest.expansion.model, "gemma3:4b");
|
|
||||||
assert_eq!(c.ingest.expansion.max_aliases_per_chunk, 12);
|
|
||||||
assert_eq!(c.ingest.expansion.prompt_version, "expansion-v2");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn embed_aliases_defaults_off() {
|
|
||||||
let cfg = Config::defaults();
|
|
||||||
assert!(!cfg.ingest.expansion.embed_aliases);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn embed_aliases_env_override() {
|
|
||||||
let mut env = HashMap::new();
|
|
||||||
env.insert("KEBAB_INGEST_EXPANSION_EMBED_ALIASES".into(), "true".into());
|
|
||||||
let c = Config::defaults().apply_env(&env);
|
|
||||||
assert!(c.ingest.expansion.embed_aliases);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub const CURRENT_SCHEMA_VERSION: u32 = 2;
|
|||||||
#[derive(Clone, Debug, PartialEq, serde::Serialize)]
|
#[derive(Clone, Debug, PartialEq, serde::Serialize)]
|
||||||
pub struct MigrationChange {
|
pub struct MigrationChange {
|
||||||
pub kind: ChangeKind,
|
pub kind: ChangeKind,
|
||||||
/// dotted path, 예: `ingest.expansion`, `workspace.include`.
|
/// dotted path, 예: `ingest.code`, `workspace.include`.
|
||||||
pub path: String,
|
pub path: String,
|
||||||
/// 사람·wire 용 한 줄 설명.
|
/// 사람·wire 용 한 줄 설명.
|
||||||
pub detail: String,
|
pub detail: String,
|
||||||
@@ -83,7 +83,6 @@ fn section_comment(path: &str) -> Option<&'static str> {
|
|||||||
"ui" => "# TUI 팔레트·role 스타일.",
|
"ui" => "# TUI 팔레트·role 스타일.",
|
||||||
"ingest" => "# ingest 정책(code skip 등).",
|
"ingest" => "# ingest 정책(code skip 등).",
|
||||||
"ingest.code" => "# code ingest skip 정책(.gitignore 자동 honor).",
|
"ingest.code" => "# code ingest skip 정책(.gitignore 자동 honor).",
|
||||||
"ingest.expansion" => "# doc-side 별칭 확장(기본 off). 패러프레이즈 강건성↑, LLM 비용 큼.",
|
|
||||||
"pdf" => "# PDF ingest. scanned PDF OCR 은 기본 off(page 당 cost).",
|
"pdf" => "# PDF ingest. scanned PDF OCR 은 기본 off(page 당 cost).",
|
||||||
"pdf.ocr" => "# scanned PDF page-단위 OCR(기본 off).",
|
"pdf.ocr" => "# scanned PDF page-단위 OCR(기본 off).",
|
||||||
"logging" => "# ingest 로그(기본 on, ~/.local/state/kebab/logs).",
|
"logging" => "# ingest 로그(기본 on, ~/.local/state/kebab/logs).",
|
||||||
@@ -259,7 +258,7 @@ mod tests {
|
|||||||
// `[pdf]` 등은 안 나오고 `[pdf.ocr]` 같은 하위 테이블만 직렬화된다.
|
// `[pdf]` 등은 안 나오고 `[pdf.ocr]` 같은 하위 테이블만 직렬화된다.
|
||||||
for section in [
|
for section in [
|
||||||
"[workspace]",
|
"[workspace]",
|
||||||
"[ingest.expansion]",
|
"[ingest.code]",
|
||||||
"[pdf.ocr]",
|
"[pdf.ocr]",
|
||||||
"[logging]",
|
"[logging]",
|
||||||
"[ui]",
|
"[ui]",
|
||||||
@@ -273,8 +272,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reconcile_adds_missing_section_preserving_user_values_and_comments() {
|
fn reconcile_adds_missing_section_preserving_user_values_and_comments() {
|
||||||
// ingest 는 code 만 있고 expansion 누락(v0.21.0 동기 시나리오),
|
// ingest 통째 누락(→ ingest.code 추가), logging 통째 누락,
|
||||||
// logging 통째 누락, score 는 사용자가 바꿈, 주석 보유.
|
// default_k 는 사용자가 바꿈, 주석 보유.
|
||||||
let user_text = "\
|
let user_text = "\
|
||||||
schema_version = 1
|
schema_version = 1
|
||||||
|
|
||||||
@@ -283,9 +282,6 @@ root = \"/my/notes\" # 내 워크스페이스
|
|||||||
|
|
||||||
[search]
|
[search]
|
||||||
default_k = 25
|
default_k = 25
|
||||||
|
|
||||||
[ingest.code]
|
|
||||||
skip_generated_header = true
|
|
||||||
";
|
";
|
||||||
let mut user: DocumentMut = user_text.parse().unwrap();
|
let mut user: DocumentMut = user_text.parse().unwrap();
|
||||||
let reference = annotated_default_document();
|
let reference = annotated_default_document();
|
||||||
@@ -294,25 +290,22 @@ skip_generated_header = true
|
|||||||
reconcile(&ref_tbl, user.as_table_mut(), "", &mut changes);
|
reconcile(&ref_tbl, user.as_table_mut(), "", &mut changes);
|
||||||
let out = user.to_string();
|
let out = user.to_string();
|
||||||
|
|
||||||
// 부분 존재하는 [ingest] 에 expansion 만 주석과 함께 추가.
|
// 누락된 [ingest.code] 가 주석과 함께 추가.
|
||||||
assert!(out.contains("[ingest.expansion]"), "expansion not added:\n{out}");
|
assert!(out.contains("[ingest.code]"), "ingest.code not added:\n{out}");
|
||||||
// 통째 누락된 logging 추가.
|
// 통째 누락된 logging 추가.
|
||||||
assert!(out.contains("[logging]"), "logging not added");
|
assert!(out.contains("[logging]"), "logging not added");
|
||||||
// 사용자 값/주석/기존 섹션 보존.
|
// 사용자 값/주석/기존 섹션 보존.
|
||||||
assert!(out.contains("root = \"/my/notes\""));
|
assert!(out.contains("root = \"/my/notes\""));
|
||||||
assert!(out.contains("# 내 워크스페이스"));
|
assert!(out.contains("# 내 워크스페이스"));
|
||||||
assert!(out.contains("default_k = 25"));
|
assert!(out.contains("default_k = 25"));
|
||||||
assert!(out.contains("skip_generated_header = true"));
|
|
||||||
// 새 섹션 주석 부착.
|
// 새 섹션 주석 부착.
|
||||||
assert!(out.contains("doc-side 별칭"));
|
assert!(out.contains("code ingest skip 정책"));
|
||||||
// 부분 존재 부모로 재귀해 leaf 경로를 기록.
|
// 통째 누락 부모는 부모 경로로 한 번 기록.
|
||||||
assert!(
|
assert!(
|
||||||
changes
|
changes
|
||||||
.iter()
|
.iter()
|
||||||
.any(|c| c.kind == ChangeKind::AddedSection && c.path == "ingest.expansion"),
|
.any(|c| c.kind == ChangeKind::AddedSection && c.path == "ingest")
|
||||||
"changes: {changes:?}"
|
|
||||||
);
|
);
|
||||||
// 통째 누락 부모는 부모 경로로 한 번 기록.
|
|
||||||
assert!(
|
assert!(
|
||||||
changes
|
changes
|
||||||
.iter()
|
.iter()
|
||||||
@@ -381,7 +374,7 @@ include = [\"*.md\"]
|
|||||||
assert_eq!(outcome.to_schema_version, CURRENT_SCHEMA_VERSION);
|
assert_eq!(outcome.to_schema_version, CURRENT_SCHEMA_VERSION);
|
||||||
assert!(outcome.changed());
|
assert!(outcome.changed());
|
||||||
assert!(!outcome.new_text.contains("include"));
|
assert!(!outcome.new_text.contains("include"));
|
||||||
assert!(outcome.new_text.contains("[ingest.expansion]"));
|
assert!(outcome.new_text.contains("[ingest.code]"));
|
||||||
assert_eq!(read_schema_version(&outcome.new_text), CURRENT_SCHEMA_VERSION);
|
assert_eq!(read_schema_version(&outcome.new_text), CURRENT_SCHEMA_VERSION);
|
||||||
|
|
||||||
let again = migrate_document(&outcome.new_text);
|
let again = migrate_document(&outcome.new_text);
|
||||||
|
|||||||
Reference in New Issue
Block a user