Files
wehub-resource-sync 97e91a83f3
Ruff / Ruff (push) Waiting to run
Test / Core Tests (push) Waiting to run
Test / Offline Coverage Tests (Python 3.10) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.11) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.12) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.13) (push) Waiting to run
Test / Offline Coverage Tests (Python 3.9) (push) Waiting to run
Test / Full Coverage (Python 3.11) (push) Waiting to run
Test / Core Provider Tests (OpenAI) (push) Blocked by required conditions
Test / Core Provider Tests (Anthropic) (push) Blocked by required conditions
Test / Core Provider Tests (Google) (push) Blocked by required conditions
Test / Core Provider Tests (Other) (push) Blocked by required conditions
Test / Anthropic Tests (push) Blocked by required conditions
Test / Gemini Tests (push) Blocked by required conditions
Test / Google GenAI Tests (push) Blocked by required conditions
Test / Vertex AI Tests (push) Blocked by required conditions
Test / OpenAI Tests (push) Blocked by required conditions
Test / Writer Tests (push) Blocked by required conditions
Test / Auto Client Tests (push) Blocked by required conditions
ty / type-check (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:38 +08:00

272 lines
8.0 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Fix old client initialization patterns in documentation files.
Replaces old initialization patterns with from_provider:
- instructor.from_openai(OpenAI()) → instructor.from_provider("openai/model-name")
- instructor.from_anthropic(Anthropic()) → instructor.from_provider("anthropic/model-name")
- instructor.patch(OpenAI()) → instructor.from_provider("openai/model-name")
- Similar patterns for all other providers
"""
import argparse
import re
from pathlib import Path
from typing import List, Tuple
# Mapping of provider names to their from_provider identifiers
PROVIDER_MAPPING = {
"openai": "openai",
"anthropic": "anthropic",
"google": "google",
"cohere": "cohere",
"mistral": "mistral",
"groq": "groq",
"litellm": "litellm",
"ollama": "ollama",
"azure": "azure",
"bedrock": "bedrock",
"vertex": "vertex",
"genai": "google", # Google GenAI
"deepseek": "deepseek",
"fireworks": "fireworks",
"cerebras": "cerebras",
"together": "together",
"anyscale": "anyscale",
"perplexity": "perplexity",
"writer": "writer",
"openrouter": "openrouter",
"sambanova": "sambanova",
"truefoundry": "truefoundry",
"cortex": "cortex",
"databricks": "databricks",
"xai": "xai",
}
def find_markdown_files(docs_dir: Path) -> List[Path]:
"""Find all markdown files in the docs directory."""
return list(docs_dir.rglob("*.md")) + list(docs_dir.rglob("*.ipynb"))
def extract_model_name(content: str, match_start: int, match_end: int) -> str:
"""
Try to extract model name from context around the match.
Looks for common patterns like model="...", model='...', or model_name=...
"""
# Look backwards and forwards for model parameter
context_start = max(0, match_start - 200)
context_end = min(len(content), match_end + 200)
context = content[context_start:context_end]
# Try to find model parameter
model_match = re.search(
r'model\s*[=:]\s*["\']([^"\']+)["\']', context, re.IGNORECASE
)
if model_match:
return model_match.group(1)
# Default model names by provider
return "gpt-4o" # Will need manual review for accuracy
def replace_from_pattern(
content: str, provider: str, dry_run: bool = False
) -> Tuple[str, int]:
"""
Replace instructor.from_PROVIDER(Provider()) patterns.
Pattern: instructor.from_openai(OpenAI(model="..."))
→ instructor.from_provider("openai/model-name")
"""
replacements = 0
# Pattern: instructor.from_PROVIDER(ProviderClass(...))
pattern = rf"instructor\.from_{provider}\((\w+)(\([^)]*\))?\)"
def replacer(match):
nonlocal replacements
provider_class = match.group(1)
args = match.group(2) or ""
# Try to extract model name from args
model_match = re.search(r'model\s*=\s*["\']([^"\']+)["\']', args)
if model_match:
model_name = model_match.group(1)
else:
# Default model - may need manual review
model_name = (
"gpt-4o" if provider == "openai" else "claude-3-5-sonnet-20241022"
)
replacements += 1
return f'instructor.from_provider("{provider}/{model_name}")'
new_content = re.sub(pattern, replacer, content, flags=re.IGNORECASE)
return new_content, replacements
def replace_patch_pattern(content: str, dry_run: bool = False) -> Tuple[str, int]:
"""
Replace instructor.patch(Provider()) patterns.
Pattern: instructor.patch(OpenAI(model="..."))
→ instructor.from_provider("openai/model-name")
"""
replacements = 0
# Pattern: instructor.patch(ProviderClass(...))
# Match common provider classes
provider_classes = "|".join(
[
"OpenAI",
"Anthropic",
"GoogleGenerativeAI",
"Cohere",
"Mistral",
"Groq",
"LiteLLM",
"Ollama",
"Bedrock",
"VertexAI",
]
)
pattern = rf"instructor\.patch\(({provider_classes})(\([^)]*\))?\)"
def replacer(match):
nonlocal replacements
provider_class = match.group(1)
args = match.group(2) or ""
# Map class name to provider identifier
class_to_provider = {
"OpenAI": "openai",
"Anthropic": "anthropic",
"GoogleGenerativeAI": "google",
"Cohere": "cohere",
"Mistral": "mistral",
"Groq": "groq",
"LiteLLM": "litellm",
"Ollama": "ollama",
"Bedrock": "bedrock",
"VertexAI": "vertex",
}
provider = class_to_provider.get(provider_class, "openai")
# Try to extract model name from args
model_match = re.search(r'model\s*=\s*["\']([^"\']+)["\']', args)
if model_match:
model_name = model_match.group(1)
else:
# Default models
defaults = {
"openai": "gpt-4o",
"anthropic": "claude-3-5-sonnet-20241022",
"google": "gemini-1.5-pro",
}
model_name = defaults.get(provider, "gpt-4o")
replacements += 1
return f'instructor.from_provider("{provider}/{model_name}")'
new_content = re.sub(pattern, replacer, content)
return new_content, replacements
def replace_old_patterns(content: str, dry_run: bool = False) -> Tuple[str, int]:
"""
Replace all old initialization patterns.
Returns:
Tuple of (new_content, total_replacements)
"""
total_replacements = 0
new_content = content
# Replace instructor.patch() patterns first
new_content, patch_replacements = replace_patch_pattern(new_content, dry_run)
total_replacements += patch_replacements
# Replace instructor.from_* patterns for each provider
for provider in PROVIDER_MAPPING.keys():
new_content, from_replacements = replace_from_pattern(
new_content, provider, dry_run
)
total_replacements += from_replacements
return new_content, total_replacements
def process_file(file_path: Path, dry_run: bool = False) -> int:
"""Process a single file and return number of replacements."""
try:
content = file_path.read_text(encoding="utf-8")
new_content, replacements = replace_old_patterns(content, dry_run)
if replacements > 0:
if dry_run:
print(f"Would fix {replacements} instances in {file_path}")
else:
file_path.write_text(new_content, encoding="utf-8")
print(f"Fixed {replacements} instances in {file_path}")
return replacements
except Exception as e:
print(f"Error processing {file_path}: {e}")
return 0
def main():
parser = argparse.ArgumentParser(
description="Replace old client initialization patterns with from_provider"
)
parser.add_argument(
"--docs-dir",
type=Path,
default=Path("docs"),
help="Directory containing documentation files (default: docs)",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Show what would be changed without making changes",
)
parser.add_argument(
"--file",
type=Path,
help="Process a single file instead of all files",
)
args = parser.parse_args()
if args.file:
files = [args.file]
else:
files = find_markdown_files(args.docs_dir)
total_replacements = 0
files_modified = 0
for file_path in files:
replacements = process_file(file_path, args.dry_run)
if replacements > 0:
total_replacements += replacements
files_modified += 1
print(f"\nSummary:")
print(f" Files processed: {len(files)}")
print(f" Files modified: {files_modified}")
print(f" Total replacements: {total_replacements}")
if args.dry_run:
print("\nRun without --dry-run to apply changes")
else:
print("\n⚠️ Note: Please review model names - defaults may need adjustment")
if __name__ == "__main__":
main()