Ctrl K
ComunidadGuías…

Makefile

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Phoenix Monorepo Makefile
# Unified commands for Python and TypeScript development

SHELL := /bin/bash
.DEFAULT_GOAL := help

# Tools
PNPM := pnpm
UV := $(CURDIR)/scripts/uv.sh
UV_BIN := uv
NODE := node

# Directories
APP_DIR := js/app
JS_DIR := js
SCHEMAS_DIR := schemas
PACKAGES_DIR := packages
PHOENIX_CLIENT_GENERATED := packages/phoenix-client/src/phoenix/client/__generated__
GH_COMMENT_WATCH_DIR := scripts/gh-comment-watch

# Colors for output
CYAN := \033[0;36m
GREEN := \033[0;32m
YELLOW := \033[0;33m
RED := \033[0;31m
NC := \033[0m # No Color

#=============================================================================
# Meta Targets
#=============================================================================

.PHONY: help check-tools \
	setup setup-remote-export install-python install-node \
	graphql schema-graphql relay-build \
	openapi schema-openapi schema-generative-ui ui-message-stream-fixtures codegen-python-client codegen-ts-client codegen-ts-app codegen-harbor-graphql \
	dev dev-backend dev-frontend dev-docker dev-mock-llm \
	test test-python test-frontend test-ts test-helm test-jcs doctest typecheck typecheck-python typecheck-python-ty typecheck-frontend typecheck-ts \
	format format-python format-frontend format-ts lint lint-python lint-frontend lint-ts clean-notebooks \
	build build-python build-frontend build-ts \
	skills skills-check codegen-prompts sync-models test-sync-models schema-ddl check-graphql-permissions check-filter-dsl-snippets check-skill-graphql-examples check-skill-filter-examples check-skill-python-examples check-skill-typescript-examples check-skill-px-commands sync-docs-snippets check-docs-snippets gen-otel-models \
	test-integration-docs \
	gh-comment-watch \
	harbor-stage harbor-plugin-e2e harbor-run harbor-view \
	clean clean-all

help: ## Show this help message
	@echo -e ""
	@echo -e "$(CYAN)Phoenix Monorepo - Available Make Targets$(NC)"
	@echo -e ""
	@echo -e "$(GREEN)Schema Generation:$(NC)"
	@echo -e "  $(YELLOW)graphql$(NC)               - Generate GraphQL schema and build Relay (Python → TypeScript)"
	@echo -e "  $(YELLOW)openapi$(NC)               - Generate OpenAPI schema and clients (Python + TypeScript)"
	@echo -e "  schema-graphql         - Generate GraphQL schema only"
	@echo -e "  relay-build            - Build Relay from existing schema"
	@echo -e "  schema-openapi         - Generate OpenAPI schema only"
	@echo -e "  schema-generative-ui   - Generate Generative UI catalog schema artifacts"
	@echo -e "  ui-message-stream-fixtures - Generate AI SDK reducer conformance fixtures"
	@echo -e "  codegen-python-client  - Generate Python client types from OpenAPI"
	@echo -e "  codegen-ts-client      - Generate TypeScript client types from OpenAPI"
	@echo -e "  codegen-ts-app         - Generate TypeScript OpenAPI types for frontend (js/app/)"
	@echo -e "  codegen-harbor-graphql - Compile the Harbor verifiers' GraphQL queries into typed Python models"
	@echo -e "  skills                 - Build every shipped copy of the public skills in skills/"
	@echo -e "  skills-check           - Ensure the skill copies are current and npx offers only skills/"
	@echo -e ""
	@echo -e "$(GREEN)Setup:$(NC)"
	@echo -e "  $(YELLOW)setup$(NC)                 - Complete development environment setup"
	@echo -e "  setup-remote-export   - Configure PXI remote trace export"
	@echo -e "  check-tools            - Verify required tools are installed"
	@echo -e "  install-python         - Install Python dependencies"
	@echo -e "  install-node           - Install Node.js dependencies"
	@echo -e ""
	@echo -e "$(GREEN)Development:$(NC)"
	@echo -e "  $(YELLOW)dev$(NC)                   - Full dev environment (backend + frontend)"
	@echo -e "  dev-backend            - Backend only (FastAPI server)"
	@echo -e "  dev-frontend           - Frontend only (React dev server)"
	@echo -e "  dev-docker             - Docker devops environment (use ARGS= for arguments)"
	@echo -e "  dev-mock-llm           - Start the mock LLM server"
	@echo -e ""
	@echo -e "$(GREEN)Testing:$(NC)"
	@echo -e "  $(YELLOW)test$(NC)                  - Run all tests (Python + TypeScript workspace)"
	@echo -e "  test-python            - Run Python tests (unit + integration)"
	@echo -e "  test-frontend          - Run frontend tests only (js/app/)"
	@echo -e "  test-ts                - Run all TypeScript tests (js/ workspace)"
	@echo -e "  test-helm              - Run Helm chart tests"
	@echo -e "  test-integration-docs  - Run configured integration docs against a running Phoenix server"
	@echo -e "  doctest                - Run doctests across all modules in src/ (override with MODULES=...)"
	@echo -e "  typecheck              - Type check all code (Python + TypeScript workspace)"
	@echo -e "  typecheck-python       - Type check Python only"
	@echo -e "  typecheck-python-ty    - Type check Python with ty (verify expected errors only)"
	@echo -e "  typecheck-frontend     - Type check frontend only (js/app/)"
	@echo -e "  typecheck-ts           - Type check all TypeScript (js/ workspace)"
	@echo -e ""
	@echo -e "$(GREEN)Code Quality:$(NC)"
	@echo -e "  $(YELLOW)format$(NC)                - Format all code (Python + TypeScript workspace)"
	@echo -e "  format-python          - Format Python with ruff"
	@echo -e "  format-frontend        - Format frontend only (js/app/)"
	@echo -e "  format-ts              - Format all TypeScript (js/ workspace)"
	@echo -e "  clean-notebooks        - Clean Jupyter notebook metadata"
	@echo -e "  $(YELLOW)lint$(NC)                  - Lint all code (Python + TypeScript workspace)"
	@echo -e "  lint-python            - Lint Python with ruff"
	@echo -e "  lint-frontend          - Lint frontend only (js/app/)"
	@echo -e "  lint-ts                - Lint all TypeScript (js/ workspace)"
	@echo -e "  check-graphql-permissions - Ensure GraphQL mutations have permission classes"
	@echo -e "  check-filter-dsl-snippets - Ensure UI filter DSL snippets compile under the Python filters"
	@echo -e "  check-skill-graphql-examples - Ensure GraphQL examples in shipped skills validate against the schema"
	@echo -e "  check-skill-filter-examples - Ensure filter conditions in shipped skills compile under the Python filters"
	@echo -e "  check-skill-python-examples - Ensure Python examples in public skills type-check against the client packages"
	@echo -e "  check-skill-typescript-examples - Ensure TypeScript examples in public skills type-check against the client packages"
	@echo -e "  check-skill-px-commands - Ensure px commands in public skills match the CLI's commands and flags"
	@echo -e "  sync-docs-snippets     - Copy examples/quickstarts code into the docs' fenced code blocks"
	@echo -e "  check-docs-snippets    - Ensure docs' fenced code blocks match examples/quickstarts"
	@echo -e ""
	@echo -e "$(GREEN)Utilities:$(NC)"
	@echo -e "  codegen-prompts        - Compile YAML prompts to Python and TypeScript"
	@echo -e "  sync-models            - Sync model cost manifest from remote sources"
	@echo -e "  test-sync-models       - Test the model cost manifest sync script"
	@echo -e "  schema-ddl             - Compile DDL schema from PostgreSQL and SQLite (use ARGS=/SQLITE_ARGS= for arguments)"
	@echo -e "  gen-otel-models        - Generate OTel GenAI semconv Pydantic models"
	@echo -e "  gh-comment-watch       - Start the GitHub comment watcher"
	@echo -e ""
	@echo -e "$(GREEN)Harbor Evals:$(NC)"
	@echo -e "  $(YELLOW)harbor-stage$(NC)             - Build the Phoenix and client wheels, produce each fixture, stage each task environment, and build the px CLI archive (HF_TOKEN=... for the TRAIL fixture, RESEED=1, HARBOR_CLI=0 to skip the archive)"
	@echo -e "  $(YELLOW)harbor-plugin-e2e$(NC)       - Manually run the credentialed Harbor plugin E2E matrix"
	@echo -e "  $(YELLOW)harbor-run$(NC)               - Run a Harbor job file with the Phoenix plugin (HARBOR_JOB=..., HARBOR_ARGS=...)"
	@echo -e "  harbor-view               - Browse Harbor job results in a local web viewer"
	@echo -e ""
	@echo -e "$(GREEN)Build:$(NC)"
	@echo -e "  $(YELLOW)build$(NC)                 - Build everything (Python + TypeScript workspace)"
	@echo -e "  build-python           - Build Python package"
	@echo -e "  build-frontend         - Build frontend only (js/app/ and its deps)"
	@echo -e "  build-ts               - Build all TypeScript (js/ workspace)"
	@echo -e ""
	@echo -e "$(GREEN)Cleanup:$(NC)"
	@echo -e "  clean                  - Clean build artifacts"
	@echo -e "  clean-all              - Clean everything including node_modules"
	@echo -e ""
	@echo -e "Highlighted targets are the most commonly used."
	@echo -e ""

#=============================================================================
# Tool Checks
#=============================================================================

check-tools: ## Verify required tools are installed
	@echo -e "$(CYAN)Checking required tools...$(NC)"
	@command -v $(UV_BIN) >/dev/null 2>&1 || { echo -e "$(RED)ERROR: uv is not installed. Install from https://github.com/astral-sh/uv$(NC)"; exit 1; }
	@echo -e "$(GREEN)✓$(NC) uv found: $$($(UV) --version)"
	@command -v $(PNPM) >/dev/null 2>&1 || { echo -e "$(RED)ERROR: pnpm is not installed. Run: npm install -g pnpm$(NC)"; exit 1; }
	@echo -e "$(GREEN)✓$(NC) pnpm found: $$($(PNPM) --version)"
	@command -v $(NODE) >/dev/null 2>&1 || { echo -e "$(RED)ERROR: node is not installed. Install from https://nodejs.org$(NC)"; exit 1; }
	@echo -e "$(GREEN)✓$(NC) node found: $$($(NODE) --version)"
	@command -v dekit >/dev/null 2>&1 && echo -e "$(GREEN)✓$(NC) dekit found: $$(dekit --version)" || echo -e "$(YELLOW)WARNING: dekit is not installed (needed by make dev). Run: curl -fsSL https://dekit.run/install.sh | sh$(NC)"
	@echo -e "$(GREEN)All required tools are installed!$(NC)"

#=============================================================================
# Setup
#=============================================================================

install-python: ## Install Python dependencies
	@echo -e "$(CYAN)Installing Python dependencies...$(NC)"
	@$(UV) sync --python 3.11
	@echo -e "$(GREEN)✓ Done$(NC)"

install-node: ## Install Node.js dependencies
	@echo -e "$(CYAN)Installing Node.js dependencies...$(NC)"
	@cd $(JS_DIR) && $(PNPM) install --silent
	@echo -e "$(CYAN)Building workspace dependencies of the app...$(NC)"
	@cd $(APP_DIR) && $(PNPM) run --silent build:deps
	@echo -e "$(GREEN)✓ Done$(NC)"

setup: check-tools install-python install-node ## Complete development environment setup
	@echo -e ""
	@echo -e "$(GREEN)✓ Phoenix development environment setup complete!$(NC)"
	@echo -e ""
	@echo -e "Next steps:"
	@echo -e "  1. Activate Python virtualenv: source .venv/bin/activate"
	@echo -e "  2. Start development: make dev"
	@echo -e ""

setup-remote-export: ## Configure PXI remote trace export
	@$(NODE) --experimental-strip-types --disable-warning=ExperimentalWarning --disable-warning=MODULE_TYPELESS_PACKAGE_JSON $(APP_DIR)/scripts/setup-remote-export.ts

#=============================================================================
# Schema Generation
#=============================================================================

skills: ## Build every shipped copy of the public skills in skills/
	@echo -e "$(CYAN)Building skill copies from skills/...$(NC)"
	@python3 $(CURDIR)/scripts/sync_skills.py
	@echo -e "$(GREEN)✓ Done$(NC)"

skills-check: ## Ensure the skill copies are current and npx offers only skills/
	@echo -e "$(CYAN)Checking skill copies against skills/...$(NC)"
	@python3 $(CURDIR)/scripts/test_sync_skills.py
	@python3 $(CURDIR)/scripts/sync_skills.py --check --npx
	@echo -e "$(GREEN)✓ Done$(NC)"

schema-graphql: ## Generate GraphQL schema from Python
	@echo -e "$(CYAN)Generating GraphQL schema...$(NC)"
	@$(UV) run strawberry export-schema phoenix.server.api.schema:_EXPORTED_GRAPHQL_SCHEMA -o $(APP_DIR)/schema.graphql
	@echo -e "$(GREEN)✓ js/app/schema.graphql$(NC)"

relay-build: ## Build Relay from GraphQL schema
	@echo -e "$(CYAN)Building Relay GraphQL types...$(NC)"
	@cd $(APP_DIR) && $(PNPM) run --silent build:relay
	@echo -e "$(GREEN)✓ Done$(NC)"

graphql: schema-graphql relay-build ## Generate GraphQL schema and build Relay (full workflow)
	@echo -e "$(GREEN)✓ GraphQL schema workflow complete$(NC)"

schema-openapi: ## Generate OpenAPI schema from Python
	@echo -e "$(CYAN)Generating OpenAPI schema...$(NC)"
	@$(UV) run python scripts/ci/compile_openapi_schema.py -o $(SCHEMAS_DIR)/openapi.json
	@echo -e "$(GREEN)✓ schemas/openapi.json$(NC)"

# The generator requires graphql-core<3.3; keep it separate from the server.
codegen-harbor-graphql: ## Compile evals/harbor/verifiers/harbor_verifiers/graphql/operations/*.graphql into evals/harbor/verifiers/harbor_verifiers/graphql/__generated__
	@echo -e "$(CYAN)Compiling Harbor GraphQL queries against js/app/schema.graphql...$(NC)"
	@rm -rf $(CURDIR)/evals/harbor/verifiers/harbor_verifiers/graphql/__generated__
	@$(UV) tool run --from ariadne-codegen==0.19.0 ariadne-codegen
	@echo -e "$(GREEN)✓ evals/harbor/verifiers/harbor_verifiers/graphql/__generated__$(NC)"

codegen-python-client: ## Generate Python client types from OpenAPI
	@echo -e "$(CYAN)Generating Python client types...$(NC)"
	@rm -f $(PHOENIX_CLIENT_GENERATED)/v1/__init__.py
	@$(UV) run datamodel-codegen \
		--input $(CURDIR)/schemas/openapi.json \
		--input-file-type openapi \
		--output $(PHOENIX_CLIENT_GENERATED)/v1/.dataclass.py \
		--output-model-type dataclasses.dataclass \
		--collapse-root-models \
		--enum-field-as-literal all \
		--target-python-version 3.11 \
		--use-default-kwarg \
		--use-double-quotes \
		--use-generic-container-types \
		--no-use-union-operator \
		--wrap-string-literal \
		--formatters black isort \
		--disable-timestamp
	@$(UV) run python $(CURDIR)/packages/phoenix-client/scripts/codegen/transform.py $(PHOENIX_CLIENT_GENERATED)/v1
	@$(UV) run ruff format $(PHOENIX_CLIENT_GENERATED)/v1
	@$(UV) run ruff check --fix $(PHOENIX_CLIENT_GENERATED)/v1
	@echo -e "$(GREEN)✓ Done$(NC)"

codegen-ts-client: ## Generate TypeScript client types from OpenAPI
	@echo -e "$(CYAN)Generating TypeScript client types...$(NC)"
	@cd $(JS_DIR)/packages/phoenix-client && $(PNPM) run --silent generate
	@cd $(JS_DIR)/packages/phoenix-testing && $(PNPM) run --silent generate
	@echo -e "$(GREEN)✓ Done$(NC)"

codegen-ts-testing: ## Generate phoenix-testing TypeScript types from OpenAPI
	@echo -e "$(CYAN)Generating phoenix-testing TypeScript types...$(NC)"
	@cd $(JS_DIR)/packages/phoenix-testing && $(PNPM) run --silent generate
	@echo -e "$(GREEN)✓ Done$(NC)"

codegen-ts-app: ## Generate TypeScript OpenAPI types for js/app/
	@echo -e "$(CYAN)Generating TypeScript OpenAPI types for app...$(NC)"
	@cd $(APP_DIR) && $(PNPM) run --silent generate:openapi
	@echo -e "$(GREEN)✓ Done$(NC)"

schema-generative-ui: ## Generate generative UI catalog schema artifacts
	@echo -e "$(CYAN)Generating UI catalog schema artifacts...$(NC)"
	@cd $(APP_DIR) && $(PNPM) run --silent generate:generative-ui-catalog
	@echo -e "$(GREEN)✓ src/phoenix/server/generative_ui$(NC)"

ui-message-stream-fixtures: ## Generate AI SDK UI-message reducer conformance fixtures
	@echo -e "$(CYAN)Generating UI-message stream conformance fixtures...$(NC)"
	@cd $(APP_DIR) && $(PNPM) run --silent generate:ui-message-stream-fixtures
	@echo -e "$(GREEN)✓ tests/unit/server/agents/fixtures/ui_message_stream$(NC)"

openapi: schema-openapi codegen-python-client codegen-ts-client codegen-ts-testing codegen-ts-app ## Generate OpenAPI schema and all clients (full workflow)
	@echo -e "$(GREEN)✓ OpenAPI schema workflow complete$(NC)"

#=============================================================================
# Development
#=============================================================================

dev: ## Full dev environment (backend + frontend with hot reload)
	@echo -e "$(CYAN)Starting full development environment...$(NC)"
	cd $(APP_DIR) && $(PNPM) dev

dev-backend: ## Backend only (FastAPI server)
	@echo -e "$(CYAN)Starting backend server...$(NC)"
	$(UV) run phoenix serve --debug
dev-frontend: ## Frontend only (React dev server)
	@echo -e "$(CYAN)Starting frontend dev server...$(NC)"
	cd $(APP_DIR) && $(PNPM) run dev:ui

#=============================================================================
# Testing
#=============================================================================

test-integration-docs: ## Run configured integration docs against a running Phoenix server
	@$(UV) run python scripts/testing/integration_docs/run.py $(INTEGRATION_DOCS_ARGS)

test-python: ## Run Python tests (unit + integration)
	@echo -e "$(CYAN)Running Python unit tests...$(NC)"
	@$(UV) run --all-packages pytest -q -n auto tests/unit
	@echo -e "$(CYAN)Running Python integration tests...$(NC)"
	@$(UV) run pytest -q tests/integration

# Run doctests across all modules under src/ by default; override on the command
# line, e.g. `make doctest MODULES="src/phoenix/foo.py src/phoenix/bar.py"`.
DOCTEST_MODULES ?= src/phoenix

doctest: ## Run doctests across all modules in src/ (override with MODULES=...)
	@echo -e "$(CYAN)Running doctests on $(or $(MODULES),$(DOCTEST_MODULES))...$(NC)"
	@$(UV) run pytest --doctest-modules $(or $(MODULES),$(DOCTEST_MODULES))
	@echo -e "$(GREEN)✓ Doctests passed$(NC)"

test-frontend: ## Run frontend tests (js/app/)
	@echo -e "$(CYAN)Running frontend tests...$(NC)"
	@cd $(APP_DIR) && $(PNPM) test

test-ts: ## Run all TypeScript tests (js/ workspace, including the app)
	@echo -e "$(CYAN)Running TypeScript tests...$(NC)"
	@cd $(JS_DIR) && $(PNPM) run test

test: test-python test-ts ## Run all tests (Python + TypeScript workspace)
	@echo -e "$(GREEN)✓ All tests complete$(NC)"

typecheck-python: ## Type check Python code
	@echo -e "$(CYAN)Type checking Python...$(NC)"
	@$(UV) run mypy
	@echo -e "$(GREEN)✓ Type check complete$(NC)"

typecheck-python-ty: ## Type check Python with ty (verify expected errors only)
	@echo -e "$(CYAN)Type checking Python with ty...$(NC)"
	@$(UV) run python scripts/uv/type_check/type_check.py
	@echo -e "$(GREEN)✓ Done$(NC)"

typecheck-frontend: ## Type check frontend (js/app/)
	@echo -e "$(CYAN)Type checking frontend...$(NC)"
	@cd $(APP_DIR) && $(PNPM) run --silent typecheck

typecheck-ts: ## Type check all TypeScript (js/ workspace, including the app)
	@echo -e "$(CYAN)Type checking TypeScript...$(NC)"
	@cd $(JS_DIR) && $(PNPM) run --silent typecheck

typecheck: typecheck-python typecheck-ts ## Type check all code (Python + TypeScript workspace)
	@echo -e "$(GREEN)✓ Type checking complete$(NC)"

#=============================================================================
# Code Quality
#=============================================================================

format-python: ## Format Python code with ruff
	@echo -e "$(CYAN)Formatting Python code...$(NC)"
	@$(UV) run ruff format
	@echo -e "$(GREEN)✓ Done$(NC)"

format-frontend: ## Format frontend (js/app/)
	@echo -e "$(CYAN)Formatting frontend...$(NC)"
	@cd $(APP_DIR) && $(PNPM) run --silent fmt
	@echo -e "$(GREEN)✓ Done$(NC)"

format-ts: ## Format all TypeScript (js/ workspace, including the app)
	@echo -e "$(CYAN)Formatting TypeScript...$(NC)"
	@cd $(JS_DIR) && $(PNPM) run --silent fmt
	@echo -e "$(GREEN)✓ Done$(NC)"

format: format-python format-ts ## Format all code (Python + TypeScript workspace)
	@echo -e "$(GREEN)✓ Code formatting complete$(NC)"

clean-notebooks: ## Clean Jupyter notebook output and metadata
	@echo -e "$(CYAN)Cleaning Jupyter notebook metadata...$(NC)"
	@find . -type f -name "*.ipynb" \
		-not -path "*/tutorials/evals/*" \
		-exec uv run jupyter nbconvert \
			--ClearOutputPreprocessor.enabled=True \
			--ClearMetadataPreprocessor.enabled=True \
			--inplace {} +
	@echo -e "$(GREEN)✓ Done$(NC)"

lint-python: ## Lint Python code with ruff
	@echo -e "$(CYAN)Linting Python code...$(NC)"
	@$(UV) run ruff check --fix
	@echo -e "$(GREEN)✓ Done$(NC)"

lint-frontend: ## Lint frontend (js/app/)
	@echo -e "$(CYAN)Linting frontend...$(NC)"
	@cd $(APP_DIR) && $(PNPM) run --silent lint
	@echo -e "$(GREEN)✓ Done$(NC)"

lint-ts: ## Lint all TypeScript (js/ workspace, including the app)
	@echo -e "$(CYAN)Linting TypeScript...$(NC)"
	@cd $(JS_DIR) && $(PNPM) run --silent lint
	@echo -e "$(GREEN)✓ Done$(NC)"

lint: lint-python lint-ts ## Lint all code (Python + TypeScript workspace)
	@echo -e "$(GREEN)✓ Linting complete$(NC)"

#=============================================================================
# Build
#=============================================================================

build-python: ## Build Python package
	@echo -e "$(CYAN)Building Python package...$(NC)"
	@$(UV) build
	@echo -e "$(GREEN)✓ dist/$(NC)"

build-frontend: ## Build frontend for production
	@echo -e "$(CYAN)Building frontend...$(NC)"
	@cd $(JS_DIR) && $(PNPM) --filter 'phoenix-ui...' run --silent build
	@echo -e "$(GREEN)✓ Done$(NC)"

build-ts: ## Build all TypeScript (js/ workspace, including the app)
	@echo -e "$(CYAN)Building TypeScript...$(NC)"
	@cd $(JS_DIR) && $(PNPM) run --silent build
	@echo -e "$(GREEN)✓ Done$(NC)"

build: build-python build-ts ## Build everything (Python + TypeScript workspace)
	@echo -e "$(GREEN)✓ Build complete$(NC)"

#=============================================================================
# Utilities
#=============================================================================


codegen-prompts: ## Generate prompts code from YAML files
	@echo -e "$(CYAN)Generating prompts code...$(NC)"
	@$(UV) run python scripts/prompts/compile_python_prompts.py src/phoenix/__generated__/classification_evaluator_configs
	@$(UV) run python scripts/prompts/compile_python_prompts.py packages/phoenix-evals/src/phoenix/evals/__generated__/classification_evaluator_configs
	@$(UV) run ruff format src/phoenix/__generated__/classification_evaluator_configs packages/phoenix-evals/src/phoenix/evals/__generated__/classification_evaluator_configs
	@$(UV) run ruff check --fix src/phoenix/__generated__/classification_evaluator_configs packages/phoenix-evals/src/phoenix/evals/__generated__/classification_evaluator_configs
	@$(UV) run python scripts/prompts/compile_typescript_prompts.py js/packages/phoenix-evals/src/__generated__/default_templates
	@$(UV) run python -c "import shutil; from pathlib import Path; dest = Path('src/phoenix/server/api/helpers/substitutions'); dest.mkdir(exist_ok=True); shutil.copy('prompts/formatters/server.yaml', dest / 'server.yaml')"
	@echo -e "$(GREEN)✓ Done$(NC)"

sync-models: ## Sync model cost manifest from remote sources
	@echo -e "$(CYAN)Syncing model cost manifest...$(NC)"
	@$(UV) run python scripts/cost_tracking/sync_models.py
	@echo -e "$(GREEN)✓ Done$(NC)"

test-sync-models: ## Test the model cost manifest sync script
	@echo -e "$(CYAN)Testing the model cost manifest sync script...$(NC)"
	@$(UV) run pytest -q $(CURDIR)/scripts/cost_tracking
	@echo -e "$(GREEN)✓ Done$(NC)"

# ARGS=--external points the PostgreSQL extractor at a foreign database, which
# says nothing about SQLite; regenerating sqlite_schema.sql from ephemeral
# migrations would overwrite a checked-in file as a side effect. SQLITE_ARGS
# requests the SQLite step outright, so it always runs.
ifeq (,$(strip $(SQLITE_ARGS)))
SKIP_SQLITE := $(findstring --external,$(ARGS))
endif

schema-ddl: ## Compile DDL schema from PostgreSQL and SQLite databases (ARGS=/SQLITE_ARGS= pass per-database arguments)
	@echo -e "$(CYAN)Compiling DDL schema...$(NC)"
	@$(UV) pip install --strict psycopg[binary] testing.postgresql pglast ty
	@$(UV) pip install --no-sources --strict --reinstall-package arize-phoenix .
	@cd scripts/ddl && $(UV) run ty check *.py && $(UV) run pytest -q -rN test_*.py
	@cd scripts/ddl && $(UV) run python generate_ddl_postgresql.py $(ARGS)
ifeq (,$(SKIP_SQLITE))
	@cd scripts/ddl && $(UV) run python generate_ddl_sqlite.py $(SQLITE_ARGS)
else
	@echo -e "$(YELLOW)Skipping SQLite: ARGS targets an external PostgreSQL database (pass SQLITE_ARGS= to run it too)$(NC)"
endif
	@cd scripts/ddl && $(UV) run python validate_schema_assets.py --postgresql-args "$(ARGS)" --sqlite-args "$(SQLITE_ARGS)"
ifeq (,$(strip $(ARGS))$(strip $(SQLITE_ARGS)))
	@cd scripts/ddl && $(UV) run python compare_schemas.py
else
	@echo -e "$(YELLOW)Skipping cross-dialect comparison: ARGS/SQLITE_ARGS may not have written the canonical files$(NC)"
endif

check-graphql-permissions: ## Ensure GraphQL mutations and subscriptions have permission classes
	@echo -e "$(CYAN)Checking GraphQL permissions...$(NC)"
	@$(UV) run python $(CURDIR)/scripts/ci/ensure_graphql_mutations_have_permission_classes.py src/phoenix/server/api
	@echo -e "$(GREEN)✓ Done$(NC)"

check-filter-dsl-snippets: ## Ensure UI filter DSL snippets and examples compile under the Python filters
	@echo -e "$(CYAN)Checking UI filter DSL snippets against the Python filters...$(NC)"
	@$(UV) run python $(CURDIR)/scripts/ci/check_filter_dsl_snippets.py
	@echo -e "$(GREEN)✓ Done$(NC)"

check-skill-graphql-examples: ## Ensure fenced GraphQL examples in shipped skills validate against js/app/schema.graphql
	@echo -e "$(CYAN)Checking skill GraphQL examples against js/app/schema.graphql...$(NC)"
	@$(UV) run pytest -q $(CURDIR)/scripts/ci/test_skill_graphql_examples.py
	@echo -e "$(GREEN)✓ Done$(NC)"

check-skill-filter-examples: ## Ensure span/trace/session filter conditions in shipped skills compile under the Python filters
	@echo -e "$(CYAN)Checking skill filter conditions against the Python filters...$(NC)"
	@$(UV) run pytest -q $(CURDIR)/scripts/ci/test_skill_filter_dsl_examples.py
	@echo -e "$(GREEN)✓ Done$(NC)"

check-skill-python-examples: ## Ensure Python examples in public skills type-check against the workspace client packages
	@echo -e "$(CYAN)Type-checking skill Python examples with pyright...$(NC)"
	@$(UV) run pytest -q $(CURDIR)/scripts/ci/test_skill_python_examples.py $(CURDIR)/scripts/ci/test_skill_lint.py
	@echo -e "$(GREEN)✓ Done$(NC)"

check-skill-typescript-examples: ## Ensure TypeScript examples in public skills type-check against the workspace client packages (needs pnpm install in js/)
	@echo -e "$(CYAN)Building the TypeScript client packages...$(NC)"
	@cd $(JS_DIR) && $(PNPM) --filter "@arizeai/phoenix-client..." --filter "@arizeai/phoenix-evals..." --filter "@arizeai/phoenix-otel..." run build >/dev/null
	@echo -e "$(CYAN)Type-checking skill TypeScript examples with tsc...$(NC)"
	@$(UV) run pytest -q $(CURDIR)/scripts/ci/test_skill_typescript_examples.py
	@echo -e "$(GREEN)✓ Done$(NC)"

check-skill-px-commands: ## Ensure px commands in public skills match the CLI's commands and flags (needs pnpm install in js/)
	@echo -e "$(CYAN)Building the px CLI's workspace dependencies...$(NC)"
	@cd $(JS_DIR) && $(PNPM) --filter "@arizeai/phoenix-cli^..." run build >/dev/null
	@echo -e "$(CYAN)Checking skill px commands against the CLI...$(NC)"
	@$(UV) run pytest -q $(CURDIR)/scripts/ci/test_skill_px_commands.py
	@echo -e "$(GREEN)✓ Done$(NC)"

# These two call python3 directly: the script is stdlib-only and its CI job has no uv.
sync-docs-snippets: ## Copy examples/quickstarts code into the docs' fenced code blocks
	@echo -e "$(CYAN)Syncing docs code blocks from examples/quickstarts...$(NC)"
	@python3 $(CURDIR)/scripts/sync_docs_snippets.py
	@echo -e "$(GREEN)✓ Done$(NC)"

check-docs-snippets: ## Ensure docs' fenced code blocks match examples/quickstarts
	@echo -e "$(CYAN)Checking docs code blocks against examples/quickstarts...$(NC)"
	@python3 $(CURDIR)/scripts/sync_docs_snippets.py --check
	@echo -e "$(GREEN)✓ Done$(NC)"

gen-otel-models: ## Generate OTel GenAI semconv Pydantic models into src/phoenix/trace/gen_ai/__generated__/models.py
	@echo -e "$(CYAN)Generating OTel GenAI semconv Pydantic models...$(NC)"
	@$(UV) run --script scripts/generate_otel_gen_ai_models.py
	@$(UV) run ruff format src/phoenix/trace/gen_ai/__generated__/models.py
	@$(UV) run ruff check --fix src/phoenix/trace/gen_ai/__generated__/models.py
	@echo -e "$(GREEN)✓ src/phoenix/trace/gen_ai/__generated__/models.py$(NC)"

test-jcs: ## Test JSON canonicalization schema implementation
	@if [ ! -f .jcs-test-data/es6testfile100m.txt ]; then \
		echo -e "$(CYAN)Downloading ES6 numbers test file...$(NC)"; \
		curl -fL https://github.com/cyberphone/json-canonicalization/releases/download/es6testfile/es6testfile100m.txt.gz | gunzip > .jcs-test-data/es6testfile100m.txt; \
	fi
	@echo -e "$(CYAN)Running JSON canonicalization tests...$(NC)"
	@PYTHONPATH=src $(UV) run python scripts/ci/json-canonicalization-schema/verify-canonicalization.py
	@PYTHONPATH=src $(UV) run python scripts/ci/json-canonicalization-schema/verify-numbers.py
	@echo -e "$(GREEN)✓ Done$(NC)"

test-helm: ## Run comprehensive Helm chart tests
	@echo -e "$(CYAN)Running Helm chart tests...$(NC)"
	$(UV) run python scripts/ci/test_helm.py
	@echo -e "$(GREEN)✓ Done$(NC)"

dev-docker: ## Run Docker devops environment (use ARGS= to pass arguments, default: up)
	@echo -e "$(CYAN)Starting Docker devops environment...$(NC)"
	cd scripts/docker/devops && bash dev.sh $(or $(ARGS),up)

dev-mock-llm: ## Start the mock LLM server
	@echo -e "$(CYAN)Starting mock LLM server...$(NC)"
	cd scripts/mock-llm-server && $(PNPM) install && $(PNPM) run build:all && $(PNPM) start

gh-comment-watch: ## Start the GitHub comment watcher
	@echo -e "$(CYAN)Starting GH Comment Watch...$(NC)"
	cd $(GH_COMMENT_WATCH_DIR) && $(PNPM) start

#=============================================================================
# Harbor Evals
#=============================================================================

# HARBOR_JOB selects the benchmark configuration. HARBOR_ARGS passes options to
# `harbor run`. The `-a` option preserves the tasks and environment but replaces the
# configured agents.
HARBOR_JOB ?= evals/harbor/jobs/regression.yaml
HARBOR_ARGS ?=
# harbor-stage downloads the error-analysis fixture, creates the TRAIL fixture when
# HF_TOKEN is set, and builds the px archive. Set HARBOR_CLI=0 to skip the archive.
HARBOR_CLI ?= 1
# The arize-phoenix plugin records tasks, trials, scores, and traces under a dataset
# named after the job's task directory. HARBOR_DATASET overrides the name. Set
# HARBOR_PLUGIN to an empty value to disable recording.
HARBOR_DATASET ?=
HARBOR_PLUGIN ?= --plugin arize-phoenix $(if $(HARBOR_DATASET),--plugin-kwarg dataset=$(HARBOR_DATASET),)
HARBOR_VERSION ?= 0.21.0
# The client package provides the arize-phoenix Harbor plugin. harbor-stage builds it from
# this checkout so a job records with the plugin in the tree. Set HARBOR_CLIENT_VERSION to
# a released version to use that release instead.
HARBOR_CLIENT_VERSION ?=
HARBOR_CLIENT_WHEEL_DIR := dist/phoenix-client
HARBOR_CLIENT = $(if $(HARBOR_CLIENT_VERSION),arize-phoenix-client==$(HARBOR_CLIENT_VERSION),$(firstword $(wildcard $(CURDIR)/$(HARBOR_CLIENT_WHEEL_DIR)/arize_phoenix_client-*.whl)))
HARBOR_ATIF_MODEL ?= openai/gpt-5-mini
HARBOR_ATIF_CLAUDE_MODEL ?= anthropic/claude-sonnet-4-5
# Pin Python because Harbor requires 3.12 or newer and the repository defaults to 3.11.
HARBOR_PYTHON ?= 3.13
UVX := uvx
HARBOR_UVX := $(UVX) --python $(HARBOR_PYTHON) --from 'harbor[daytona]==$(HARBOR_VERSION)'
HARBOR = $(HARBOR_UVX) --with '$(HARBOR_CLIENT)' harbor

# Require the px archive only when HARBOR_ARGS does not replace the configured agents, and
# the client wheel only when no released client version is selected.
define check-harbor-staged
	@$(UV) run --script evals/harbor/scripts/check_job_staged.py $(HARBOR_JOB) $(if $(filter -a,$(HARBOR_ARGS)),--agents-replaced,) $(if $(HARBOR_CLIENT_VERSION),,--client-wheel-dir $(HARBOR_CLIENT_WHEEL_DIR))
endef

harbor-stage: ## Build the Phoenix and client wheels, produce each fixture, stage each task environment, and build the px CLI archive (HF_TOKEN=..., RESEED=1, HARBOR_CLI=0, HARBOR_CLI_PLATFORM=..., HARBOR_CLIENT_VERSION=...)
	@echo -e "$(CYAN)Staging Harbor task environments...$(NC)"
	./evals/harbor/scripts/stage_harbor_environments.sh
	$(if $(HARBOR_CLIENT_VERSION),@echo -e "$(YELLOW)Skipping the client wheel (HARBOR_CLIENT_VERSION=$(HARBOR_CLIENT_VERSION))$(NC)",\
	rm -rf $(HARBOR_CLIENT_WHEEL_DIR) && $(UV) build --wheel packages/phoenix-client --out-dir $(HARBOR_CLIENT_WHEEL_DIR))
	$(if $(filter 0,$(HARBOR_CLI)),@echo -e "$(YELLOW)Skipping the px CLI archive (HARBOR_CLI=0)$(NC)",\
	./evals/harbor/scripts/build_phoenix_cli_archive.sh)
	@echo -e "$(GREEN)✓ Done$(NC)"

harbor-plugin-e2e: ## Manually run the credentialed Harbor plugin E2E matrix
	HARBOR_VERSION=$(HARBOR_VERSION) HARBOR_PYTHON=$(HARBOR_PYTHON) \
		HARBOR_ATIF_MODEL=$(HARBOR_ATIF_MODEL) HARBOR_ATIF_CLAUDE_MODEL=$(HARBOR_ATIF_CLAUDE_MODEL) \
		uv run python tests/integration/harbor/run_plugin_e2e.py

harbor-run: ## Run a Harbor job file with the Phoenix plugin (HARBOR_JOB=..., HARBOR_ARGS=..., HARBOR_CLIENT_VERSION=...)
	$(check-harbor-staged)
	@echo -e "$(CYAN)Running Harbor job $(HARBOR_JOB)...$(NC)"
	PYTHONPATH=. $(HARBOR) run -c $(HARBOR_JOB) $(HARBOR_PLUGIN) $(HARBOR_ARGS) --yes

harbor-view: ## Browse Harbor job results in a local web viewer
	$(HARBOR_UVX) harbor view jobs

#=============================================================================
# Cleanup
#=============================================================================

clean: ## Clean build artifacts
	@echo -e "$(CYAN)Cleaning build artifacts...$(NC)"
	@rm -rf dist/ build/ *.egg-info
	@rm -rf $(APP_DIR)/dist $(APP_DIR)/build
	@find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
	@find . -type d -name ".pytest_cache" -exec rm -rf {} + 2>/dev/null || true
	@find . -type d -name ".ruff_cache" -exec rm -rf {} + 2>/dev/null || true
	@find . -type d -name ".mypy_cache" -exec rm -rf {} + 2>/dev/null || true
	@find . -type f -name "*.pyc" -delete
	@echo -e "$(GREEN)✓ Done$(NC)"

clean-all: clean ## Clean everything including node_modules
	@echo -e "$(CYAN)Cleaning node_modules...$(NC)"
	@rm -rf $(APP_DIR)/node_modules $(APP_DIR)/.pnpm-store
	@rm -rf $(JS_DIR)/node_modules $(JS_DIR)/.pnpm-store
	@find $(JS_DIR) -type d -name "node_modules" -exec rm -rf {} + 2>/dev/null || true
	@rm -rf .venv
	@echo -e "$(GREEN)✓ Done$(NC)"