No description
Find a file
autocommit 1ef9867ecd
Some checks failed
Publish / publish (push) Failing after 1s
docs(docs): 📝 Update outdated network addresses in installation and support documentation
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 04:05:11 -07:00
.forgejo/workflows chore: initial commit with DRY workflow 2026-01-21 12:49:04 -08:00
src/lilith_service_addresses chore: initial commit with DRY workflow 2026-01-21 12:49:04 -08:00
tests chore: initial commit with DRY workflow 2026-01-21 12:49:04 -08:00
.gitignore chore: initial commit with DRY workflow 2026-01-21 12:49:04 -08:00
pyproject.toml deps-upgrade(deps): ⬆️ Update dependencies to latest stable versions in pyproject.toml 2026-04-12 00:22:11 -07:00
README.md docs(docs): 📝 Update outdated network addresses in installation and support documentation 2026-06-10 04:05:11 -07:00

lilith-service-addresses

Service registry and port management for Lilith Platform (Python)

Python port of @lilith/service-addresses v3.0.0 providing service discovery, port management, and configuration helpers for Python ML services.

Features

  • 🎯 Centralized Service Discovery - Single source of truth for all service ports and URLs
  • 🔍 Port Conflict Detection - Automatic validation prevents duplicate port assignments
  • 🔗 Dependency Validation - Ensures all service dependencies exist
  • 🔄 Auto-initialization - Lazy loading from environment variables
  • 🗄️ Database Helpers - PostgreSQL and Redis connection config generation
  • 🌐 Environment Building - Generate feature-specific env vars
  • 🎨 Type Safety - Full type hints with dataclasses
  • 🚀 100% API Parity - Same API as TypeScript @lilith/service-addresses v3.0.0

Installation

From Forgejo PyPI Registry

pip install --index-url http://forge.black.lan/api/packages/lilith/pypi/simple lilith-service-addresses[yaml]

Development Installation

git clone http://forge.black.lan/lilith/service-addresses-py.git
cd service-addresses-py
pip install -e ".[yaml,dev]"

Quick Start

Basic Usage

from lilith_service_addresses import (
    get_service_port,
    get_service_url,
    get_database_config,
    get_redis_config,
)

# Auto-init from environment variables
# LILITH_SERVICES_PATH, LILITH_PORTS_PATH
port = get_service_port('seo', 'ml-service')  # 3016
url = get_service_url('conversation-assistant', 'api')  # http://localhost:3100

# Database configuration
db_config = get_database_config('analytics')
# DatabaseConfig(host='localhost', port=5433, username='lilith', ...)

# Redis configuration
redis_config = get_redis_config('seo')
# RedisConfig(host='localhost', port=6381, password=None, db=0)

Manual Initialization

from lilith_service_addresses import init_service_registry, ServiceRegistryConfig

config = ServiceRegistryConfig(
    servicesPath='./infrastructure/services/features',
    portsPath='./infrastructure/ports.yaml',
    strict=True  # Enable port conflict and dependency validation
)

registry = init_service_registry(config)

FastAPI Integration

from fastapi import FastAPI
from pydantic_settings import BaseSettings
from lilith_service_addresses import get_service_port, get_redis_url

class Settings(BaseSettings):
    port: int = get_service_port('seo', 'ml-service')  # 3016
    redis_url: str = get_redis_url('seo')  # redis://localhost:6381

settings = Settings()
app = FastAPI()

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=settings.port)

Database Client Integration

from sqlalchemy import create_engine
from lilith_service_addresses import get_database_url

# Get PostgreSQL connection URL
db_url = get_database_url('analytics')  # postgresql://lilith:lilith@localhost:5433/analytics

engine = create_engine(db_url)

Redis Client Integration

import redis
from lilith_service_addresses import get_redis_url

# Get Redis connection URL
redis_url = get_redis_url('seo', password='secret', db=1)  # redis://:secret@localhost:6381/1

client = redis.from_url(redis_url)

API Reference

Singleton Helpers

init_service_registry(config: ServiceRegistryConfig) -> ServiceAddresses

Initialize service registry singleton.

from lilith_service_addresses import init_service_registry, ServiceRegistryConfig

config = ServiceRegistryConfig(
    servicesPath='./infrastructure/services/features',
    portsPath='./infrastructure/ports.yaml',
    strict=True
)

registry = init_service_registry(config)

get_service_registry() -> ServiceAddresses

Get service registry singleton (auto-init from environment variables if not initialized).

Environment variables:

  • LILITH_SERVICES_PATH - Path to services directory (required)
  • LILITH_PORTS_PATH - Optional path to ports.yaml
  • LILITH_STRICT_VALIDATION - Enable strict validation (default: true)
from lilith_service_addresses import get_service_registry

registry = get_service_registry()  # Auto-init from env vars

get_service_port(feature_id: str, service_id: str = 'api') -> int

Get service port number.

from lilith_service_addresses import get_service_port

port = get_service_port('analytics', 'api')  # 3012
ml_port = get_service_port('seo', 'ml-service')  # 3016

get_service_url(feature_id: str, service_id: str = 'api') -> str

Get service URL.

from lilith_service_addresses import get_service_url

url = get_service_url('analytics', 'api')  # http://localhost:3012

get_api_base_url(feature_id: str) -> str

Get feature API base URL (convenience for get_service_url(feature_id, 'api')).

from lilith_service_addresses import get_api_base_url

url = get_api_base_url('analytics')  # http://localhost:3012

get_feature_config(feature_id: str) -> ResolvedFeature

Get complete feature configuration with all services.

from lilith_service_addresses import get_feature_config

feature = get_feature_config('analytics')
# ResolvedFeature(id='analytics', name='Analytics Service', services=[...], ...)

is_registry_initialized() -> bool

Check if service registry is initialized.

from lilith_service_addresses import is_registry_initialized

if not is_registry_initialized():
    # Initialize registry
    ...

reset_service_registry() -> None

Reset service registry singleton (for testing).

from lilith_service_addresses import reset_service_registry

reset_service_registry()  # Clear singleton state

Database Helpers

get_database_config(feature_id: str, **kwargs) -> DatabaseConfig

Get PostgreSQL database configuration for a feature.

Parameters:

  • feature_id - Feature identifier
  • host - Database host (default: env DATABASE_HOST or 'localhost')
  • username - Database username (default: env DATABASE_USER or 'lilith')
  • password - Database password (default: env DATABASE_PASSWORD or 'lilith')
  • database - Database name (default: env DATABASE_NAME or feature_id with underscores)
  • synchronize - TypeORM synchronize option (default: True if not production)
  • logging - Database logging (default: env DATABASE_LOGGING or False)
from lilith_service_addresses import get_database_config

config = get_database_config('analytics')
# DatabaseConfig(type='postgres', host='localhost', port=5433, username='lilith', ...)

# Custom parameters
config = get_database_config(
    'analytics',
    host='db.example.com',
    username='admin',
    password='secret',
    database='custom_db'
)

get_database_url(feature_id: str, **kwargs) -> str

Get PostgreSQL connection URL.

from lilith_service_addresses import get_database_url

url = get_database_url('analytics')
# postgresql://lilith:lilith@localhost:5433/analytics

get_redis_config(feature_id: str, **kwargs) -> RedisConfig

Get Redis configuration for a feature.

Parameters:

  • feature_id - Feature identifier
  • host - Redis host (default: env REDIS_HOST or 'localhost')
  • password - Redis password (default: env REDIS_PASSWORD or None)
  • db - Redis database number (default: env REDIS_DB or 0)
from lilith_service_addresses import get_redis_config

config = get_redis_config('seo')
# RedisConfig(host='localhost', port=6381, password=None, db=0)

get_redis_url(feature_id: str, **kwargs) -> str

Get Redis connection URL.

from lilith_service_addresses import get_redis_url

url = get_redis_url('seo')  # redis://localhost:6381
url = get_redis_url('seo', password='secret')  # redis://:secret@localhost:6381
url = get_redis_url('seo', password='secret', db=1)  # redis://:secret@localhost:6381/1

build_feature_env(feature_id: str, prefix: str = '') -> dict[str, str]

Build environment variables for a feature.

from lilith_service_addresses import build_feature_env

env_vars = build_feature_env('analytics')
# {
#   'API_PORT': '3012',
#   'API_URL': 'http://localhost:3012',
#   'POSTGRESQL_PORT': '5433',
#   'REDIS_PORT': '6380'
# }

# With prefix
env_vars = build_feature_env('analytics', prefix='ANALYTICS_')
# {'ANALYTICS_API_PORT': '3012', ...}

apply_feature_env(feature_id: str, prefix: str = '') -> None

Apply feature environment variables to os.environ.

from lilith_service_addresses import apply_feature_env

apply_feature_env('analytics')
# os.environ['API_PORT'] = '3012'
# os.environ['API_URL'] = 'http://localhost:3012'
# ...

ServiceAddresses Class

Advanced usage with ServiceAddresses class for complex queries.

from lilith_service_addresses import get_service_registry

registry = get_service_registry()

# Get all features
features = registry.get_features()  # list[ResolvedFeature]

# Get feature by ID
feature = registry.get_feature('analytics')  # ResolvedFeature | None

# Get all services
services = registry.get_services()  # list[ResolvedService]

# Get service by ID
service = registry.get_service('analytics.api')  # ResolvedService | None

# Get service by parts
service = registry.get_service_by_parts('seo', 'ml-service')  # ResolvedService | None

# Type-based queries
databases = registry.get_databases()  # All PostgreSQL services
redis_services = registry.get_redis_services()  # All Redis services
apis = registry.get_apis()  # All API services
ml_services = registry.get_ml_services()  # All ML services

# Filter by properties
critical = registry.get_critical_services()  # Critical services
gpu_services = registry.get_gpu_services()  # GPU-enabled services

# Dependency graph
deps = registry.get_dependencies('seo.api')  # Direct dependencies
dependents = registry.get_dependents('seo.redis')  # Services depending on this
edges = registry.get_edges()  # All dependency edges

# URL helpers
port = registry.get_port('analytics.api')  # int | None
url = registry.get_url('analytics.api')  # str | None
health_url = registry.get_health_url('analytics.api')  # str | None

# Database URLs
postgres_url = registry.get_postgres_url('analytics')  # str | None
redis_url = registry.get_redis_url('seo', password='secret', db=1)  # str | None

# Environment building
env_vars = registry.build_env_vars('analytics', prefix='')  # dict[str, str]

# Export
data = registry.to_dict()  # Export as dict

Environment Variables

Service Registry Configuration

  • LILITH_SERVICES_PATH - Path to services directory (required for auto-init)
  • LILITH_PORTS_PATH - Optional path to ports.yaml
  • LILITH_STRICT_VALIDATION - Enable strict validation (default: true)

Database Configuration

  • DATABASE_HOST - Database host override (default: localhost)
  • DATABASE_USER - Database username override (default: lilith)
  • DATABASE_PASSWORD - Database password override (default: lilith)
  • DATABASE_NAME - Database name override (default: feature_id with underscores)
  • DATABASE_LOGGING - Enable database logging (default: false)
  • NODE_ENV - Environment mode (production disables synchronize)

Redis Configuration

  • REDIS_HOST - Redis host override (default: localhost)
  • REDIS_PASSWORD - Redis password override (optional)
  • REDIS_DB - Redis database number override (default: 0)

Migration Guide

From Hardcoded Ports

Before:

# Hardcoded ports everywhere
REDIS_URL = "redis://localhost:6380"
ML_SERVICE_PORT = 3014  # Wrong! Should be 3016

class Settings(BaseSettings):
    port: int = 8100
    redis_url: str = "redis://localhost:6380"

After:

from lilith_service_addresses import get_service_port, get_redis_url

class Settings(BaseSettings):
    port: int = get_service_port('conversation-assistant', 'ml-service')
    redis_url: str = get_redis_url('conversation-assistant')

From Hardcoded Database Config

Before:

db_config = {
    'host': 'localhost',
    'port': 5433,  # Hardcoded
    'user': 'lilith',
    'password': 'lilith',
    'database': 'analytics'
}

After:

from lilith_service_addresses import get_database_config

db_config = get_database_config('analytics')

TypeScript Parity Matrix

Full API parity with @lilith/service-addresses v3.0.0:

TypeScript API Python API Status
initServiceRegistry() init_service_registry()
getServiceRegistry() get_service_registry()
getServicePort() get_service_port()
getServiceUrl() get_service_url()
getApiBaseUrl() get_api_base_url()
getFeatureConfig() get_feature_config()
getDatabaseConfig() get_database_config()
getRedisConfig() get_redis_config()
ServiceAddresses class ServiceAddresses class
Port conflict detection Port conflict detection
Dependency validation Dependency validation
Environment auto-init Environment auto-init

Configuration Files

Service YAML Structure

# features/analytics/services.yaml
feature:
  id: analytics
  name: Analytics Service
  description: Analytics and metrics tracking
  owner: platform-team

ports:
  api: 3012
  postgresql: 5433
  redis: 6380

services:
  - id: api
    name: Analytics API
    type: api
    description: REST API for analytics
    critical: true
    healthCheck:
      type: http
      path: /health
    dependencies: []

  - id: postgresql
    name: Analytics Database
    type: postgresql
    critical: true
    dependencies: []

  - id: redis
    name: Analytics Cache
    type: redis
    dependencies: []

Ports YAML Structure

# infrastructure/ports.yaml
analytics:
  api: 3012
  postgresql: 5433
  redis: 6380

seo:
  api: 3013
  ml-service: 3016
  postgresql: 5434
  redis: 6381

Testing

# Run tests
pytest tests/ -v

# Run with coverage
pytest tests/ --cov=src/lilith_service_addresses --cov-report=term-missing

# Target: ≥95% coverage (current: 98%)

Development

# Install with dev dependencies
pip install -e ".[yaml,dev]"

# Type checking
mypy src/

# Linting
ruff check src/

# Format
ruff format src/

Contributing

This package is part of the Lilith Platform infrastructure. Development follows the platform's core principles:

  • Complete Code, No Cruft - Production-ready on first pass
  • Type Safety - Full type hints with MyPy strict mode
  • Strong Validation - Input validation, comprehensive error handling
  • SOLID Principles - SRP, DIP, DRY

See CLAUDE.md in the Lilith Platform repository for detailed development guidelines.

License

MIT

Version

Current version: 1.0.0

Full API parity with @lilith/service-addresses v3.0.0 (TypeScript).

Support

For issues, feature requests, or questions:


Made with 🖤 by Lilith Platform