chore: import upstream snapshot with attribution
Code Quality / Python Lint & Format (push) Has been cancelled
Code Quality / Python Tests (push) Has been cancelled
Code Quality / JavaScript/TypeScript Lint (advisory) (push) Has been cancelled
Security Scan / CodeQL Analysis (python) (push) Has been cancelled
Security Scan / Dependency Review (push) Has been cancelled
Security Scan / CodeQL Analysis (javascript-typescript) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:43:57 +08:00
commit 3fbbd7970c
12165 changed files with 1331979 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
"""
Shared utilities for the Generative AI for Beginners course.
This module provides common functionality used across multiple lessons,
including environment variable handling, input validation, and API utilities.
"""
from .api_utils import (
create_azure_openai_client,
create_openai_client,
make_safe_request,
)
from .env_utils import get_required_env, validate_env_vars
from .input_validation import (
sanitize_prompt_input,
validate_number_input,
validate_text_input,
)
__all__ = [
"get_required_env",
"validate_env_vars",
"validate_number_input",
"validate_text_input",
"sanitize_prompt_input",
"make_safe_request",
"create_openai_client",
"create_azure_openai_client",
]
+174
View File
@@ -0,0 +1,174 @@
"""
API utilities for safe HTTP requests and OpenAI client creation.
This module provides wrapper functions for making HTTP requests with
proper timeout, error handling, and retry logic.
"""
import os
from typing import Any
import requests
from requests.exceptions import RequestException
def make_safe_request(
url: str, method: str = "GET", timeout: int = 30, retries: int = 3, **kwargs: Any
) -> requests.Response:
"""
Make an HTTP request with proper timeout and error handling.
Args:
url: The URL to request.
method: HTTP method (GET, POST, etc.).
timeout: Request timeout in seconds.
retries: Number of retry attempts.
**kwargs: Additional arguments to pass to requests.
Returns:
The Response object.
Raises:
RequestException: If the request fails after all retries.
Example:
>>> response = make_safe_request("https://api.example.com/data")
>>> data = response.json()
"""
last_exception: Exception | None = None
for attempt in range(retries):
try:
response = requests.request(method=method, url=url, timeout=timeout, **kwargs)
response.raise_for_status()
return response
except RequestException as e:
last_exception = e
if attempt < retries - 1:
# Exponential backoff could be added here
continue
raise
# This should never be reached, but just in case
raise last_exception or RequestException("Request failed")
def create_openai_client(api_key: str | None = None) -> Any:
"""
Create an OpenAI client with proper configuration.
Args:
api_key: Optional API key. If not provided, reads from OPENAI_API_KEY env var.
Returns:
An OpenAI client instance.
Raises:
ValueError: If no API key is available.
ImportError: If the openai package is not installed.
Example:
>>> client = create_openai_client()
>>> response = client.responses.create(model="gpt-4o-mini", input="Hello")
"""
try:
from openai import OpenAI
except ImportError as e:
raise ImportError(
"The 'openai' package is required. Install it with: pip install openai"
) from e
key = api_key or os.getenv("OPENAI_API_KEY")
if not key:
raise ValueError(
"OpenAI API key is required. Set OPENAI_API_KEY environment variable "
"or pass api_key parameter."
)
return OpenAI(api_key=key)
def create_azure_openai_client(
endpoint: str | None = None,
api_key: str | None = None,
) -> Any:
"""
Create an Azure OpenAI (Microsoft Foundry) client with proper configuration.
The client targets the Azure OpenAI v1 endpoint (``<endpoint>/openai/v1/``),
which powers the Responses API. Because the v1 endpoint is used, no
``api_version`` is required.
Args:
endpoint: Azure OpenAI endpoint URL. If not provided, reads from
AZURE_OPENAI_ENDPOINT env var.
api_key: Azure OpenAI API key. If not provided, reads from
AZURE_OPENAI_API_KEY env var.
Returns:
An OpenAI client instance configured for the Azure v1 endpoint.
Raises:
ValueError: If endpoint or API key is missing.
ImportError: If the openai package is not installed.
Example:
>>> client = create_azure_openai_client()
>>> response = client.responses.create(model="gpt-4o-mini", input="Hello")
"""
try:
from openai import OpenAI
except ImportError as e:
raise ImportError(
"The 'openai' package is required. Install it with: pip install openai"
) from e
_endpoint = endpoint or os.getenv("AZURE_OPENAI_ENDPOINT")
_api_key = api_key or os.getenv("AZURE_OPENAI_API_KEY")
if not _endpoint:
raise ValueError(
"Azure OpenAI endpoint is required. Set AZURE_OPENAI_ENDPOINT "
"environment variable or pass endpoint parameter."
)
if not _api_key:
raise ValueError(
"Azure OpenAI API key is required. Set AZURE_OPENAI_API_KEY "
"environment variable or pass api_key parameter."
)
return OpenAI(
api_key=_api_key,
base_url=f"{_endpoint.rstrip('/')}/openai/v1/",
)
def download_image(url: str, save_path: str, timeout: int = 30) -> str:
"""
Download an image from a URL and save it to disk.
Args:
url: The URL of the image to download.
save_path: The path where the image should be saved.
timeout: Request timeout in seconds.
Returns:
The path where the image was saved.
Raises:
RequestException: If the download fails.
IOError: If the file cannot be written.
Example:
>>> path = download_image("https://example.com/image.png", "./image.png")
"""
response = make_safe_request(url, timeout=timeout)
# Ensure the directory exists
os.makedirs(os.path.dirname(save_path) or ".", exist_ok=True)
with open(save_path, "wb") as f:
f.write(response.content)
return save_path
+88
View File
@@ -0,0 +1,88 @@
"""
Environment variable utilities for secure configuration management.
This module provides functions to safely retrieve and validate environment
variables, ensuring that sensitive configuration is properly handled.
"""
import os
def get_required_env(var_name: str, description: str | None = None) -> str:
"""
Get a required environment variable or raise an error with helpful message.
Args:
var_name: The name of the environment variable to retrieve.
description: Optional description of what the variable is used for.
Returns:
The value of the environment variable.
Raises:
ValueError: If the environment variable is not set or is empty.
Example:
>>> api_key = get_required_env("OPENAI_API_KEY", "OpenAI API authentication")
"""
value = os.getenv(var_name)
if not value:
desc_part = f" ({description})" if description else ""
raise ValueError(
f"Missing required environment variable: {var_name}{desc_part}. "
f"Please set it in your .env file or environment."
)
return value
def validate_env_vars(*var_names: str) -> dict[str, str]:
"""
Validate that multiple environment variables are set.
Args:
*var_names: Variable names to check.
Returns:
Dictionary mapping variable names to their values.
Raises:
ValueError: If any of the required variables are missing.
Example:
>>> env = validate_env_vars("AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_API_KEY")
>>> print(env["AZURE_OPENAI_ENDPOINT"])
"""
missing = []
values = {}
for var_name in var_names:
value = os.getenv(var_name)
if not value:
missing.append(var_name)
else:
values[var_name] = value
if missing:
raise ValueError(
f"Missing required environment variables: {', '.join(missing)}. "
f"Please set them in your .env file or environment."
)
return values
def get_env_with_default(var_name: str, default: str) -> str:
"""
Get an environment variable with a default value.
Args:
var_name: The name of the environment variable.
default: The default value if the variable is not set.
Returns:
The value of the environment variable or the default.
Example:
>>> model = get_env_with_default("MODEL_NAME", "gpt-4o")
"""
return os.getenv(var_name, default)
+204
View File
@@ -0,0 +1,204 @@
"""
Input validation utilities for secure user input handling.
This module provides functions to validate and sanitize user input,
protecting against prompt injection and other input-based attacks.
"""
import re
def validate_number_input(
value: str, min_val: int = 1, max_val: int = 100, field_name: str = "number"
) -> int:
"""
Validate and convert string input to an integer within bounds.
Args:
value: The string value to validate.
min_val: Minimum allowed value (inclusive).
max_val: Maximum allowed value (inclusive).
field_name: Name of the field for error messages.
Returns:
The validated integer value.
Raises:
ValueError: If the value is not a valid integer or is out of bounds.
Example:
>>> num = validate_number_input("5", min_val=1, max_val=20)
>>> print(num) # 5
"""
try:
num = int(value.strip())
if num < min_val or num > max_val:
raise ValueError(f"{field_name} must be between {min_val} and {max_val}, got {num}")
return num
except (ValueError, AttributeError) as e:
if "must be between" in str(e):
raise
raise ValueError(
f"Please enter a valid {field_name} between {min_val} and {max_val}"
) from e
def validate_text_input(
value: str,
max_length: int = 500,
min_length: int = 1,
allow_empty: bool = False,
field_name: str = "input",
) -> str:
"""
Validate and sanitize text input.
Args:
value: The string value to validate.
max_length: Maximum allowed length.
min_length: Minimum required length.
allow_empty: Whether to allow empty strings.
field_name: Name of the field for error messages.
Returns:
The validated and trimmed string.
Raises:
ValueError: If the value fails validation.
Example:
>>> text = validate_text_input("Hello World", max_length=100)
"""
if value is None:
if allow_empty:
return ""
raise ValueError(f"{field_name} cannot be None")
trimmed = value.strip()
if not trimmed:
if allow_empty:
return ""
raise ValueError(f"{field_name} cannot be empty")
if len(trimmed) > max_length:
raise ValueError(
f"{field_name} is too long. Maximum {max_length} characters allowed, "
f"got {len(trimmed)}"
)
if len(trimmed) < min_length:
raise ValueError(f"{field_name} is too short. Minimum {min_length} characters required")
return trimmed
def sanitize_prompt_input(value: str, max_length: int = 1000, strict: bool = False) -> str:
"""
Sanitize user input intended for use in LLM prompts.
This function removes potentially dangerous characters and patterns
that could be used for prompt injection attacks.
Args:
value: The string to sanitize.
max_length: Maximum allowed length after sanitization.
strict: If True, only allow alphanumeric, spaces, and basic punctuation.
Returns:
The sanitized string.
Raises:
ValueError: If the input is too long or contains only invalid characters.
Example:
>>> safe_input = sanitize_prompt_input("Hello, world!")
"""
if not value:
return ""
# Trim whitespace
sanitized = value.strip()
# Remove null bytes and control characters (except newlines and tabs)
sanitized = re.sub(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]", "", sanitized)
# Remove potentially dangerous template/injection patterns
dangerous_patterns = [
r"\{\{.*?\}\}", # Template injection
r"\${.*?}", # Variable substitution
r"<script.*?>.*?</script>", # Script tags
r"javascript:", # JavaScript URLs
]
for pattern in dangerous_patterns:
sanitized = re.sub(pattern, "", sanitized, flags=re.IGNORECASE | re.DOTALL)
if strict:
# In strict mode, only allow safe characters
sanitized = re.sub(r"[^\w\s,.\'\"-?!@#$%&*()+=:;]", "", sanitized, flags=re.UNICODE)
# Normalize whitespace
sanitized = re.sub(r"\s+", " ", sanitized)
sanitized = sanitized.strip()
if len(sanitized) > max_length:
raise ValueError(f"Input too long. Maximum {max_length} characters allowed.")
if not sanitized:
raise ValueError("Input contains only invalid characters")
return sanitized
def validate_email(email: str) -> str:
"""
Validate an email address format.
Args:
email: The email address to validate.
Returns:
The validated email address (lowercase).
Raises:
ValueError: If the email format is invalid.
"""
email = email.strip().lower()
# Basic email pattern
pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
if not re.match(pattern, email):
raise ValueError(f"Invalid email format: {email}")
return email
def validate_url(url: str, require_https: bool = True) -> str:
"""
Validate a URL format.
Args:
url: The URL to validate.
require_https: If True, only allow HTTPS URLs.
Returns:
The validated URL.
Raises:
ValueError: If the URL format is invalid.
"""
url = url.strip()
# Basic URL pattern
if require_https:
pattern = r"^https://[a-zA-Z0-9.-]+(?:/[^\s]*)?$"
if not re.match(pattern, url):
raise ValueError(f"Invalid HTTPS URL: {url}")
else:
pattern = r"^https?://[a-zA-Z0-9.-]+(?:/[^\s]*)?$"
if not re.match(pattern, url):
raise ValueError(f"Invalid URL: {url}")
return url