Add Model Provider Service routing for claude and codex #225
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| workflow_dispatch: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| jobs: | |
| test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 | |
| - run: uv run pytest --ignore=tests/test_e2e.py --ignore=tests/test_e2e_tracing.py | |
| e2e: | |
| runs-on: ubuntu-latest | |
| env: | |
| UCODE_TEST_WORKSPACE: ${{ secrets.UCODE_TEST_WORKSPACE }} | |
| DATABRICKS_HOST: ${{ secrets.UCODE_TEST_WORKSPACE }} | |
| # DATABRICKS_BEARER is the CI escape hatch: `databricks auth token` | |
| # only retrieves cached user-OAuth tokens, so on a hosted runner | |
| # (no databrickscfg, no cached login) it can never produce a bearer. | |
| # Pre-fetch one (e.g. via M2M OAuth client_credentials against | |
| # /oidc/v1/token) and store it as a repo secret. Both | |
| # has_valid_databricks_auth + get_databricks_token + the agents' | |
| # apiKeyHelper short-circuit to this value when set. Tokens are | |
| # short-lived (~1h); rotate when CI starts failing with 401s. | |
| DATABRICKS_BEARER: ${{ secrets.DATABRICKS_BEARER }} | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 | |
| - uses: databricks/setup-cli@bdb89f81c11a5bd647fd55b585b7c396ec68a25a # v1.0.0 | |
| # The agent launch tests `_require_binary("codex")` etc. and skip when | |
| # the CLI isn't on PATH. Install all six so each TestXxxLaunch test | |
| # actually runs instead of skipping. | |
| - name: Install agent CLIs | |
| run: npm install -g | |
| @anthropic-ai/claude-code | |
| @openai/codex | |
| @google/gemini-cli | |
| opencode-ai | |
| @github/copilot | |
| @earendil-works/pi-coding-agent | |
| - run: uv tool install . | |
| # Redirect stdin so any interactive `databricks auth login --no-browser` | |
| # fallback EOFs instead of hanging the runner. With DATABRICKS_BEARER | |
| # set, the auth code path doesn't shell out at all — this is a safety | |
| # net for any code path we may have missed. | |
| - run: uv run pytest tests/test_e2e.py -v < /dev/null | |
| # MLflow tracing e2e lives in its own file and needs the `tracing` | |
| # extra so `import mlflow` resolves (otherwise the test importorskips | |
| # and silently passes as skipped). `!cancelled()` lets this run even | |
| # when the previous pytest step failed — the two suites are | |
| # independent and one shouldn't mask the other. | |
| - if: ${{ !cancelled() }} | |
| run: uv run --extra tracing pytest tests/test_e2e_tracing.py -v < /dev/null | |
| # Diagnostic: the Claude Stop hook writes its trace-creation log to | |
| # $cwd/.claude/mlflow/claude_tracing.log. If the tracing test failed | |
| # because no root `claude_code_conversation` span landed, this shows | |
| # whether the hook fired on the runner and what its MLflow write did. | |
| - if: ${{ failure() }} | |
| name: Dump Claude tracing hook log | |
| run: | | |
| echo "=== claude_tracing.log ===" | |
| cat .claude/mlflow/claude_tracing.log 2>/dev/null || echo "(no hook log — Stop hook never ran)" | |
| echo "=== installed claude-code + mlflow CLI ===" | |
| claude --version || true | |
| "$(uv tool dir --bin)/mlflow" --version || true | |
| # Diagnostic: reproduce the client-side span export from the runner with | |
| # verbose logging. The hook's `log_spans` failure is logged at WARNING to | |
| # mlflow's own logger (not the hook file log), so surface it directly to | |
| # see why the root span never reaches the trace server from CI. | |
| - if: ${{ failure() }} | |
| name: Probe MLflow span export (3.11.1 vs 3.12.0) | |
| env: | |
| DATABRICKS_TOKEN: ${{ secrets.DATABRICKS_BEARER }} | |
| run: | | |
| for V in 3.11.1 3.12.0; do | |
| echo "=== probing mlflow==$V ===" | |
| uv run --with "mlflow[databricks]==$V" python - "$V" <<'PY' 2>&1 | grep -iE "probe|trace_id|version|error|exception|Failed to log" || true | |
| import sys, mlflow | |
| v = sys.argv[1] | |
| mlflow.set_tracking_uri("databricks") | |
| mlflow.set_experiment(experiment_id="2190569664060193") | |
| print("mlflow version", mlflow.__version__) | |
| with mlflow.start_span(name=f"ci_probe_{v.replace('.','_')}") as span: | |
| span.set_inputs({"v": v}) | |
| print("trace_id", span.trace_id) | |
| mlflow.flush_trace_async_logging() | |
| PY | |
| done |