Bellok commited on
Commit
1c68bde
·
1 Parent(s): 87dbd82

feat: Add support for remote pack loading with environment configuration and enhanced metadata handling

Browse files
.hf_pack_cache/datasets--Bellok--warbler-cda-corpus/blobs/45a1df9f2f9e0221d282e0ba9a65b5ac8ac41354 ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "warbler-pack-hf-arxiv",
3
+ "version": "1.0.0",
4
+ "description": "Warbler pack generated from HuggingFace datasets (chunked)",
5
+ "created_at": "2026-04-08T12:07:25.549352",
6
+ "document_count": 250000,
7
+ "source": "HuggingFace",
8
+ "content_types": [
9
+ "scholarly_discussion"
10
+ ],
11
+ "chunked": true,
12
+ "chunk_count": 5,
13
+ "docs_per_chunk": 50000,
14
+ "chunk_pattern": "warbler-pack-hf-arxiv-chunk-*.jsonl"
15
+ }
.hf_pack_cache/datasets--Bellok--warbler-cda-corpus/blobs/f7cb515e761ca01d38aa98b07149c0453840d68f ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "warbler-pack-core",
3
+ "version": "0.1.0",
4
+ "description": "Core conversation pack for Warbler NPC system with essential dialogue templates",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.js"
12
+ },
13
+ "./templates": "./pack/templates.json"
14
+ },
15
+ "files": [
16
+ "dist/**/*",
17
+ "pack/templates.json",
18
+ "README.md",
19
+ "package.json"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "test": "echo \"Info: Content pack - no tests required\"",
24
+ "validate": "node ../../scripts/validate-warbler-pack.mjs pack/templates.json",
25
+ "prepublishOnly": "npm run build && npm run validate"
26
+ },
27
+ "keywords": [
28
+ "warbler",
29
+ "npc",
30
+ "conversation",
31
+ "dialogue",
32
+ "templates",
33
+ "core"
34
+ ],
35
+ "author": "TWG Team",
36
+ "license": "MIT",
37
+ "dependencies": {
38
+ "warbler-core": "^0.1.0"
39
+ },
40
+ "devDependencies": {
41
+ "typescript": "^5.3.0"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/jmeyer1980/TWG-TLDA.git",
46
+ "directory": "packs/warbler-pack-core"
47
+ },
48
+ "engines": {
49
+ "node": ">=18.0.0"
50
+ },
51
+ "warbler": {
52
+ "packType": "core",
53
+ "templateCount": 8,
54
+ "compatibleEngine": "^0.1.0"
55
+ }
56
+ }
.hf_pack_cache/datasets--Bellok--warbler-cda-corpus/refs/main ADDED
@@ -0,0 +1 @@
 
 
1
+ 1944661030a32fea59c540fa6957aca6e60b4ae0
.hf_pack_cache/datasets--Bellok--warbler-cda-corpus/snapshots/1944661030a32fea59c540fa6957aca6e60b4ae0/packs/warbler-pack-core/package.json ADDED
@@ -0,0 +1 @@
 
 
1
+ ../../../../blobs/f7cb515e761ca01d38aa98b07149c0453840d68f
.hf_pack_cache/datasets--Bellok--warbler-cda-corpus/snapshots/1944661030a32fea59c540fa6957aca6e60b4ae0/packs/warbler-pack-hf-arxiv/package.json ADDED
@@ -0,0 +1 @@
 
 
1
+ ../../../../blobs/45a1df9f2f9e0221d282e0ba9a65b5ac8ac41354
app.py CHANGED
@@ -9,6 +9,7 @@ Provides a web UI for the FractalStat RAG system with GPU acceleration.
9
  import gradio as gr
10
  import os
11
  import time
 
12
  from warbler_cda.remote_pack_loader import RemotePackLoader
13
 
14
  # Import the HuggingFace Spaces GPU decorator
@@ -108,10 +109,10 @@ remote_loader = None
108
  documents = []
109
  try:
110
  print(f"📚 Attempting to fetch packs from remote dataset repo: {REMOTE_DATASET_REPO}")
111
- remote_loader = RemotePackLoader(repo_id=REMOTE_DATASET_REPO)
112
  remote_packs_dir = remote_loader.fetch_packs()
113
  print(f"✅ Remote packs downloaded to: {remote_packs_dir}")
114
- pack_loader = PackLoader(packs_dir=remote_packs_dir)
115
  documents = pack_loader.discover_documents()
116
  print(f"✅ Loaded {len(documents)} documents from remote dataset repo")
117
  except Exception as e:
@@ -308,7 +309,7 @@ def get_system_stats() -> str:
308
  output += "### 🕵️ Bob the Skeptic - Conflict Detection\n\n"
309
 
310
  # Access conflict detector if available
311
- conflict_detector = getattr(api, 'conflict_detector', None) if hasattr(api, 'config') and api.config else None
312
 
313
  if conflict_detector and hasattr(conflict_detector, 'get_global_conflict_summary'):
314
  try:
@@ -363,7 +364,11 @@ def get_system_stats() -> str:
363
 
364
  # Recent Activity
365
  output += "### 📈 Recent Activity\n\n"
366
- output += f"**Retrieval Success Rate:** {metrics['system_health']['retrieval_success_rate']:.1% if 'retrieval_success_rate' in metrics['system_health'] else 'N/A'}\n\n"
 
 
 
 
367
 
368
  return output
369
 
 
9
  import gradio as gr
10
  import os
11
  import time
12
+
13
  from warbler_cda.remote_pack_loader import RemotePackLoader
14
 
15
  # Import the HuggingFace Spaces GPU decorator
 
109
  documents = []
110
  try:
111
  print(f"📚 Attempting to fetch packs from remote dataset repo: {REMOTE_DATASET_REPO}")
112
+ remote_loader = RemotePackLoader.from_environment(repo_id=REMOTE_DATASET_REPO)
113
  remote_packs_dir = remote_loader.fetch_packs()
114
  print(f"✅ Remote packs downloaded to: {remote_packs_dir}")
115
+ pack_loader = PackLoader.from_environment(packs_dir=remote_packs_dir)
116
  documents = pack_loader.discover_documents()
117
  print(f"✅ Loaded {len(documents)} documents from remote dataset repo")
118
  except Exception as e:
 
309
  output += "### 🕵️ Bob the Skeptic - Conflict Detection\n\n"
310
 
311
  # Access conflict detector if available
312
+ conflict_detector = getattr(api, 'conflict_detector', None)
313
 
314
  if conflict_detector and hasattr(conflict_detector, 'get_global_conflict_summary'):
315
  try:
 
364
 
365
  # Recent Activity
366
  output += "### 📈 Recent Activity\n\n"
367
+ retrieval_success_rate = metrics["system_health"].get("retrieval_success_rate")
368
+ if retrieval_success_rate is None:
369
+ output += "**Retrieval Success Rate:** N/A\n\n"
370
+ else:
371
+ output += f"**Retrieval Success Rate:** {retrieval_success_rate:.1%}\n\n"
372
 
373
  return output
374
 
tests/test_remote_pack_loader.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from unittest.mock import patch
2
+
3
+
4
+ def test_build_allow_patterns_limits_chunked_remote_pack_downloads():
5
+ from warbler_cda.remote_pack_loader import RemotePackLoader
6
+
7
+ repo_files = [
8
+ "packs/warbler-pack-core/package.json",
9
+ "packs/warbler-pack-core/pack/templates.json",
10
+ "packs/warbler-pack-hf-arxiv/package.json",
11
+ "packs/warbler-pack-hf-arxiv/warbler-pack-hf-arxiv-chunk-001.jsonl",
12
+ "packs/warbler-pack-hf-arxiv/warbler-pack-hf-arxiv-chunk-002.jsonl",
13
+ "packs/warbler-pack-hf-arxiv/warbler-pack-hf-arxiv-chunk-003.jsonl",
14
+ ]
15
+
16
+ loader = RemotePackLoader(
17
+ repo_id="Bellok/warbler-cda-corpus",
18
+ max_documents_per_pack=5000,
19
+ )
20
+
21
+ with patch.object(loader, "_list_repo_files", return_value=repo_files), patch.object(
22
+ loader,
23
+ "_load_pack_metadata",
24
+ side_effect=lambda pack_name: {
25
+ "chunked": True,
26
+ "docs_per_chunk": 50000,
27
+ }
28
+ if pack_name == "warbler-pack-hf-arxiv"
29
+ else {"chunked": False},
30
+ ):
31
+ allow_patterns = loader.build_allow_patterns()
32
+
33
+ assert "packs/warbler-pack-core/package.json" in allow_patterns
34
+ assert "packs/warbler-pack-core/pack/templates.json" in allow_patterns
35
+ assert "packs/warbler-pack-hf-arxiv/package.json" in allow_patterns
36
+ assert "packs/warbler-pack-hf-arxiv/warbler-pack-hf-arxiv-chunk-001.jsonl" in allow_patterns
37
+ assert "packs/warbler-pack-hf-arxiv/warbler-pack-hf-arxiv-chunk-002.jsonl" not in allow_patterns
38
+
39
+
40
+ def test_from_environment_applies_hosted_defaults_for_remote_loader():
41
+ from warbler_cda.remote_pack_loader import RemotePackLoader
42
+
43
+ with patch.dict("os.environ", {"SPACE_ID": "Bellok/warbler-cda"}, clear=False):
44
+ loader = RemotePackLoader.from_environment("Bellok/warbler-cda-corpus")
45
+
46
+ assert loader.max_documents_per_pack == 5000
47
+ assert "warbler-pack-hf-tinystories" in loader.exclude_packs
warbler_cda/remote_pack_loader.py CHANGED
@@ -1,26 +1,175 @@
1
- """RemotePackLoader: Download Warbler packs from a Hugging Face dataset repo."""
 
 
 
 
 
2
  import os
3
  from pathlib import Path
4
- from typing import List, Optional
5
- from huggingface_hub import snapshot_download
 
 
 
 
6
 
7
  class RemotePackLoader:
8
- def __init__(self, repo_id: str, cache_dir: Optional[str] = None, allow_patterns: Optional[List[str]] = None):
 
 
 
 
 
 
 
 
 
 
9
  self.repo_id = repo_id
10
  self.cache_dir = cache_dir or os.getenv("HF_PACK_CACHE", ".hf_pack_cache")
11
- self.allow_patterns = allow_patterns or ["packs/*"]
12
- self.local_dir = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  def fetch_packs(self) -> Path:
15
- """Download the packs directory from the remote dataset repo."""
 
 
 
 
16
  self.local_dir = Path(
17
  snapshot_download(
18
  repo_id=self.repo_id,
19
  repo_type="dataset",
20
  cache_dir=self.cache_dir,
21
- allow_patterns=self.allow_patterns,
22
  local_files_only=False,
23
- resume_download=True,
24
  )
25
  )
26
  return self.local_dir / "packs"
 
1
+ """Download hosted Warbler packs selectively from a Hugging Face dataset repo."""
2
+
3
+ import fnmatch
4
+ import json
5
+ import logging
6
+ import math
7
  import os
8
  from pathlib import Path
9
+ from typing import Dict, List, Optional
10
+
11
+ from huggingface_hub import HfApi, hf_hub_download, snapshot_download
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
 
16
  class RemotePackLoader:
17
+ """Download only the pack files needed for hosted-safe startup."""
18
+
19
+ def __init__(
20
+ self,
21
+ repo_id: str,
22
+ cache_dir: Optional[str] = None,
23
+ include_packs: Optional[List[str]] = None,
24
+ exclude_packs: Optional[List[str]] = None,
25
+ max_documents_per_pack: Optional[int] = None,
26
+ token: Optional[str] = None,
27
+ ):
28
  self.repo_id = repo_id
29
  self.cache_dir = cache_dir or os.getenv("HF_PACK_CACHE", ".hf_pack_cache")
30
+ self.include_packs = include_packs or []
31
+ self.exclude_packs = exclude_packs or []
32
+ self.max_documents_per_pack = max_documents_per_pack
33
+ self.local_dir: Optional[Path] = None
34
+ self.api = HfApi(token=token)
35
+
36
+ @classmethod
37
+ def from_environment(cls, repo_id: str):
38
+ """Create a remote loader configured with the same hosted-safe defaults as PackLoader."""
39
+ include_packs = cls._split_csv_env("WARBLER_INCLUDE_PACKS")
40
+ exclude_packs = cls._split_csv_env("WARBLER_EXCLUDE_PACKS")
41
+ max_documents_per_pack = cls._parse_int_env("WARBLER_MAX_DOCUMENTS_PER_PACK")
42
+
43
+ if cls._is_hosted_environment():
44
+ if not exclude_packs:
45
+ exclude_packs = ["warbler-pack-hf-tinystories"]
46
+ if max_documents_per_pack is None:
47
+ max_documents_per_pack = 5000
48
+
49
+ return cls(
50
+ repo_id=repo_id,
51
+ include_packs=include_packs,
52
+ exclude_packs=exclude_packs,
53
+ max_documents_per_pack=max_documents_per_pack,
54
+ token=os.getenv("HF_TOKEN"),
55
+ )
56
+
57
+ @staticmethod
58
+ def _is_hosted_environment() -> bool:
59
+ hosted_flag = os.getenv("WARBLER_HOSTED_MODE", "").lower()
60
+ return hosted_flag in {"1", "true", "yes", "on"} or bool(
61
+ os.getenv("SPACE_ID") or os.getenv("HF_SPACE_ID")
62
+ )
63
+
64
+ @staticmethod
65
+ def _split_csv_env(name: str) -> List[str]:
66
+ raw_value = os.getenv(name, "")
67
+ return [part.strip() for part in raw_value.split(",") if part.strip()]
68
+
69
+ @staticmethod
70
+ def _parse_int_env(name: str) -> Optional[int]:
71
+ raw_value = os.getenv(name)
72
+ if raw_value in (None, ""):
73
+ return None
74
+ try:
75
+ return int(raw_value)
76
+ except ValueError:
77
+ logger.warning("Ignoring invalid integer for %s: %s", name, raw_value)
78
+ return None
79
+
80
+ def _should_load_pack(self, pack_name: str) -> bool:
81
+ if self.include_packs:
82
+ included = any(fnmatch.fnmatch(pack_name, pattern) for pattern in self.include_packs)
83
+ if not included:
84
+ return False
85
+
86
+ if self.exclude_packs:
87
+ excluded = any(fnmatch.fnmatch(pack_name, pattern) for pattern in self.exclude_packs)
88
+ if excluded:
89
+ return False
90
+
91
+ return True
92
+
93
+ def _list_repo_files(self) -> List[str]:
94
+ return self.api.list_repo_files(repo_id=self.repo_id, repo_type="dataset")
95
+
96
+ def _load_pack_metadata(self, pack_name: str) -> Dict[str, object]:
97
+ metadata_path = f"packs/{pack_name}/package.json"
98
+ try:
99
+ downloaded_path = hf_hub_download(
100
+ repo_id=self.repo_id,
101
+ repo_type="dataset",
102
+ filename=metadata_path,
103
+ cache_dir=self.cache_dir,
104
+ token=os.getenv("HF_TOKEN"),
105
+ )
106
+ except Exception:
107
+ return {}
108
+
109
+ try:
110
+ return json.loads(Path(downloaded_path).read_text(encoding="utf-8"))
111
+ except (OSError, json.JSONDecodeError):
112
+ logger.warning("Failed to parse remote metadata for %s", pack_name)
113
+ return {}
114
+
115
+ def build_allow_patterns(self) -> List[str]:
116
+ """Build a minimal file set for the selected remote packs."""
117
+ repo_files = self._list_repo_files()
118
+ pack_files: Dict[str, List[str]] = {}
119
+
120
+ for repo_file in repo_files:
121
+ parts = Path(repo_file).parts
122
+ if len(parts) < 3 or parts[0] != "packs":
123
+ continue
124
+ pack_name = parts[1]
125
+ if not self._should_load_pack(pack_name):
126
+ continue
127
+ pack_files.setdefault(pack_name, []).append(repo_file)
128
+
129
+ allow_patterns: List[str] = []
130
+ for pack_name in sorted(pack_files):
131
+ files = sorted(pack_files[pack_name])
132
+ metadata = self._load_pack_metadata(pack_name)
133
+ package_json = f"packs/{pack_name}/package.json"
134
+ if package_json in files:
135
+ allow_patterns.append(package_json)
136
+
137
+ templates_path = f"packs/{pack_name}/pack/templates.json"
138
+ if templates_path in files:
139
+ allow_patterns.append(templates_path)
140
+
141
+ jsonl_files = [path for path in files if path.endswith(".jsonl")]
142
+ if metadata.get("chunked"):
143
+ docs_per_chunk = metadata.get("docs_per_chunk")
144
+ chunk_limit = None
145
+ if self.max_documents_per_pack and isinstance(docs_per_chunk, int) and docs_per_chunk > 0:
146
+ chunk_limit = max(1, math.ceil(self.max_documents_per_pack / docs_per_chunk))
147
+ elif self.max_documents_per_pack:
148
+ chunk_limit = 1
149
+
150
+ selected_jsonl = jsonl_files[:chunk_limit] if chunk_limit is not None else jsonl_files
151
+ else:
152
+ preferred_jsonl = f"packs/{pack_name}/{pack_name}.jsonl"
153
+ selected_jsonl = [preferred_jsonl] if preferred_jsonl in jsonl_files else jsonl_files[:1]
154
+
155
+ allow_patterns.extend(selected_jsonl)
156
+
157
+ return allow_patterns
158
 
159
  def fetch_packs(self) -> Path:
160
+ """Download only the selected pack files from the remote dataset repo."""
161
+ allow_patterns = self.build_allow_patterns()
162
+ if not allow_patterns:
163
+ raise RuntimeError(f"No remote pack files selected for repo {self.repo_id}")
164
+
165
  self.local_dir = Path(
166
  snapshot_download(
167
  repo_id=self.repo_id,
168
  repo_type="dataset",
169
  cache_dir=self.cache_dir,
170
+ allow_patterns=allow_patterns,
171
  local_files_only=False,
172
+ token=os.getenv("HF_TOKEN"),
173
  )
174
  )
175
  return self.local_dir / "packs"