name: Sync Provider Models Daily on: schedule: - cron: '30 0 * * 1-5' # 30 min after model prices sync, Monday to Friday workflow_dispatch: inputs: force_regen: description: 'Regenerate and re-upload YAML even if no new models were found' type: boolean required: false default: false permissions: contents: write pull-requests: write jobs: sync-models: runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Checkout Repository uses: actions/checkout@v6 with: persist-credentials: false - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install dependencies run: pip install requests - name: Set branch name run: | echo "BRANCH_NAME=github-actions/NA-sync-provider-models-$(date +%Y-%m-%d-%H-%M-%S)" >> "$GITHUB_ENV" - name: Run sync script id: sync env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} FORCE_REGEN_FLAG: ${{ inputs.force_regen == true && '--force-regen' || '' }} run: | set +e -o pipefail # $FORCE_REGEN_FLAG is intentionally unquoted: it holds either # `--force-regen` (a single flag) or the empty string. Quoting it # would pass a literal empty argument to python, which is harmless # here but conceptually wrong — the variable is acting as an # optional argv slot, not a single value. # shellcheck disable=SC2086 python scripts/sync_provider_models.py $FORCE_REGEN_FLAG 2>sync_stderr.txt | tee sync_summary.txt EXIT_CODE=${PIPESTATUS[0]} set -e echo "exit_code=$EXIT_CODE" >> "$GITHUB_OUTPUT" # Capture summary for PR body (multiline output) { echo "summary<> "$GITHUB_OUTPUT" if [ "$EXIT_CODE" -eq 1 ]; then echo "No new models found, skipping PR creation." fi rm -f sync_summary.txt sync_stderr.txt - name: Print summary if: always() && steps.sync.outputs.summary != '' run: | { echo "## Sync Summary" echo '```' echo "${{ steps.sync.outputs.summary }}" echo '```' } >> "$GITHUB_STEP_SUMMARY" - name: Check for changes if: steps.sync.outputs.exit_code == '0' id: check_changes run: | if git diff --quiet; then echo "has_changes=false" >> "$GITHUB_OUTPUT" else echo "has_changes=true" >> "$GITHUB_OUTPUT" fi - name: Commit files if: steps.check_changes.outputs.has_changes == 'true' run: | set -ex git config --local user.email "github-actions@comet.com" git config --local user.name "Github Actions (${{ github.actor }})" git add apps/opik-backend/src/main/java/com/comet/opik/infrastructure/llm/ git add apps/opik-frontend/src/types/providers.ts git add apps/opik-frontend/src/constants/providerModels.ts git add apps/opik-backend/src/main/resources/llm-models-default.yaml git commit -m "[NA] [BE][FE] chore: sync provider model definitions" - name: Configure AWS credentials if: steps.sync.outputs.exit_code == '0' uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_OPIK_CDN_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_OPIK_CDN_SECRET }} aws-region: us-east-1 role-to-assume: ${{ vars.AWS_OPIK_CDN_ROLE }} role-session-name: sync-provider-models - name: Upload YAML to S3 if: steps.sync.outputs.exit_code == '0' run: | aws s3 cp \ apps/opik-backend/src/main/resources/llm-models-default.yaml \ ${{ vars.AWS_OPIK_CDN_BUCKET }}/llm-models-default.yaml \ --cache-control "max-age=300" - name: Invalidate CloudFront distribution if: steps.sync.outputs.exit_code == '0' run: | aws cloudfront create-invalidation \ --distribution-id ${{ secrets.AWS_OPIK_CDN_DISTRIBUTION }} \ --paths "/opik/llm-models-default.yaml" - name: Create Pull Request if: steps.check_changes.outputs.has_changes == 'true' uses: peter-evans/create-pull-request@v6 with: token: ${{ secrets.COMET_ACTION_CREATE_PR }} branch: ${{ env.BRANCH_NAME }} title: "[NA] [BE][FE] chore: sync provider model definitions" body: | ## Details Automated sync of LLM provider model definitions from source APIs and prices JSON. **Sync summary:** ``` ${{ steps.sync.outputs.summary }} ``` ## Change checklist - [x] User facing - [ ] Documentation update ## Issues - NA ## AI-WATERMARK AI-WATERMARK: yes - If yes: - Tools: GitHub Actions (`sync_provider_models.yml`) - Model(s): N/A (scripted automation) - Scope: Automated model list sync - Human verification: Requires manual review before merge ## Testing - Script dry-run passed in CI - Changes are add-only; stale/deprecated models flagged for manual review ## Documentation N/A base: main