From 3f41fa42e173aa09d77c1d830ad044c914d18f3c Mon Sep 17 00:00:00 2001 From: Ar-temis Date: Thu, 16 Apr 2026 02:04:46 +0800 Subject: [PATCH 1/3] Initializing CLAUDE.md, added some tool unit tests --- CLAUDE.md | 96 ++++++++++ DK_SR_CLASSDATA_CHATDKU.csv | Bin 0 -> 623666 bytes chatdku/ingestion/major_ingest.py | 2 +- tests/conftest.py | 147 +++++++++++++++ tests/test_course_schedule.py | 147 +++++++++++++++ tests/test_course_schedule_ret.py | 9 +- tests/test_llama_index_tools.py | 290 ++++++++++++++++++++++++++++++ tests/test_major_requirements.py | 178 ++++++++++++++++++ tests/test_prerequisites.py | 85 +++++++++ 9 files changed, 950 insertions(+), 4 deletions(-) create mode 100644 CLAUDE.md create mode 100755 DK_SR_CLASSDATA_CHATDKU.csv create mode 100644 tests/conftest.py create mode 100644 tests/test_course_schedule.py create mode 100644 tests/test_llama_index_tools.py create mode 100644 tests/test_major_requirements.py create mode 100644 tests/test_prerequisites.py diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..5cf45e3b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,96 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +ChatDKU is an agentic RAG (Retrieval-Augmented Generation) system for Duke Kunshan University. It answers student questions about courses, policies, requirements, and campus resources using a three-stage DSPy pipeline: **Planner** -> **Executor** (assess-act-distill loop) -> **Synthesizer**. + +## Commands + +```bash +# Install dependencies +uv sync + +# Run the agent CLI +python -m chatdku.core.agent + +# Run Django backend +python manage.py runserver + +# Run tests +python -m pytest tests/ +python -m pytest tests/test_retriever.py # single file + +# Lint (CI runs these on changed .py files in PRs) +black --check +flake8 --ignore=E203,W503 --max-line-length 120 + +# Format +black + +# Sync and run on dev server +./devsync.sh # runs the agent remotely +./devsync.sh chatdku/core/tools/your_file.py # runs a specific file +``` + +## Architecture + +### Agent Pipeline (`chatdku/core/`) + +The agent is three DSPy modules in sequence per user message: + +1. **Planner** (`dspy_classes/plan.py`) — Decides whether to answer directly (`send_message`), ask clarifying questions, or produce a plan for what information to gather. +2. **Executor** (`dspy_classes/executor.py`) — Runs an Assess-Act loop guided by the plan, calling tools to gather information. A Distill step extracts only relevant context from the trajectory. +3. **Synthesizer** (`dspy_classes/synthesizer.py`) — Generates the final cited response from distilled context. +4. **ConversationMemory** (`dspy_classes/conversation_memory.py`) — Compresses/maintains chat history across turns. + +Entry point: `chatdku/core/agent.py` + +### Tools (`chatdku/core/tools/`) + +Tools available to the executor: `VectorQuery` (ChromaDB semantic search), `KeywordQuery` (Redis BM25), `MajorRequirementsLookup`, `QueryCurriculum` (PostgreSQL course/syllabus DB), `PrerequisiteLookup`, and others (calculator, campus service, email, search, GraphRAG). + +### Document Ingestion (`chatdku/ingestion/`) + +Parses, chunks, and loads documents into vector/keyword stores: +- `update_data.py` — Parse and chunk documents +- `load_chroma.py` — Load into ChromaDB +- `load_redis.py` — Load into Redis (BM25 index) +- `load_postgres.py` — Load course data into PostgreSQL +- `clean_classdata.py` — Clean class schedule CSV data + +### Configuration (`chatdku/config.py`) + +Singleton `Config` class loaded from environment variables (`.env`). Access via `from chatdku.config import config`. Supports attribute access (`config.llm_temperature`), `.set()`, `.update()`, and a read-only `.view()`. All paths, model names, DB connections, and tuning parameters live here. + +### Backend (`chatdku/backend/`) + +Legacy Flask backend and Django backend (`chatdku/django/`). Django app uses `manage.py` at repo root. + +### Infrastructure + +- **LLM**: OpenAI-compatible endpoint (vLLM/SGLang with Qwen models) +- **Embeddings**: TEI with `BAAI/bge-m3` +- **Vector DB**: ChromaDB +- **Keyword Index**: Redis +- **Course DB**: PostgreSQL +- **Observability**: Arize Phoenix +- **Framework**: DSPy for agent logic, LlamaIndex for document ingestion + +## Code Style + +- **Formatter**: Black (pre-commit hook, CI) +- **Linter**: Flake8 — `max-line-length=120`, ignores `E203,W503` +- **Python**: 3.11 (pinned in `.python-version`); `pyproject.toml` requires `>=3.11, <3.13` +- **Docstrings**: NumPy format preferred (per GUIDE.md) + +## Key Environment Variables + +`LLM_BASE_URL`, `LLM_API_KEY`, `LLM_MODEL`, `TEI_URL`, `EMBEDDING_MODEL`, `CHROMA_HOST`, `CHROMA_DB_PORT`, `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD`, `DB_USER`, `DB_PASSWORD`, `DB_HOST`, `DB_PORT`, `DB_NAME`. See `.env.example` or `chatdku/config.py` for the full set. + +## Git Workflow + +- `main` branch is protected — never push directly; always use PRs with review. +- Create a GitHub issue before starting work. +- Use `devsync.sh` to iterate on the shared dev server (rsyncs code, runs `uv sync`, starts a live session). diff --git a/DK_SR_CLASSDATA_CHATDKU.csv b/DK_SR_CLASSDATA_CHATDKU.csv new file mode 100755 index 0000000000000000000000000000000000000000..236b33bdbd7e8d1676cf448f9a1506e30bde13e0 GIT binary patch literal 623666 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ೂ<%> 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 ks2N=e(^WZ+lG_x=9!8)0iRx;fedL&t7 str: # Remove or replace unsafe characters safe = re.sub(r"[^\w\s-]", "", name) safe = re.sub(r"[-\s]+", "-", safe) - return safe.strip("-").lower() + return safe.strip().lower() def save_major_content(major_name: str, content: Dict, output_dir: Path): diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..ced3b457 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,147 @@ +"""Shared fixtures for ChatDKU tool tests.""" + +from contextlib import contextmanager +from unittest.mock import MagicMock + +import pandas as pd +import pytest + + +@pytest.fixture() +def mock_span_ctx(monkeypatch): + """Mock span_ctx_start so no real tracer/Phoenix is needed. + + Patches at every import site since each tool module binds the name at import time. + Returns the mock span for assertions on set_attributes / set_status. + """ + mock_span = MagicMock() + + @contextmanager + def fake_span_ctx_start(name, kind, parent_context=None): + yield mock_span + + targets = [ + "chatdku.core.utils.span_ctx_start", + "chatdku.core.tools.course_schedule.span_ctx_start", + "chatdku.core.tools.get_prerequisites.span_ctx_start", + "chatdku.core.tools.major_requirements.span_ctx_start", + "chatdku.core.tools.syllabi_tool.query_curriculum_db.span_ctx_start", + "chatdku.core.tools.retriever.base_retriever.span_ctx_start", + ] + for target in targets: + try: + monkeypatch.setattr(target, fake_span_ctx_start) + except (AttributeError, ImportError): + pass # module not yet imported — safe to skip + + return mock_span + + +@pytest.fixture() +def mock_get_current_span(monkeypatch): + """Mock get_current_span for llama_index_tools which uses it directly.""" + mock_span = MagicMock() + monkeypatch.setattr( + "chatdku.core.tools.llama_index_tools.get_current_span", lambda: mock_span + ) + return mock_span + + +@pytest.fixture() +def sample_classdata_csv(tmp_path): + """Create a temporary class schedule CSV with representative data.""" + csv_path = tmp_path / "classdata.csv" + df = pd.DataFrame( + { + "Subject": ["COMPSCI", "COMPSCI", "MATH", "BIOL", "CHINESE"], + "Catalog": ["101", "201", "201", "305", "101A"], + "Section": ["01", "01", "01", "01", "01"], + "Component": ["LEC", "LEC", "LEC", "LAB", "LEC"], + "Instructor": [ + "Alice Smith", + "Bob Jones", + "Carol Lee", + "Dave Kim", + "Eve Wu", + ], + "Days": ["MWF", "TTh", "MWF", "TTh", "MWF"], + "Start Time": ["09:00", "10:30", "11:00", "14:00", "13:00"], + "End Time": ["09:50", "11:45", "11:50", "15:15", "13:50"], + "Enrollment": [30, 25, 40, 15, 20], + } + ) + df.to_csv(csv_path, index=False) + return str(csv_path) + + +@pytest.fixture() +def sample_prereq_csv(tmp_path): + """Create a temporary prerequisites CSV with UTF-16LE encoding. + + Column layout matches positional access in get_prereq: + col 0: ID + col 1: Effective Date (MM/DD/YYYY) + col 2: Subject + col 3: Catalog + cols 4-12: padding + col 13: Description (prerequisite text) + """ + csv_path = tmp_path / "prereq.csv" + rows = [ + # COMPSCI 201 with prereqs, two rows with different dates + [ + 1, + "01/15/2023", + "COMPSCI", + "201", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Prereq: COMPSCI 101", + ], + [ + 2, + "09/01/2024", + "COMPSCI", + "201", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Prereq: COMPSCI 101 or COMPSCI 102", + ], + # MATH 201 with prereqs + [ + 3, + "03/10/2024", + "MATH", + "201", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "Prereq: MATH 101", + ], + # BIOL 305 with empty description + [4, "06/01/2024", "BIOL", "305", "", "", "", "", "", "", "", "", "", ""], + ] + columns = [f"col{i}" for i in range(14)] + df = pd.DataFrame(rows, columns=columns) + df.to_csv(csv_path, index=False, encoding="utf-16le") + return str(csv_path) diff --git a/tests/test_course_schedule.py b/tests/test_course_schedule.py new file mode 100644 index 00000000..3ced34ee --- /dev/null +++ b/tests/test_course_schedule.py @@ -0,0 +1,147 @@ +"""Comprehensive tests for chatdku.core.tools.course_schedule.""" + +import json + +import pandas as pd +import pytest +from opentelemetry.trace import StatusCode + +from chatdku.core.tools.course_schedule import ( + CourseScheduleLookupOuter, + _lookup, + _parse_course, +) + + +# --------------------------------------------------------------------------- +# _parse_course (pure function — no mocks needed) +# --------------------------------------------------------------------------- + + +class TestParseCourse: + def test_with_space(self): + assert _parse_course("COMPSCI 101") == ("COMPSCI", "101") + + def test_no_separator(self): + assert _parse_course("COMPSCI101") == ("COMPSCI", "101") + + def test_with_hyphen(self): + assert _parse_course("COMPSCI-101") == ("COMPSCI", "101") + + def test_alpha_suffix(self): + assert _parse_course("Chinese 101A") == ("CHINESE", "101A") + + def test_strips_whitespace(self): + assert _parse_course(" COMPSCI 101 ") == ("COMPSCI", "101") + + def test_lowercase_normalised_to_upper(self): + assert _parse_course("math 201") == ("MATH", "201") + + def test_empty_string_raises(self): + with pytest.raises(ValueError): + _parse_course("") + + def test_only_symbols_raises(self): + with pytest.raises(ValueError): + _parse_course("!!!") + + def test_only_numbers_raises(self): + with pytest.raises(ValueError): + _parse_course("12345") + + +# --------------------------------------------------------------------------- +# _lookup (needs a DataFrame, no external mocks) +# --------------------------------------------------------------------------- + + +@pytest.fixture() +def schedule_df(): + return pd.DataFrame( + { + "Subject": ["COMPSCI", "COMPSCI", "MATH", "BIOL"], + "Catalog": ["101", "201", "201", "305"], + "Section": ["01", "02", "01", "01"], + "Instructor": ["Alice", "Bob", "Carol", "Dave"], + } + ) + + +class TestLookup: + def test_finds_matching_rows(self, schedule_df): + rows = _lookup("COMPSCI 101", schedule_df) + assert len(rows) == 1 + assert rows[0]["Subject"] == "COMPSCI" + assert rows[0]["Catalog"] == "101" + + def test_returns_empty_for_nonexistent(self, schedule_df): + assert _lookup("ASTROLOGY 1239", schedule_df) == [] + + def test_case_insensitive(self, schedule_df): + rows = _lookup("compsci 201", schedule_df) + assert len(rows) == 1 + + def test_multiple_sections(self, schedule_df): + # Add a second section for COMPSCI 101 + extra = pd.DataFrame( + { + "Subject": ["COMPSCI"], + "Catalog": ["101"], + "Section": ["02"], + "Instructor": ["Eve"], + } + ) + df = pd.concat([schedule_df, extra], ignore_index=True) + rows = _lookup("COMPSCI 101", df) + assert len(rows) == 2 + + +# --------------------------------------------------------------------------- +# CourseScheduleLookupOuter (needs mock_span_ctx + CSV fixture) +# --------------------------------------------------------------------------- + + +class TestCourseScheduleLookupOuter: + def test_returns_callable(self, mock_span_ctx, sample_classdata_csv): + fn = CourseScheduleLookupOuter(sample_classdata_csv) + assert callable(fn) + + def test_single_course_found(self, mock_span_ctx, sample_classdata_csv): + fn = CourseScheduleLookupOuter(sample_classdata_csv) + result = json.loads(fn(["COMPSCI 101"])) + assert "COMPSCI 101" in result + assert isinstance(result["COMPSCI 101"], list) + assert result["COMPSCI 101"][0]["Instructor"] == "Alice Smith" + + def test_multiple_courses(self, mock_span_ctx, sample_classdata_csv): + fn = CourseScheduleLookupOuter(sample_classdata_csv) + result = json.loads(fn(["COMPSCI 101", "MATH 201"])) + assert "COMPSCI 101" in result + assert "MATH 201" in result + + def test_course_not_found_message(self, mock_span_ctx, sample_classdata_csv): + fn = CourseScheduleLookupOuter(sample_classdata_csv) + result = json.loads(fn(["FAKE 999"])) + assert "No schedule found" in result["FAKE 999"] + + def test_mixed_found_and_not_found(self, mock_span_ctx, sample_classdata_csv): + fn = CourseScheduleLookupOuter(sample_classdata_csv) + result = json.loads(fn(["COMPSCI 101", "FAKE 999"])) + assert isinstance(result["COMPSCI 101"], list) + assert "No schedule found" in result["FAKE 999"] + + def test_file_not_found_raises(self, mock_span_ctx): + fn = CourseScheduleLookupOuter("/nonexistent/path.csv") + with pytest.raises(FileNotFoundError): + fn(["COMPSCI 101"]) + + def test_span_status_ok_on_success(self, mock_span_ctx, sample_classdata_csv): + fn = CourseScheduleLookupOuter(sample_classdata_csv) + fn(["COMPSCI 101"]) + calls = mock_span_ctx.set_status.call_args_list + assert any(c.args[0].status_code == StatusCode.OK for c in calls if c.args) + + def test_span_attributes_set(self, mock_span_ctx, sample_classdata_csv): + fn = CourseScheduleLookupOuter(sample_classdata_csv) + fn(["COMPSCI 101"]) + assert mock_span_ctx.set_attributes.called diff --git a/tests/test_course_schedule_ret.py b/tests/test_course_schedule_ret.py index d37b8459..b50d97f1 100644 --- a/tests/test_course_schedule_ret.py +++ b/tests/test_course_schedule_ret.py @@ -4,9 +4,6 @@ _parse_course, ) -CSV_PATH = "/tmp/cleaned_classdata.csv" -df = pd.read_csv(CSV_PATH) - def test_parse_course(): assert _parse_course("COMPSCI 101") == ("COMPSCI", "101") @@ -17,4 +14,10 @@ def test_parse_course(): def test_lookup(): + df = pd.DataFrame( + { + "Subject": ["COMPSCI", "MATH"], + "Catalog": ["101", "201"], + } + ) assert _lookup("ASTROLOGY 1239", df) == [] diff --git a/tests/test_llama_index_tools.py b/tests/test_llama_index_tools.py new file mode 100644 index 00000000..167e03d5 --- /dev/null +++ b/tests/test_llama_index_tools.py @@ -0,0 +1,290 @@ +"""Tests for chatdku.core.tools.llama_index_tools (VectorRetrieverOuter, KeywordRetrieverOuter).""" + +from contextlib import contextmanager +from unittest.mock import MagicMock, patch + +import pytest + +from chatdku.core.tools.retriever.base_retriever import NodeWithScore +from chatdku.core.tools.utils import QueryTimeoutError + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +SAMPLE_NODES = [ + NodeWithScore(node_id="1", text="doc one", metadata={"src": "a"}, score=0.9), + NodeWithScore(node_id="2", text="doc two", metadata={"src": "b"}, score=0.8), +] + + +@contextmanager +def fake_timeout(seconds=5): + """Drop-in replacement for the real timeout context manager.""" + + class FakeCtx: + def run(self, func, *args, **kwargs): + return func(*args, **kwargs) + + yield FakeCtx() + + +@contextmanager +def fake_timeout_that_expires(seconds=5): + """Simulates a timeout by raising QueryTimeoutError on .run().""" + + class FakeCtx: + def run(self, func, *args, **kwargs): + raise QueryTimeoutError(f"Query exceeded {seconds} second timeout") + + yield FakeCtx() + + +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- + + +@pytest.fixture() +def _patch_vector_retriever(monkeypatch): + """Patch VectorRetriever class so no ChromaDB connection is needed.""" + mock_instance = MagicMock() + mock_instance.query_with_tell.return_value = SAMPLE_NODES + mock_cls = MagicMock(return_value=mock_instance) + monkeypatch.setattr( + "chatdku.core.tools.llama_index_tools.VectorRetriever", mock_cls + ) + return mock_instance + + +@pytest.fixture() +def _patch_keyword_retriever(monkeypatch): + """Patch KeywordRetriever class so no Redis connection is needed.""" + mock_instance = MagicMock() + mock_instance.query_with_tell.return_value = SAMPLE_NODES + mock_cls = MagicMock(return_value=mock_instance) + monkeypatch.setattr( + "chatdku.core.tools.llama_index_tools.KeywordRetriever", mock_cls + ) + return mock_instance + + +@pytest.fixture() +def _patch_rerank(monkeypatch): + """Patch the rerank function.""" + mock_rerank = MagicMock(return_value=SAMPLE_NODES[:1]) + monkeypatch.setattr("chatdku.core.tools.llama_index_tools.rerank", mock_rerank) + return mock_rerank + + +@pytest.fixture() +def _patch_timeout(monkeypatch): + """Replace the real timeout with a synchronous fake.""" + monkeypatch.setattr("chatdku.core.tools.llama_index_tools.timeout", fake_timeout) + + +@pytest.fixture() +def _patch_timeout_expires(monkeypatch): + """Replace the real timeout with one that always times out.""" + monkeypatch.setattr( + "chatdku.core.tools.llama_index_tools.timeout", fake_timeout_that_expires + ) + + +# --------------------------------------------------------------------------- +# VectorRetrieverOuter +# --------------------------------------------------------------------------- + + +class TestVectorRetrieverOuter: + @pytest.fixture(autouse=True) + def _setup( + self, + mock_get_current_span, + _patch_vector_retriever, + _patch_rerank, + _patch_timeout, + ): + self.mock_retriever = _patch_vector_retriever + self.mock_rerank = _patch_rerank + + def _make(self, **kwargs): + from chatdku.core.tools.llama_index_tools import VectorRetrieverOuter + + defaults = dict( + retriever_top_k=10, + use_reranker=False, + reranker_top_n=5, + user_id="Chat_DKU", + search_mode=0, + files=[], + ) + defaults.update(kwargs) + return VectorRetrieverOuter(**defaults) + + def test_returns_callable(self): + assert callable(self._make()) + + def test_query_returns_string(self): + fn = self._make() + result = fn("what is DKU?") + assert isinstance(result, str) + + def test_query_calls_retriever(self): + fn = self._make() + fn("what is DKU?") + self.mock_retriever.query_with_tell.assert_called_once() + + def test_with_reranker_calls_rerank(self): + fn = self._make(use_reranker=True) + fn("what is DKU?") + self.mock_rerank.assert_called_once() + + def test_without_reranker_skips_rerank(self): + fn = self._make(use_reranker=False) + fn("what is DKU?") + self.mock_rerank.assert_not_called() + + def test_invalid_search_mode_defaults_to_zero(self): + # Should not raise; logs a warning and defaults to 0 + fn = self._make(search_mode=5) + result = fn("test") + assert isinstance(result, str) + + def test_search_mode_nonzero_without_files_defaults(self): + # search_mode=1 but files=[] → should default to 0 + fn = self._make(search_mode=1, files=[]) + result = fn("test") + assert isinstance(result, str) + + def test_value_error_propagates(self): + self.mock_retriever.query_with_tell.side_effect = ValueError("bad input") + fn = self._make() + with pytest.raises(ValueError, match="bad input"): + fn("test") + + def test_retrieval_failure_raises_exception(self): + self.mock_retriever.query_with_tell.side_effect = RuntimeError( + "connection lost" + ) + fn = self._make() + with pytest.raises(Exception, match="Vector retrieval failed"): + fn("test") + + +class TestVectorRetrieverOuterTimeout: + def test_timeout_raises_exception( + self, + mock_get_current_span, + _patch_vector_retriever, + _patch_rerank, + _patch_timeout_expires, + ): + from chatdku.core.tools.llama_index_tools import VectorRetrieverOuter + + fn = VectorRetrieverOuter( + retriever_top_k=10, + use_reranker=False, + reranker_top_n=5, + user_id="Chat_DKU", + search_mode=0, + files=[], + ) + with pytest.raises(Exception, match="timed out"): + fn("test") + + +# --------------------------------------------------------------------------- +# KeywordRetrieverOuter +# --------------------------------------------------------------------------- + + +class TestKeywordRetrieverOuter: + @pytest.fixture(autouse=True) + def _setup( + self, + mock_get_current_span, + _patch_keyword_retriever, + _patch_rerank, + _patch_timeout, + ): + self.mock_retriever = _patch_keyword_retriever + self.mock_rerank = _patch_rerank + + def _make(self, **kwargs): + from chatdku.core.tools.llama_index_tools import KeywordRetrieverOuter + + defaults = dict( + retriever_top_k=10, + use_reranker=False, + reranker_top_n=5, + user_id="Chat_DKU", + search_mode=0, + files=[], + ) + defaults.update(kwargs) + return KeywordRetrieverOuter(**defaults) + + def test_returns_callable(self): + assert callable(self._make()) + + def test_query_string_returns_string(self): + fn = self._make() + result = fn("DKU courses") + assert isinstance(result, str) + + def test_query_list_converts_to_strings(self): + fn = self._make() + result = fn(["term1", 42, "term3"]) + assert isinstance(result, str) + # The function stringifies list items in-place + self.mock_retriever.query_with_tell.assert_called_once() + + def test_query_calls_retriever(self): + fn = self._make() + fn("test query") + self.mock_retriever.query_with_tell.assert_called_once() + + def test_with_reranker_calls_rerank(self): + fn = self._make(use_reranker=True) + fn("test") + self.mock_rerank.assert_called_once() + + def test_without_reranker_skips_rerank(self): + fn = self._make(use_reranker=False) + fn("test") + self.mock_rerank.assert_not_called() + + def test_invalid_search_mode_defaults_to_zero(self): + fn = self._make(search_mode=5) + result = fn("test") + assert isinstance(result, str) + + def test_retrieval_failure_raises_exception(self): + self.mock_retriever.query_with_tell.side_effect = RuntimeError("redis down") + fn = self._make() + with pytest.raises(Exception, match="Keyword retrieval failed"): + fn("test") + + +class TestKeywordRetrieverOuterTimeout: + def test_timeout_raises_exception( + self, + mock_get_current_span, + _patch_keyword_retriever, + _patch_rerank, + _patch_timeout_expires, + ): + from chatdku.core.tools.llama_index_tools import KeywordRetrieverOuter + + fn = KeywordRetrieverOuter( + retriever_top_k=10, + use_reranker=False, + reranker_top_n=5, + user_id="Chat_DKU", + search_mode=0, + files=[], + ) + with pytest.raises(Exception, match="Keyword retriever timeout"): + fn("test") diff --git a/tests/test_major_requirements.py b/tests/test_major_requirements.py new file mode 100644 index 00000000..19548536 --- /dev/null +++ b/tests/test_major_requirements.py @@ -0,0 +1,178 @@ +"""Tests for chatdku.core.tools.major_requirements.""" + +import pytest +from opentelemetry.trace import StatusCode + +from chatdku.core.tools.major_requirements import ( + MajorRequirementsLookupOuter, + _best_match, + _jaccard, + _list_stems, + _tokenize, +) + + +# --------------------------------------------------------------------------- +# _tokenize (pure) +# --------------------------------------------------------------------------- + + +class TestTokenize: + def test_lowercases(self): + assert _tokenize("Data Science") == {"data", "science"} + + def test_strips_separators(self): + result = _tokenize("data-science/track") + assert "data" in result + assert "science" in result + assert "track" in result + + def test_removes_punctuation(self): + result = _tokenize("hello! world?") + assert result == {"hello", "world"} + + def test_empty_string(self): + assert _tokenize("") == set() + + +# --------------------------------------------------------------------------- +# _jaccard (pure) +# --------------------------------------------------------------------------- + + +class TestJaccard: + def test_identical_sets(self): + assert _jaccard({"a", "b"}, {"a", "b"}) == 1.0 + + def test_disjoint_sets(self): + assert _jaccard({"a"}, {"b"}) == 0.0 + + def test_partial_overlap(self): + # intersection={b,c}, union={a,b,c,d} → 2/4 = 0.5 + assert _jaccard({"a", "b", "c"}, {"b", "c", "d"}) == 0.5 + + def test_empty_sets(self): + assert _jaccard(set(), set()) == 0.0 + + +# --------------------------------------------------------------------------- +# _best_match (pure) +# --------------------------------------------------------------------------- + + +class TestBestMatch: + STEMS = [ + "data-science", + "computation-and-design-computer-science", + "behavioral-science-psychology", + "requirements-for-all-majors", + ] + + def test_exact_match(self): + assert _best_match("data science", self.STEMS) == "data-science" + + def test_partial_match(self): + result = _best_match("computer science", self.STEMS) + assert result == "computation-and-design-computer-science" + + def test_no_match_returns_none(self): + assert _best_match("astrology", self.STEMS) is None + + def test_empty_query_returns_none(self): + assert _best_match("", self.STEMS) is None + + def test_requirements_for_all(self): + result = _best_match("requirements for all majors", self.STEMS) + assert result == "requirements-for-all-majors" + + +# --------------------------------------------------------------------------- +# _list_stems +# --------------------------------------------------------------------------- + + +class TestListStems: + def test_returns_sorted_stems(self, tmp_path): + (tmp_path / "b-major.md").write_text("B") + (tmp_path / "a-major.md").write_text("A") + stems = _list_stems(tmp_path) + assert stems == ["a-major", "b-major"] + + def test_ignores_non_md_files(self, tmp_path): + (tmp_path / "readme.txt").write_text("text") + (tmp_path / "data.md").write_text("data") + stems = _list_stems(tmp_path) + assert stems == ["data"] + + def test_empty_dir(self, tmp_path): + assert _list_stems(tmp_path) == [] + + +# --------------------------------------------------------------------------- +# MajorRequirementsLookupOuter (needs mock_span_ctx + tmp dir with .md files) +# --------------------------------------------------------------------------- + + +@pytest.fixture() +def requirements_dir(tmp_path): + """Create a temporary requirements directory with sample .md files.""" + (tmp_path / "data-science.md").write_text( + "# Data Science\n\n- COMPSCI 101\n- STATS 202\n" + ) + (tmp_path / "computation-and-design-computer-science.md").write_text( + "# Computation and Design / Computer Science\n\n- COMPSCI 201\n" + ) + (tmp_path / "requirements-for-all-majors.md").write_text( + "# General Requirements\n\n- WRIT 101\n- MATH 101\n" + ) + return str(tmp_path) + + +class TestMajorRequirementsLookupOuter: + def test_returns_callable(self, mock_span_ctx, requirements_dir): + fn = MajorRequirementsLookupOuter(requirements_dir) + assert callable(fn) + + def test_list_returns_all_majors(self, mock_span_ctx, requirements_dir): + fn = MajorRequirementsLookupOuter(requirements_dir) + result = fn("list") + assert "data-science" in result + assert "computation-and-design-computer-science" in result + assert "requirements-for-all-majors" in result + + def test_lookup_returns_file_content(self, mock_span_ctx, requirements_dir): + fn = MajorRequirementsLookupOuter(requirements_dir) + result = fn("data science") + assert "COMPSCI 101" in result + assert "STATS 202" in result + + def test_lookup_prepends_requirements_header(self, mock_span_ctx, requirements_dir): + fn = MajorRequirementsLookupOuter(requirements_dir) + result = fn("data science") + assert result.startswith("# Requirements:") + + def test_no_match_returns_message(self, mock_span_ctx, requirements_dir): + fn = MajorRequirementsLookupOuter(requirements_dir) + result = fn("astrology") + assert "No matching major" in result + + def test_nonexistent_directory_raises(self, mock_span_ctx): + fn = MajorRequirementsLookupOuter("/nonexistent/path") + with pytest.raises(FileNotFoundError): + fn("data science") + + def test_empty_directory_raises(self, mock_span_ctx, tmp_path): + fn = MajorRequirementsLookupOuter(str(tmp_path)) + with pytest.raises(FileNotFoundError): + fn("data science") + + def test_span_status_ok_on_success(self, mock_span_ctx, requirements_dir): + fn = MajorRequirementsLookupOuter(requirements_dir) + fn("data science") + calls = mock_span_ctx.set_status.call_args_list + assert any(c.args[0].status_code == StatusCode.OK for c in calls if c.args) + + def test_span_attributes_set(self, mock_span_ctx, requirements_dir): + fn = MajorRequirementsLookupOuter(requirements_dir) + fn("data science") + assert mock_span_ctx.set_attributes.called diff --git a/tests/test_prerequisites.py b/tests/test_prerequisites.py new file mode 100644 index 00000000..71afdfe4 --- /dev/null +++ b/tests/test_prerequisites.py @@ -0,0 +1,85 @@ +"""Tests for chatdku.core.tools.get_prerequisites.""" + +import pytest +from opentelemetry.trace import StatusCode + +from chatdku.core.tools.get_prerequisites import PrerequisiteLookupOuter, get_prereq + + +# --------------------------------------------------------------------------- +# get_prereq (internal helper) +# --------------------------------------------------------------------------- + + +class TestGetPrereq: + def test_returns_prerequisite_description(self, sample_prereq_csv): + result = get_prereq("COMPSCI 201", sample_prereq_csv) + assert "(Source: DKUHub)" in result + assert "COMPSCI" in result + + def test_uses_latest_effective_date(self, sample_prereq_csv): + """Two rows for COMPSCI 201 — should pick the 09/01/2024 entry.""" + result = get_prereq("COMPSCI 201", sample_prereq_csv) + assert "COMPSCI 102" in result # only in the newer row + + def test_returns_not_found_for_unknown_course(self, sample_prereq_csv): + result = get_prereq("ASTRO 999", sample_prereq_csv) + assert "No prerequisites found" in result + + def test_empty_description_returns_not_found(self, sample_prereq_csv): + """BIOL 305 has an empty description in col 13.""" + result = get_prereq("BIOL 305", sample_prereq_csv) + assert "No prerequisites found" in result + + def test_file_not_found_raises(self): + with pytest.raises(FileNotFoundError): + get_prereq("COMPSCI 201", "/nonexistent/path.csv") + + def test_handles_extra_spaces_in_course_name(self, sample_prereq_csv): + result = get_prereq("COMPSCI 201", sample_prereq_csv) + # Should still parse — splits on underscore after space→underscore replacement + assert "COMPSCI" in result + + def test_known_course_with_prereqs(self, sample_prereq_csv): + result = get_prereq("MATH 201", sample_prereq_csv) + assert "MATH 101" in result + assert "(Source: DKUHub)" in result + + +# --------------------------------------------------------------------------- +# PrerequisiteLookupOuter (needs mock_span_ctx + sample CSV) +# --------------------------------------------------------------------------- + + +class TestPrerequisiteLookupOuter: + def test_returns_callable(self, mock_span_ctx, sample_prereq_csv): + fn = PrerequisiteLookupOuter(sample_prereq_csv) + assert callable(fn) + + def test_single_course_lookup(self, mock_span_ctx, sample_prereq_csv): + fn = PrerequisiteLookupOuter(sample_prereq_csv) + result = fn(["MATH 201"]) + assert "MATH 101" in result + + def test_multiple_courses_joined_by_newline(self, mock_span_ctx, sample_prereq_csv): + fn = PrerequisiteLookupOuter(sample_prereq_csv) + result = fn(["COMPSCI 201", "MATH 201"]) + assert "\n" in result + assert "COMPSCI" in result + assert "MATH" in result + + def test_file_not_found_propagates(self, mock_span_ctx): + fn = PrerequisiteLookupOuter("/nonexistent/path.csv") + with pytest.raises(FileNotFoundError): + fn(["COMPSCI 201"]) + + def test_span_status_ok_on_success(self, mock_span_ctx, sample_prereq_csv): + fn = PrerequisiteLookupOuter(sample_prereq_csv) + fn(["MATH 201"]) + calls = mock_span_ctx.set_status.call_args_list + assert any(c.args[0].status_code == StatusCode.OK for c in calls if c.args) + + def test_span_attributes_set(self, mock_span_ctx, sample_prereq_csv): + fn = PrerequisiteLookupOuter(sample_prereq_csv) + fn(["MATH 201"]) + assert mock_span_ctx.set_attributes.called From 26bfd2dade11b0435a8779b9e900875666c58cf9 Mon Sep 17 00:00:00 2001 From: "Anar.N" Date: Thu, 16 Apr 2026 09:03:04 +0800 Subject: [PATCH 2/3] Updated gitignore to ignore local and transient Claude files. --- .gitignore | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.gitignore b/.gitignore index 9ef164cf..95430e74 100644 --- a/.gitignore +++ b/.gitignore @@ -224,3 +224,13 @@ benchmarks/questions.json #For testing **/benchmarks + +# Claude local and transient stuff +.claude/settings.local.json +.claude/skills/.cache/ +.claude/skills/*.log +.claude/skills/state/ +.claude/skills/tmp/ +.claude/conversations/ +.claude/sessions/ +.claude/checkpoints/ From fc8fa9b5f2eedeaa5d6e3f16b6d351e6126fc681 Mon Sep 17 00:00:00 2001 From: "Anar.N" Date: Thu, 16 Apr 2026 09:12:38 +0800 Subject: [PATCH 3/3] removed DK_SR_CLASSDATA_CHATDKU file from project --- DK_SR_CLASSDATA_CHATDKU.csv | Bin 623666 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 DK_SR_CLASSDATA_CHATDKU.csv diff --git a/DK_SR_CLASSDATA_CHATDKU.csv b/DK_SR_CLASSDATA_CHATDKU.csv deleted file mode 100755 index 236b33bdbd7e8d1676cf448f9a1506e30bde13e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 623666 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ೂ<%> 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 ks2N=e(^WZ+lG_x=9!8)0iRx;fedL&t7