Skip to content

Commit 3c80b74

Browse files
author
adminmetricvisio
committed
Updates In Corpus
1 parent 7acf675 commit 3c80b74

1 file changed

Lines changed: 80 additions & 47 deletions

File tree

chatterbot/corpus.py

Lines changed: 80 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,71 @@
1-
import os
21
import io
3-
import glob
42
from pathlib import Path
3+
import glob
4+
from dataclasses import dataclass
5+
from typing import List, Generator, Tuple
6+
57
from chatterbot.exceptions import OptionalDependencyImportError
68

9+
# Try to import ChatterBot corpus data directory
710
try:
811
from chatterbot_corpus.corpus import DATA_DIRECTORY
912
except (ImportError, ModuleNotFoundError):
10-
# Default to the home directory of the current user
11-
DATA_DIRECTORY = os.path.join(
12-
Path.home(),
13-
'chatterbot_corpus',
14-
'data'
15-
)
13+
# Default to home directory
14+
DATA_DIRECTORY = Path.home() / 'chatterbot_corpus' / 'data'
1615

16+
# Default corpus file extensions
17+
CORPUS_EXTENSIONS = ['yml', 'yaml', 'json']
1718

18-
CORPUS_EXTENSION = 'yml'
19+
# Simple cache for loaded corpus files
20+
_corpus_cache = {}
1921

2022

21-
def get_file_path(dotted_path, extension='json') -> str:
23+
@dataclass
24+
class CorpusData:
25+
conversations: List[List[str]]
26+
categories: List[str]
27+
file_path: str
28+
29+
30+
def get_file_path(dotted_path: str, extensions: List[str] = CORPUS_EXTENSIONS) -> Path:
2231
"""
23-
Reads a dotted file path and returns the file path.
32+
Convert a dotted path or filesystem path into an actual file path.
33+
Raises FileNotFoundError if the file does not exist.
2434
"""
25-
# If the operating system's file path seperator character is in the string
26-
if os.sep in dotted_path or '/' in dotted_path:
27-
# Assume the path is a valid file path
28-
return dotted_path
35+
path = Path(dotted_path)
36+
37+
# If path already exists, return it
38+
if path.exists():
39+
return path
2940

41+
# Split dotted path
3042
parts = dotted_path.split('.')
3143
if parts[0] == 'chatterbot':
32-
parts.pop(0)
33-
parts[0] = DATA_DIRECTORY
44+
parts[0] = str(DATA_DIRECTORY)
45+
46+
base_path = Path(*parts)
3447

35-
corpus_path = os.path.join(*parts)
48+
# Check for file existence with supported extensions
49+
for ext in extensions:
50+
candidate = base_path.with_suffix(f'.{ext}')
51+
if candidate.exists():
52+
return candidate
3653

37-
path_with_extension = '{}.{}'.format(corpus_path, extension)
38-
if os.path.exists(path_with_extension):
39-
corpus_path = path_with_extension
54+
# If directory exists, return it
55+
if base_path.is_dir():
56+
return base_path
4057

41-
return corpus_path
58+
raise FileNotFoundError(f"Corpus file or directory not found for: {dotted_path}")
4259

4360

44-
def read_corpus(file_name) -> dict:
61+
def read_corpus(file_path: Path) -> dict:
4562
"""
46-
Read and return the data from a corpus json file.
63+
Read a YAML or JSON corpus file and return its contents.
64+
Caches results for repeated access.
4765
"""
66+
if file_path in _corpus_cache:
67+
return _corpus_cache[file_path]
68+
4869
try:
4970
import yaml
5071
except ImportError:
@@ -55,37 +76,49 @@ def read_corpus(file_name) -> dict:
5576
)
5677
raise OptionalDependencyImportError(message)
5778

58-
with io.open(file_name, encoding='utf-8') as data_file:
59-
return yaml.safe_load(data_file)
79+
try:
80+
with io.open(file_path, encoding='utf-8') as f:
81+
data = yaml.safe_load(f)
82+
except Exception as e:
83+
raise RuntimeError(f"Failed to read corpus file {file_path}: {e}") from e
84+
85+
if not isinstance(data, dict):
86+
raise ValueError(f"Corpus file {file_path} did not return a dictionary.")
87+
88+
_corpus_cache[file_path] = data
89+
return data
6090

6191

62-
def list_corpus_files(dotted_path) -> list[str]:
92+
def list_corpus_files(dotted_path: str) -> List[Path]:
6393
"""
64-
Return a list of file paths to each data file in the specified corpus.
94+
Return a sorted list of all corpus files (with supported extensions)
95+
in the given dotted path or directory.
6596
"""
66-
corpus_path = get_file_path(dotted_path, extension=CORPUS_EXTENSION)
67-
paths = []
97+
path = get_file_path(dotted_path)
98+
files: List[Path] = []
6899

69-
if os.path.isdir(corpus_path):
70-
paths = glob.glob(corpus_path + '/**/*.' + CORPUS_EXTENSION, recursive=True)
100+
if path.is_dir():
101+
for ext in CORPUS_EXTENSIONS:
102+
files.extend(path.rglob(f'*.{ext}'))
71103
else:
72-
paths.append(corpus_path)
104+
files.append(path)
73105

74-
paths.sort()
75-
return paths
106+
return sorted(files)
76107

77108

78-
def load_corpus(*data_file_paths):
109+
def load_corpus(*data_file_paths: str) -> Generator[CorpusData, None, None]:
79110
"""
80-
Return the data contained within a specified corpus.
111+
Yield CorpusData objects for each specified corpus file.
81112
"""
82-
for file_path in data_file_paths:
83-
corpus = []
84-
corpus_data = read_corpus(file_path)
85-
86-
conversations = corpus_data.get('conversations', [])
87-
corpus.extend(conversations)
88-
89-
categories = corpus_data.get('categories', [])
90-
91-
yield corpus, categories, file_path
113+
for file_path_str in data_file_paths:
114+
path = get_file_path(file_path_str)
115+
if path.is_dir():
116+
files = list_corpus_files(path)
117+
else:
118+
files = [path]
119+
120+
for file in files:
121+
corpus_data = read_corpus(file)
122+
conversations = corpus_data.get('conversations', [])
123+
categories = corpus_data.get('categories', [])
124+
yield CorpusData(conversations, categories, str(file))

0 commit comments

Comments
 (0)