Merge remote-tracking branch 'origin/CODEX' into main_laptop_1

This commit is contained in:
2026-10-06 15:15:22 +09:00
2 changed files with 29 additions and 14 deletions
+11 -8
View File
@@ -1,4 +1,4 @@
"""원문 Markdown 검색과 Gemini 설명. 실무문서는 어떤 경로에서도 읽지 않는다."""
"""지식DB Markdown 검색과 Gemini 설명. 제외 폴더는 읽지 않는다."""
import os
import re
@@ -12,7 +12,8 @@ from pydantic import BaseModel, Field
from common_util.common_util_auth import verify_session
router = APIRouter(prefix="/knowledge-chat", tags=["B01_KnowledgeChat"])
ORIGINAL = Path(__file__).resolve().parents[1] / "resources" / "knowledge" / "original"
KNOWLEDGE = Path(__file__).resolve().parents[1] / "resources" / "knowledge"
EXCLUDED = ("실무문서", "stmate", "다산소프트")
MODEL = "gemini-3.8-flash"
@@ -21,24 +22,26 @@ class ChatRequest(BaseModel):
def _allowed(path: Path) -> bool:
return path.suffix.lower() == ".md" and "실무문서" not in path.relative_to(ORIGINAL).parts
return path.suffix.lower() == ".md" and not any(
excluded in part.casefold()
for part in path.relative_to(KNOWLEDGE).parts
for excluded in EXCLUDED
)
@lru_cache(maxsize=1)
def _sections() -> list[tuple[str, str]]:
sections = []
for path in ORIGINAL.rglob("*.md"):
for path in KNOWLEDGE.rglob("*.md"):
if not _allowed(path):
continue
relative = path.relative_to(ORIGINAL).as_posix()
if path.name in {"CHANGELOG.md", "_index.md"} or path.name.startswith("_"):
continue
relative = path.relative_to(KNOWLEDGE).as_posix()
content = path.read_text(encoding="utf-8", errors="replace")
for part in re.split(r"(?=^#{1,4} )", content, flags=re.MULTILINE):
part = part.strip()
for start in range(0, len(part), 1800):
snippet = part[start : start + 1800].strip()
if len(snippet) >= 40:
if snippet:
sections.append((relative, snippet))
return sections
+18 -6
View File
@@ -3,14 +3,23 @@ import asyncio
from B01_Dashboard import B01_Dashboard_Knowledge_Chat as chat
def test_chat_excludes_practice_documents_from_gemini(monkeypatch, tmp_path):
public = tmp_path / "법률"
private = tmp_path / "실무문서"
public.mkdir()
def test_chat_searches_all_knowledge_markdown_except_excluded_folders(monkeypatch, tmp_path):
public = tmp_path / "original" / "법률"
guide = tmp_path / "technical_info"
private = tmp_path / "original" / "실무문서"
stmate = tmp_path / "original" / "원가계산" / "STmate"
dasan = tmp_path / "original" / "원가계산" / "다산소프트"
public.mkdir(parents=True)
guide.mkdir()
private.mkdir()
stmate.mkdir(parents=True)
dasan.mkdir()
(public / "현행.md").write_text("# 임도 기준\n" + "임도 설치 기준 내용 " * 10, encoding="utf-8")
(guide / "_검토.md").write_text("# 임도 기준\n기술정보 안내 " * 10, encoding="utf-8")
(private / "사례.md").write_text("# 임도 기준\n비공개 사례 " * 10, encoding="utf-8")
monkeypatch.setattr(chat, "ORIGINAL", tmp_path)
(stmate / "자료.md").write_text("# 임도 기준\nSTmate 자료 " * 10, encoding="utf-8")
(dasan / "자료.md").write_text("# 임도 기준\n다산소프트 자료 " * 10, encoding="utf-8")
monkeypatch.setattr(chat, "KNOWLEDGE", tmp_path)
monkeypatch.setenv("GEMINI_API_KEY", "test-key")
chat._sections.cache_clear()
sent = {}
@@ -39,7 +48,10 @@ def test_chat_excludes_practice_documents_from_gemini(monkeypatch, tmp_path):
monkeypatch.setattr(chat.httpx, "AsyncClient", Client)
result = asyncio.run(chat.ask_knowledge_chat(chat.ChatRequest(question="임도 기준"), {}))
assert result["answer"] == "설명 [1]"
assert result["sources"][0]["path"] == "법률/현행.md"
paths = {source["path"] for source in result["sources"]}
assert paths == {"original/법률/현행.md", "technical_info/_검토.md"}
assert "실무문서" not in sent["prompt"]
assert "비공개 사례" not in sent["prompt"]
assert "STmate" not in sent["prompt"]
assert "다산소프트" not in sent["prompt"]
chat._sections.cache_clear()