Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
b8705fb
feat(runtime): Add generic handler factory for serverless execution
deanq Jan 3, 2026
8c0b62a
feat(cli): Add handler generator, manifest builder, and scanner for b…
deanq Jan 3, 2026
c14ed9f
test(runtime): Add comprehensive tests for generic handler
deanq Jan 3, 2026
8c84c34
test(cli): Add tests for handler generation, manifest building, and s…
deanq Jan 3, 2026
cc77fa5
docs(runtime): Document generic handler factory architecture
deanq Jan 3, 2026
72ff4a1
docs(cli): Add flash build command documentation
deanq Jan 3, 2026
e761d48
docs: Add build process and handler generation section to README
deanq Jan 3, 2026
9af1505
feat(cli): Integrate build utilities into flash build command
deanq Jan 3, 2026
b1968d6
refactor(build): Fix directory structure and add comprehensive error …
deanq Jan 3, 2026
8717dc3
feat(resources): Add LoadBalancerSlsResource for LB endpoints
deanq Jan 4, 2026
3cdb565
fix(test): Fix LoadBalancerSlsResource deployment test mocks
deanq Jan 4, 2026
daa1375
feat(resources): Phase 1 - Core infrastructure for @remote on LB endp…
deanq Jan 4, 2026
d02082b
feat(build): Phase 2.1 - Enhanced scanner for HTTP routing extraction
deanq Jan 4, 2026
e83c4f0
feat(build): Phase 2.2 - Updated manifest schema for HTTP routing
deanq Jan 4, 2026
3b41ca4
feat(cli): Add LB handler generator for FastAPI app creation
deanq Jan 4, 2026
6cc2888
feat(runtime): Implement LB handler factory for FastAPI app creation
deanq Jan 4, 2026
babfe12
feat(cli): Route build command to separate handlers for LB endpoints
deanq Jan 4, 2026
c9a160b
feat(resources): Add LiveLoadBalancer for local LB endpoint testing
deanq Jan 4, 2026
7f1961b
test(stubs): Add comprehensive unit tests for LoadBalancerSlsStub
deanq Jan 4, 2026
bc8f733
fix(test): Correct LB endpoint test decorator to match assertions
deanq Jan 4, 2026
79e8f88
docs: Add comprehensive documentation for @remote with LoadBalancer e…
deanq Jan 4, 2026
47d73f8
security: Remove /execute from deployed LoadBalancer endpoints
deanq Jan 4, 2026
2353c69
feat(build): Phase 4 - Fix LiveLoadBalancer handler generation to inc…
deanq Jan 4, 2026
d86b58c
fix(scanner): Discover LoadBalancer resources in addition to Serverle…
deanq Jan 4, 2026
db28ae0
chore: Format code for line length and remove unused imports
deanq Jan 4, 2026
7304d17
fix: Address PR #131 review feedback
deanq Jan 4, 2026
0218995
style: Format datetime chaining for line length
deanq Jan 4, 2026
483536b
fix: LiveLoadBalancer template not serialized to RunPod GraphQL
deanq Jan 4, 2026
ca8cd7e
fix: LoadBalancer endpoint URL and add CPU support
deanq Jan 4, 2026
17bf287
fix: Export CpuLiveLoadBalancer and CpuLoadBalancerSlsResource from t…
deanq Jan 4, 2026
a5368b7
fix: Add API key authentication to LoadBalancer health check
deanq Jan 4, 2026
8cd129a
fix(lb): Exclude flashboot from CpuLoadBalancerSlsResource GraphQL pa…
deanq Jan 4, 2026
cc73b94
fix(lb): Expand CpuInstanceType.ANY to all CPU flavors in CpuLoadBala…
deanq Jan 4, 2026
8bf1739
refactor(cpu): Move instanceIds validator to CpuEndpointMixin
deanq Jan 4, 2026
8f31e03
test: Update CPU instance test to reflect validator expansion
deanq Jan 4, 2026
5da2441
fix(lb): Increase health check timeout from 5s to 15s
deanq Jan 4, 2026
586286d
fix(lb): Fix CPU load balancer template deployment error
deanq Jan 4, 2026
027965c
fix(drift): Exclude runtime fields from config hash to prevent false …
deanq Jan 4, 2026
1b55718
fix(http): Standardize RunPod HTTP client authentication across codebase
deanq Jan 5, 2026
8b97197
feat(http): Extend HTTP utilities to cover both sync and async authen…
deanq Jan 5, 2026
9f4e19a
fix: Address PR feedback on HTTP utilities implementation
deanq Jan 5, 2026
462654b
Merge branch 'deanq/ae-1102-load-balancer-sls-resource' into deanq/ae…
deanq Jan 5, 2026
b57748f
refactor(drift): Extract runtime field constants and improve maintain…
deanq Jan 5, 2026
915f574
docs: Improve LoadBalancer documentation accuracy and completeness
deanq Jan 5, 2026
1c6d99d
docs: add resource config drift detection documentation
deanq Jan 5, 2026
f719c73
docs: proper name for the file
deanq Jan 5, 2026
2a2a21d
test(build): Add comprehensive test coverage for scanner and handler …
deanq Jan 6, 2026
17d338a
Merge branch 'deanq/ae-1251-handler-mapper' into deanq/ae-1102-load-b…
deanq Jan 6, 2026
5ead8e7
Merge branch 'deanq/ae-1102-load-balancer-sls-resource' into deanq/ae…
deanq Jan 6, 2026
6d3ff3b
test(scanner): Fix resource type assertions to match scanner behavior
deanq Jan 6, 2026
8fe3d67
Merge branch 'deanq/ae-1102-load-balancer-sls-resource' into deanq/ae…
deanq Jan 6, 2026
2640b98
Merge branch 'main' into deanq/ae-1196-absolute-drift-detection
deanq Jan 8, 2026
6431b62
chore: merge correction
deanq Jan 8, 2026
1c31455
fix(drift): Remove manual undeploy/deploy from update() method
deanq Jan 9, 2026
426ba16
docs(drift): Clarify _has_structural_changes detects version-triggeri…
deanq Jan 9, 2026
42382af
feat(drift): Enable environment variable drift detection
deanq Jan 9, 2026
d02d8c8
test(drift): Update tests for environment variable drift detection
deanq Jan 9, 2026
9ea43f2
Merge branch 'main' into deanq/ae-1196-absolute-drift-detection
deanq Jan 9, 2026
c8bab65
fix: Address Copilot review feedback on type hints and documentation
deanq Jan 9, 2026
b19bf7c
feat(mothership): implement auto-provisioning with State Manager reco…
deanq Jan 9, 2026
1f9b573
docs: fix Cross_Endpoint_Routing terminology (Directory → Manifest)
deanq Jan 9, 2026
1c07a3d
fix: correct endpoint and exception references (Directory → Manifest)
deanq Jan 9, 2026
9723e1a
Merge branch 'main' into deanq/ae-1660-mothership-deploys-manifest
deanq Jan 12, 2026
7abff9d
Merge branch 'main' into deanq/ae-1660-mothership-deploys-manifest
deanq Jan 12, 2026
a1dc695
Merge branch 'deanq/ae-1660-mothership-deploys-manifest' of https://g…
deanq Jan 12, 2026
abc01b8
Merge branch 'main' into deanq/ae-1660-mothership-deploys-manifest
deanq Jan 12, 2026
2eff912
Merge branch 'deanq/ae-1660-mothership-deploys-manifest' of https://g…
deanq Jan 12, 2026
239981c
feat(runtime): Migrate from URL to ID-based mothership identification
deanq Jan 13, 2026
c58e72f
feat(provisioner): Support all resource types and add cache validation
deanq Jan 13, 2026
6d833e2
feat(build): Add local tetra_rp bundling and manifest endpoint improv…
deanq Jan 13, 2026
2d0ccd5
feat(cli): Add undeploy force flag and improve discovery logging
deanq Jan 13, 2026
2f0120c
test: Update tests for new provisioning behavior and ID-based config
deanq Jan 13, 2026
99946c5
fix(build): Use importlib for LB handler imports to support numeric d…
deanq Jan 14, 2026
3534679
feat(build): Store config variable names in manifest for test-mothership
deanq Jan 14, 2026
b090987
fix: Address PR review comments for security and error handling
deanq Jan 14, 2026
36f72e8
Merge branch 'main' into deanq/ae-1660-mothership-deploys-manifest
deanq Jan 14, 2026
fe44047
Merge remote-tracking branch 'origin/main' into deanq/ae-1660-mothers…
deanq Jan 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
373 changes: 317 additions & 56 deletions docs/Cross_Endpoint_Routing.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies = [
"typer>=0.12.0",
"questionary>=2.0.0",
"pathspec>=0.11.0",
"tomli>=2.0.0; python_version < '3.11'",
]

[dependency-groups]
Expand Down
176 changes: 172 additions & 4 deletions src/tetra_rp/cli/commands/build.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Flash build command - Package Flash applications for deployment."""

import ast
import importlib.util
import json
import logging
import re
Expand All @@ -9,13 +10,19 @@
import sys
import tarfile
from pathlib import Path
from typing import Optional

import typer
from rich.console import Console
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn
from rich.table import Table

try:
import tomllib # Python 3.11+
except ImportError:
import tomli as tomllib # Python 3.9-3.10

from ..utils.ignore import get_file_tree, load_ignore_patterns
from .build_utils.handler_generator import HandlerGenerator
from .build_utils.lb_handler_generator import LBHandlerGenerator
Expand Down Expand Up @@ -52,6 +59,132 @@
PIP_MODULE = "pip"


def _find_local_tetra_rp() -> Optional[Path]:
"""Find local tetra_rp source directory if available.

Returns:
Path to tetra_rp package directory, or None if not found or installed from PyPI
"""
try:
spec = importlib.util.find_spec("tetra_rp")

if not spec or not spec.origin:
return None

# Get package directory (spec.origin is __init__.py path)
pkg_dir = Path(spec.origin).parent

# Skip if installed in site-packages (PyPI install)
if "site-packages" in str(pkg_dir):
return None

# Must be development install
return pkg_dir

except Exception:
return None


def _bundle_local_tetra_rp(build_dir: Path) -> bool:
"""Copy local tetra_rp source into build directory.

Args:
build_dir: Target build directory

Returns:
True if bundled successfully, False otherwise
"""
tetra_pkg = _find_local_tetra_rp()

if not tetra_pkg:
console.print(
"[yellow]⚠ Local tetra_rp not found or using PyPI install[/yellow]"
)
return False

# Copy tetra_rp to build
dest = build_dir / "tetra_rp"
if dest.exists():
shutil.rmtree(dest)

shutil.copytree(
tetra_pkg,
dest,
ignore=shutil.ignore_patterns("__pycache__", "*.pyc", ".pytest_cache"),
)

console.print(f"[cyan]✓ Bundled local tetra_rp from {tetra_pkg}[/cyan]")
return True


def _extract_tetra_rp_dependencies(tetra_pkg_dir: Path) -> list[str]:
"""Extract runtime dependencies from tetra_rp's pyproject.toml.

When bundling local tetra_rp source, we need to also install its dependencies
so they're available in the build environment.

Args:
tetra_pkg_dir: Path to tetra_rp package directory (src/tetra_rp)

Returns:
List of dependency strings, empty list if parsing fails
"""
try:
# Navigate from tetra_rp package to project root
# tetra_pkg_dir is src/tetra_rp, need to go up 2 levels to reach project root
project_root = tetra_pkg_dir.parent.parent
pyproject_path = project_root / "pyproject.toml"

if not pyproject_path.exists():
console.print(
"[yellow]⚠ tetra_rp pyproject.toml not found, "
"dependencies may be missing[/yellow]"
)
return []

# Parse TOML
with open(pyproject_path, "rb") as f:
data = tomllib.load(f)

# Extract dependencies from [project.dependencies]
dependencies = data.get("project", {}).get("dependencies", [])

if dependencies:
console.print(
f"[dim]Found {len(dependencies)} tetra_rp dependencies to install[/dim]"
)

return dependencies

except Exception as e:
console.print(f"[yellow]⚠ Failed to parse tetra_rp dependencies: {e}[/yellow]")
return []


def _remove_tetra_from_requirements(build_dir: Path) -> None:
"""Remove tetra_rp from requirements.txt and clean up dist-info since we bundled source."""
req_file = build_dir / "requirements.txt"

if not req_file.exists():
return

lines = req_file.read_text().splitlines()
filtered = [
line
for line in lines
if not line.strip().startswith("tetra_rp")
and not line.strip().startswith("tetra-rp")
]

req_file.write_text("\n".join(filtered) + "\n")

# Remove tetra_rp dist-info directory to avoid conflicts with bundled source
# dist-info is created by pip install and can confuse Python's import system
for dist_info in build_dir.glob("tetra_rp-*.dist-info"):
if dist_info.is_dir():
shutil.rmtree(dist_info)


def build_command(
no_deps: bool = typer.Option(
False, "--no-deps", help="Skip transitive dependencies during pip install"
Expand All @@ -67,6 +200,11 @@ def build_command(
"--exclude",
help="Comma-separated packages to exclude (e.g., 'torch,torchvision')",
),
use_local_tetra: bool = typer.Option(
False,
"--use-local-tetra",
help="Bundle local tetra_rp source instead of PyPI version (for development/testing)",
),
):
"""
Build Flash application for deployment.
Expand All @@ -90,6 +228,9 @@ def build_command(
console.print("Run [bold]flash init[/bold] to create a Flash project")
raise typer.Exit(1)

# Create build directory first to ensure clean state before collecting files
build_dir = create_build_directory(project_dir, app_name)

# Parse exclusions
excluded_packages = []
if exclude:
Expand Down Expand Up @@ -121,9 +262,8 @@ def build_command(
)
progress.stop_task(collect_task)

# Create build directory
# Note: build directory already created before progress tracking
build_task = progress.add_task("Creating build directory...")
build_dir = create_build_directory(project_dir, app_name)
progress.update(
build_task,
description="[green]✓ Created .flash/.build/",
Expand Down Expand Up @@ -156,15 +296,16 @@ def build_command(
handler_paths = []

# Separate resources by type
# Use flag determined by isinstance() at scan time
lb_resources = {
name: data
for name, data in manifest.get("resources", {}).items()
if data.get("resource_type") == "LoadBalancerSlsResource"
if data.get("is_load_balanced", False)
}
qb_resources = {
name: data
for name, data in manifest.get("resources", {}).items()
if data.get("resource_type") != "LoadBalancerSlsResource"
if not data.get("is_load_balanced", False)
}

# Generate LB handlers
Expand Down Expand Up @@ -219,10 +360,21 @@ def build_command(
logger.exception("Build failed")
raise typer.Exit(1)

# Extract tetra_rp dependencies if bundling local version
tetra_deps = []
if use_local_tetra:
tetra_pkg = _find_local_tetra_rp()
if tetra_pkg:
tetra_deps = _extract_tetra_rp_dependencies(tetra_pkg)

# Install dependencies
deps_task = progress.add_task("Installing dependencies...")
requirements = collect_requirements(project_dir, build_dir)

# Add tetra_rp dependencies if bundling local version
# This ensures all tetra_rp runtime dependencies are available in the build
requirements.extend(tetra_deps)

# Filter out excluded packages
if excluded_packages:
original_count = len(requirements)
Expand Down Expand Up @@ -280,6 +432,22 @@ def build_command(

progress.stop_task(deps_task)

# Bundle local tetra_rp if requested
if use_local_tetra:
tetra_task = progress.add_task("Bundling local tetra_rp...")
if _bundle_local_tetra_rp(build_dir):
_remove_tetra_from_requirements(build_dir)
progress.update(
tetra_task,
description="[green]✓ Bundled local tetra_rp",
)
else:
progress.update(
tetra_task,
description="[yellow]⚠ Using PyPI tetra_rp",
)
progress.stop_task(tetra_task)

# Clean up Python bytecode before archiving
cleanup_python_bytecode(build_dir)

Expand Down
11 changes: 6 additions & 5 deletions src/tetra_rp/cli/commands/build_utils/handler_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,13 @@ def generate_handlers(self) -> List[Path]:

for resource_name, resource_data in resources.items():
# Skip load-balanced resources (handled by LBHandlerGenerator)
resource_type = (
resource_data.resource_type
if hasattr(resource_data, "resource_type")
else resource_data.get("resource_type")
# Use flag determined by isinstance() at scan time
is_load_balanced = (
resource_data.is_load_balanced
if hasattr(resource_data, "is_load_balanced")
else resource_data.get("is_load_balanced", False)
)
if resource_type == "LoadBalancerSlsResource":
if is_load_balanced:
continue

handler_path = self._generate_handler(resource_name, resource_data)
Expand Down
Loading
Loading