Files
wehub-resource-sync 76d991c447
Auto Update PR / update-prs (push) Has been cancelled
CI / format-check (push) Has been cancelled
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / live-api-tests (push) Has been cancelled
CI / plugin-integration-test (push) Has been cancelled
CI / ollama-integration-test (push) Has been cancelled
CI / test-fork-pr (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:37:14 +08:00

354 lines
9.7 KiB
Python

# Copyright 2025 Google LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Progress and visualization utilities for LangExtract."""
from __future__ import annotations
from typing import Any
import urllib.parse
import tqdm
# ANSI color codes for terminal output
BLUE = "\033[94m"
GREEN = "\033[92m"
CYAN = "\033[96m"
BOLD = "\033[1m"
RESET = "\033[0m"
# Google Blue color for progress bars
GOOGLE_BLUE = "#4285F4"
def create_download_progress_bar(
total_size: int, url: str, ncols: int = 100, max_url_length: int = 50
) -> tqdm.tqdm:
"""Create a styled progress bar for downloads.
Args:
total_size: Total size in bytes.
url: The URL being downloaded.
ncols: Number of columns for the progress bar.
max_url_length: Maximum length to show for the URL.
Returns:
A configured tqdm progress bar.
"""
# Truncate URL if too long, keeping the domain and end
if len(url) > max_url_length:
parsed = urllib.parse.urlparse(url)
domain = parsed.netloc or parsed.hostname or "unknown"
path_parts = parsed.path.strip("/").split("/")
filename = path_parts[-1] if path_parts and path_parts[-1] else "file"
available = max_url_length - len(domain) - len(filename) - 5
if available > 0:
url_display = f"{domain}/.../{filename}"
else:
url_display = url[: max_url_length - 3] + "..."
else:
url_display = url
return tqdm.tqdm(
total=total_size,
unit="B",
unit_scale=True,
desc=(
f"{BLUE}{BOLD}LangExtract{RESET}: Downloading"
f" {GREEN}{url_display}{RESET}"
),
bar_format=(
"{desc}: {percentage:3.0f}%|{bar}| {n_fmt}/{total_fmt}"
" [{elapsed}<{remaining}, {rate_fmt}]"
),
colour=GOOGLE_BLUE,
ncols=ncols,
)
def create_extraction_progress_bar(
iterable: Any, model_info: str | None = None, disable: bool = False
) -> tqdm.tqdm:
"""Create a styled progress bar for extraction.
Args:
iterable: The iterable to wrap with progress bar.
model_info: Optional model information to display (e.g., "gemini-1.5-pro").
disable: Whether to disable the progress bar.
Returns:
A configured tqdm progress bar.
"""
desc = format_extraction_progress(model_info)
return tqdm.tqdm(
iterable,
desc=desc,
bar_format="{desc} [{elapsed}]",
disable=disable,
dynamic_ncols=True,
)
def print_download_complete(
char_count: int, word_count: int, filename: str
) -> None:
"""Print a styled download completion message.
Args:
char_count: Number of characters downloaded.
word_count: Number of words downloaded.
filename: Name of the downloaded file.
"""
print(
f"{GREEN}{RESET} Downloaded {BOLD}{char_count:,}{RESET} characters "
f"({BOLD}{word_count:,}{RESET} words) from {BLUE}{filename}{RESET}",
flush=True,
)
def print_extraction_complete() -> None:
"""Print a generic extraction completion message."""
print(f"{GREEN}{RESET} Extraction processing complete", flush=True)
def print_extraction_summary(
num_extractions: int,
unique_classes: int,
elapsed_time: float | None = None,
chars_processed: int | None = None,
num_chunks: int | None = None,
) -> None:
"""Print a styled extraction summary with optional performance metrics.
Args:
num_extractions: Total number of extractions.
unique_classes: Number of unique extraction classes.
elapsed_time: Optional elapsed time in seconds.
chars_processed: Optional number of characters processed.
num_chunks: Optional number of chunks processed.
"""
print(
f"{GREEN}{RESET} Extracted {BOLD}{num_extractions}{RESET} entities "
f"({BOLD}{unique_classes}{RESET} unique types)",
flush=True,
)
if elapsed_time is not None:
metrics = []
# Time
metrics.append(f"Time: {BOLD}{elapsed_time:.2f}s{RESET}")
# Speed
if chars_processed is not None and elapsed_time > 0:
speed = chars_processed / elapsed_time
metrics.append(f"Speed: {BOLD}{speed:,.0f}{RESET} chars/sec")
if num_chunks is not None:
metrics.append(f"Chunks: {BOLD}{num_chunks}{RESET}")
for metric in metrics:
print(f" {CYAN}{RESET} {metric}", flush=True)
def create_save_progress_bar(
output_path: str, disable: bool = False
) -> tqdm.tqdm:
"""Create a progress bar for saving documents.
Args:
output_path: The output file path.
disable: Whether to disable the progress bar.
Returns:
A configured tqdm progress bar.
"""
filename = output_path.split("/")[-1]
return tqdm.tqdm(
desc=(
f"{BLUE}{BOLD}LangExtract{RESET}: Saving to {GREEN}{filename}{RESET}"
),
unit=" docs",
disable=disable,
)
def create_load_progress_bar(
file_path: str, total_size: int | None = None, disable: bool = False
) -> tqdm.tqdm:
"""Create a progress bar for loading documents.
Args:
file_path: The file path being loaded.
total_size: Optional total file size in bytes.
disable: Whether to disable the progress bar.
Returns:
A configured tqdm progress bar.
"""
filename = file_path.split("/")[-1]
if total_size:
return tqdm.tqdm(
total=total_size,
desc=(
f"{BLUE}{BOLD}LangExtract{RESET}: Loading {GREEN}{filename}{RESET}"
),
unit="B",
unit_scale=True,
disable=disable,
)
else:
return tqdm.tqdm(
desc=(
f"{BLUE}{BOLD}LangExtract{RESET}: Loading {GREEN}{filename}{RESET}"
),
unit=" docs",
disable=disable,
)
def print_save_complete(num_docs: int, file_path: str) -> None:
"""Print a save completion message.
Args:
num_docs: Number of documents saved.
file_path: Path to the saved file.
"""
filename = file_path.split("/")[-1]
print(
f"{GREEN}{RESET} Saved {BOLD}{num_docs}{RESET} documents to"
f" {GREEN}{filename}{RESET}",
flush=True,
)
def print_load_complete(num_docs: int, file_path: str) -> None:
"""Print a load completion message.
Args:
num_docs: Number of documents loaded.
file_path: Path to the loaded file.
"""
filename = file_path.split("/")[-1]
print(
f"{GREEN}{RESET} Loaded {BOLD}{num_docs}{RESET} documents from"
f" {GREEN}{filename}{RESET}",
flush=True,
)
def get_model_info(language_model: Any) -> str | None:
"""Extract model information from a language model instance.
Args:
language_model: A language model instance.
Returns:
A string describing the model, or None if not available.
"""
if hasattr(language_model, "model_id"):
return language_model.model_id
if hasattr(language_model, "model_url"):
return language_model.model_url
return None
def format_extraction_stats(current_chars: int, processed_chars: int) -> str:
"""Format extraction progress statistics with colors.
Args:
current_chars: Number of characters in current batch.
processed_chars: Total number of characters processed so far.
Returns:
Formatted string with colored statistics.
"""
current_str = f"{GREEN}{current_chars:,}{RESET}"
processed_str = f"{GREEN}{processed_chars:,}{RESET}"
return f"current={current_str} chars, processed={processed_str} chars"
def create_extraction_postfix(current_chars: int, processed_chars: int) -> str:
"""Create a formatted postfix string for extraction progress.
Args:
current_chars: Number of characters in current batch.
processed_chars: Total number of characters processed so far.
Returns:
Formatted string with statistics.
"""
current_str = f"{GREEN}{current_chars:,}{RESET}"
processed_str = f"{GREEN}{processed_chars:,}{RESET}"
return f"current={current_str} chars, processed={processed_str} chars"
def format_extraction_progress(
model_info: str | None,
current_chars: int | None = None,
processed_chars: int | None = None,
) -> str:
"""Format the complete extraction progress bar description.
Args:
model_info: Optional model information (e.g., "gemini-2.0-flash").
current_chars: Number of characters in current batch (optional).
processed_chars: Total number of characters processed so far (optional).
Returns:
Formatted description string.
"""
# Base description
if model_info:
desc = f"{BLUE}{BOLD}LangExtract{RESET}: model={GREEN}{model_info}{RESET}"
else:
desc = f"{BLUE}{BOLD}LangExtract{RESET}: Processing"
# Add stats if provided
if current_chars is not None and processed_chars is not None:
current_str = f"{GREEN}{current_chars:,}{RESET}"
processed_str = f"{GREEN}{processed_chars:,}{RESET}"
desc += f", current={current_str} chars, processed={processed_str} chars"
return desc
def create_pass_progress_bar(
total_passes: int, disable: bool = False
) -> tqdm.tqdm:
"""Create a progress bar for sequential extraction passes.
Args:
total_passes: Total number of sequential passes.
disable: Whether to disable the progress bar.
Returns:
A configured tqdm progress bar.
"""
desc = f"{BLUE}{BOLD}LangExtract{RESET}: Extraction passes"
return tqdm.tqdm(
total=total_passes,
desc=desc,
bar_format=(
"{desc}: {percentage:3.0f}%|{bar}| {n_fmt}/{total_fmt} [{elapsed}]"
),
disable=disable,
colour=GOOGLE_BLUE,
ncols=100,
)