diff --git a/.github/workflows/release-pipeline.yml b/.github/workflows/release-pipeline.yml index 796ed23e51..805ee6ca9b 100644 --- a/.github/workflows/release-pipeline.yml +++ b/.github/workflows/release-pipeline.yml @@ -86,570 +86,9 @@ jobs: id: detect run: ./.github/workflows/scripts/detect-all-changes.sh "auto" - # Run all tests in parallel before any releases - test-core: - needs: [check-skip, detect-changes] - if: needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.core-needs-release == 'true' - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 - with: - egress-policy: block - allowed-endpoints: > - api.anthropic.com:443 - api.cerebras.ai:443 - api.cohere.ai:443 - api.elevenlabs.io:443 - api.fireworks.ai:443 - api.github.com:443 - api.groq.com:443 - api.mistral.ai:443 - api.openai.com:443 - api.parasail.io:443 - api.perplexity.ai:443 - bedrock-runtime.us-east-1.amazonaws.com:443 - bedrock.us-east-1.amazonaws.com:443 - bifrost-batch-api-file-upload-testing.s3.us-east-1.amazonaws.com:443 - fal.media:443 - generativelanguage.googleapis.com:443 - github.com:443 - huggingface.co:443 - login.microsoftonline.com:443 - maxim-o1.openai.azure.com:443 - nodejs.org:443 - openrouter.ai:443 - proxy.golang.org:443 - registry.npmjs.org:443 - release-assets.githubusercontent.com:443 - router.huggingface.co:443 - storage.googleapis.com:443 - sum.golang.org:443 - - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - fetch-tags: true - - - name: Set up Go - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - with: - go-version: "1.26.2" - - - name: Set up Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: "25" - - - name: Run core tests - env: - MAXIM_API_KEY: ${{ secrets.MAXIM_API_KEY }} - MAXIM_LOGGER_ID: ${{ secrets.MAXIM_LOG_REPO_ID }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_SESSION_TOKEN: ${{ secrets.AWS_SESSION_TOKEN }} - AWS_ARN: ${{ secrets.AWS_ARN }} - BEDROCK_API_KEY: ${{ secrets.BEDROCK_API_KEY }} - AZURE_ENDPOINT: ${{ secrets.AZURE_ENDPOINT }} - AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }} - AZURE_API_VERSION: ${{ secrets.AZURE_API_VERSION }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} - MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} - PARASAIL_API_KEY: ${{ secrets.PARASAIL_API_KEY }} - ELEVENLABS_API_KEY: ${{ secrets.ELEVENLABS_API_KEY }} - PERPLEXITY_API_KEY: ${{ secrets.PERPLEXITY_API_KEY }} - SGL_API_KEY: ${{ secrets.SGL_API_KEY }} - CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }} - COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }} - FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }} - VERTEX_CREDENTIALS: ${{ secrets.VERTEX_CREDENTIALS }} - VERTEX_PROJECT_ID: ${{ secrets.VERTEX_PROJECT_ID }} - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - HUGGING_FACE_API_KEY: ${{ secrets.HUGGING_FACE_API_KEY }} - AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} - AWS_BEDROCK_ROLE_ARN: ${{ secrets.AWS_BEDROCK_ROLE_ARN }} - BIFROST_ENCRYPTION_KEY: ${{ secrets.BIFROST_ENCRYPTION_KEY }} - run: ./.github/workflows/scripts/test-core.sh - - # Approval gate for flaky test-core failures - # If test-core fails (often due to flaky provider API calls), this job waits for manual approval - # to continue the release pipeline without requiring a full re-run - approve-flaky-test-core: - needs: [check-skip, detect-changes, test-core] - if: | - always() && - needs.check-skip.outputs.should-skip != 'true' && - needs.detect-changes.outputs.core-needs-release == 'true' && - needs.test-core.result == 'failure' - runs-on: ubuntu-latest - environment: - name: flaky-test-override - url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - outputs: - approved: ${{ steps.approve.outputs.approved }} - steps: - - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 - with: - egress-policy: block - allowed-endpoints: >+ - - - name: Display failed test info - run: | - echo "::warning::test-core failed. Review the logs to determine if this is a flaky test." - echo "If this is a known flaky test (e.g., provider API timeout), approve to continue." - echo "If this is a real failure, reject and fix the issue." - - name: Mark as approved - id: approve - run: echo "approved=true" >> $GITHUB_OUTPUT - - test-framework: - needs: [check-skip, detect-changes] - if: needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.framework-needs-release == 'true' - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 - with: - egress-policy: block - allowed-endpoints: > - _grpc_config.cluster.qdrant.io:443 - _grpc_config.localhost:443 - api.github.com:443 - auth.docker.io:443 - cluster.qdrant.io:443 - cluster.weaviate.network:443 - codecov.io:443 - dl-cdn.alpinelinux.org:443 - ghcr.io:443 - github.com:443 - pkg-containers.githubusercontent.com:443 - production.cloudflare.docker.com:443 - proxy.golang.org:443 - registry-1.docker.io:443 - release-assets.githubusercontent.com:443 - storage.googleapis.com:443 - sum.golang.org:443 - telemetry.qdrant.io:443 - uploader.codecov.io:443 - - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - fetch-tags: true - - - name: Set up Go - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - with: - go-version: "1.26.2" - - - name: Set up Docker Compose - run: | - docker --version - if ! docker compose version >/dev/null 2>&1; then - echo "Installing Docker Compose..." - sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - sudo chmod +x /usr/local/bin/docker-compose - docker-compose --version - else - echo "Docker Compose plugin is available" - docker compose version - fi - - - name: Run framework tests - env: - MAXIM_API_KEY: ${{ secrets.MAXIM_API_KEY }} - MAXIM_LOGGER_ID: ${{ secrets.MAXIM_LOG_REPO_ID }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_SESSION_TOKEN: ${{ secrets.AWS_SESSION_TOKEN }} - AWS_ARN: ${{ secrets.AWS_ARN }} - BEDROCK_API_KEY: ${{ secrets.BEDROCK_API_KEY }} - AZURE_ENDPOINT: ${{ secrets.AZURE_ENDPOINT }} - AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }} - AZURE_API_VERSION: ${{ secrets.AZURE_API_VERSION }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} - MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} - PARASAIL_API_KEY: ${{ secrets.PARASAIL_API_KEY }} - ELEVENLABS_API_KEY: ${{ secrets.ELEVENLABS_API_KEY }} - PERPLEXITY_API_KEY: ${{ secrets.PERPLEXITY_API_KEY }} - SGL_API_KEY: ${{ secrets.SGL_API_KEY }} - CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }} - COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }} - FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }} - VERTEX_CREDENTIALS: ${{ secrets.VERTEX_CREDENTIALS }} - VERTEX_PROJECT_ID: ${{ secrets.VERTEX_PROJECT_ID }} - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - HUGGING_FACE_API_KEY: ${{ secrets.HUGGING_FACE_API_KEY }} - BIFROST_ENCRYPTION_KEY: ${{ secrets.BIFROST_ENCRYPTION_KEY }} - run: ./.github/workflows/scripts/test-framework.sh - - test-plugins: - needs: [check-skip, detect-changes] - if: needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.plugins-need-release == 'true' - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 - with: - egress-policy: block - allowed-endpoints: > - _grpc_config.localhost:443 - _http._tcp.azure.archive.ubuntu.com:443 - _https._tcp.esm.ubuntu.com:443 - _https._tcp.motd.ubuntu.com:443 - _https._tcp.packages.microsoft.com:443 - api.github.com:443 - api.openai.com:443 - auth.docker.io:443 - azure.archive.ubuntu.com:80 - codecov.io:443 - dl-cdn.alpinelinux.org:443 - esm.ubuntu.com:443 - getbifrost.ai:443 - ghcr.io:443 - github.com:443 - packages.microsoft.com:443 - pkg-containers.githubusercontent.com:443 - production.cloudflare.docker.com:443 - proxy.golang.org:443 - registry-1.docker.io:443 - release-assets.githubusercontent.com:443 - storage.googleapis.com:443 - sum.golang.org:443 - telemetry.qdrant.io:443 - uploader.codecov.io:443 - - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - fetch-tags: true - - - name: Install jq - run: | - sudo apt-get update - sudo apt-get install -y jq - - - name: Set up Go - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - with: - go-version: "1.26.2" - - - name: Set up Docker Compose - run: | - docker --version - if ! docker compose version >/dev/null 2>&1; then - echo "Installing Docker Compose..." - sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - sudo chmod +x /usr/local/bin/docker-compose - docker-compose --version - else - echo "Docker Compose plugin is available" - docker compose version - fi - - - name: Run plugin tests - env: - MAXIM_API_KEY: ${{ secrets.MAXIM_API_KEY }} - MAXIM_LOGGER_ID: ${{ secrets.MAXIM_LOG_REPO_ID }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_SESSION_TOKEN: ${{ secrets.AWS_SESSION_TOKEN }} - AWS_ARN: ${{ secrets.AWS_ARN }} - BEDROCK_API_KEY: ${{ secrets.BEDROCK_API_KEY }} - AZURE_ENDPOINT: ${{ secrets.AZURE_ENDPOINT }} - AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }} - AZURE_API_VERSION: ${{ secrets.AZURE_API_VERSION }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} - MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} - OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} - PARASAIL_API_KEY: ${{ secrets.PARASAIL_API_KEY }} - ELEVENLABS_API_KEY: ${{ secrets.ELEVENLABS_API_KEY }} - PERPLEXITY_API_KEY: ${{ secrets.PERPLEXITY_API_KEY }} - SGL_API_KEY: ${{ secrets.SGL_API_KEY }} - CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }} - COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }} - FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }} - VERTEX_CREDENTIALS: ${{ secrets.VERTEX_CREDENTIALS }} - VERTEX_PROJECT_ID: ${{ secrets.VERTEX_PROJECT_ID }} - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - HUGGING_FACE_API_KEY: ${{ secrets.HUGGING_FACE_API_KEY }} - BIFROST_ENCRYPTION_KEY: ${{ secrets.BIFROST_ENCRYPTION_KEY }} - run: ./.github/workflows/scripts/test-all-plugins.sh - - test-bifrost-http: - needs: [check-skip, detect-changes] - if: needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.bifrost-http-needs-release == 'true' - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 - with: - egress-policy: block - allowed-endpoints: > - 172.38.0.12:8080 - 172.38.0.12:8301 - 172.38.0.2:5432 - api.github.com:443 - auth.docker.io:443 - codecov.io:443 - cr.weaviate.io:443 - example.com:443 - file.example.com:443 - fonts.googleapis.com:443 - fonts.gstatic.com:443 - getbifrost.ai:443 - github.com:443 - nodejs.org:443 - production.cloudflare.docker.com:443 - proxy.golang.org:443 - registry-1.docker.io:443 - registry.npmjs.org:443 - release-assets.githubusercontent.com:443 - storage.googleapis.com:443 - sum.golang.org:443 - telemetry.qdrant.io:443 - uploader.codecov.io:443 - www.getbifrost.ai:443 - - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - fetch-tags: true - - - name: Set up Go - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - with: - go-version: "1.26.2" - - - name: Set up Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: "25" - - - name: Set up Docker Compose - run: | - docker --version - if ! docker compose version >/dev/null 2>&1; then - echo "Installing Docker Compose..." - sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - sudo chmod +x /usr/local/bin/docker-compose - docker-compose --version - else - echo "Docker Compose plugin is available" - docker compose version - fi - - - name: Run bifrost-http tests - env: - MAXIM_API_KEY: ${{ secrets.MAXIM_API_KEY }} - MAXIM_LOGGER_ID: ${{ secrets.MAXIM_LOG_REPO_ID }} - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - BIFROST_ENCRYPTION_KEY: ${{ secrets.BIFROST_ENCRYPTION_KEY }} - run: ./.github/workflows/scripts/test-bifrost-http.sh - - # Migration tests - validates database migrations from previous versions - test-migrations: - needs: [check-skip, detect-changes] - if: needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.bifrost-http-needs-release == 'true' - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 - with: - egress-policy: block - allowed-endpoints: > - 172.38.0.2:5432 - api.anthropic.com:443 - api.github.com:443 - api.openai.com:443 - auth.docker.io:443 - downloads.getmaxim.ai:443 - getbifrost.ai:443 - github.com:443 - nodejs.org:443 - production.cloudflare.docker.com:443 - proxy.golang.org:443 - registry-1.docker.io:443 - registry.npmjs.org:443 - release-assets.githubusercontent.com:443 - storage.googleapis.com:443 - sum.golang.org:443 - - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - fetch-tags: true - - - name: Set up Go - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - with: - go-version: "1.26.2" - - - name: Set up Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: "25" - - - name: Set up Docker Compose - run: | - docker --version - if ! docker compose version >/dev/null 2>&1; then - echo "Installing Docker Compose..." - sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - sudo chmod +x /usr/local/bin/docker-compose - docker-compose --version - else - echo "Docker Compose plugin is available" - docker compose version - fi - - - name: Run migration tests - run: | - chmod +x ./.github/workflows/scripts/run-migration-tests.sh - ./.github/workflows/scripts/run-migration-tests.sh postgres - - # E2E UI tests - validates UI with Playwright - test-e2e-ui: - needs: [check-skip, detect-changes] - if: needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.bifrost-http-needs-release == 'true' - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - name: Harden Runner - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 - with: - egress-policy: block - allowed-endpoints: > - 172.38.0.12:8301 - 172.38.0.2:5432 - _http._tcp.azure.archive.ubuntu.com:443 - _https._tcp.esm.ubuntu.com:443 - _https._tcp.motd.ubuntu.com:443 - _https._tcp.packages.microsoft.com:443 - api.github.com:443 - api.openai.com:443 - auth.docker.io:443 - azure.archive.ubuntu.com:80 - cdn.jsdelivr.net:443 - cdn.playwright.dev:443 - cr.weaviate.io:443 - d3gk2c5xim1je2.cloudfront.net:443 - d4tuoctqmanu0.cloudfront.net:443 - docs.getbifrost.ai:443 - esm.ubuntu.com:443 - fonts.googleapis.com:443 - fonts.gstatic.com:443 - getbifrost.ai:443 - github.com:443 - mintcdn.com:443 - nodejs.org:443 - packages.microsoft.com:443 - playwright.download.prss.microsoft.com:443 - production.cloudflare.docker.com:443 - proxy.golang.org:443 - registry-1.docker.io:443 - registry.npmjs.org:443 - release-assets.githubusercontent.com:443 - static.cloudflareinsights.com:443 - storage.googleapis.com:443 - sum.golang.org:443 - telemetry.qdrant.io:443 - ts-mcp-sse-proxy.fly.dev:443 - - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - fetch-tags: true - - - name: Set up Go - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 - with: - go-version: "1.26.2" - - - name: Set up Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: "25" - - - name: Set up Docker Compose - run: | - docker --version - if ! docker compose version >/dev/null 2>&1; then - echo "Installing Docker Compose..." - sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - sudo chmod +x /usr/local/bin/docker-compose - docker-compose --version - else - echo "Docker Compose plugin is available" - docker compose version - fi - - - name: Run E2E UI tests - env: - MCP_SSE_HEADERS: ${{ secrets.MCP_SSE_HEADERS }} - run: ./.github/workflows/scripts/test-e2e-ui.sh - - - name: Upload Playwright artifacts - if: ${{ !cancelled() }} - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: playwright-report - path: | - tests/e2e/test-results/ - tests/e2e/playwright-report/ - retention-days: 30 - core-release: - needs: - [ - check-skip, - detect-changes, - test-core, - approve-flaky-test-core, - test-framework, - test-plugins, - test-bifrost-http, - test-migrations, - ] - if: "always() && needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.core-needs-release == 'true' && (needs.test-core.result == 'success' || (needs.test-core.result == 'failure' && needs.approve-flaky-test-core.result == 'success')) && (needs.test-framework.result == 'success' || needs.test-framework.result == 'skipped') && (needs.test-plugins.result == 'success' || needs.test-plugins.result == 'skipped') && (needs.test-bifrost-http.result == 'success' || needs.test-bifrost-http.result == 'skipped') && (needs.test-migrations.result == 'success' || needs.test-migrations.result == 'skipped')" + needs: [check-skip, detect-changes] + if: "needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.core-needs-release == 'true'" runs-on: ubuntu-latest permissions: contents: write @@ -731,19 +170,8 @@ jobs: run: ./.github/workflows/scripts/release-core.sh "${{ needs.detect-changes.outputs.core-version }}" framework-release: - needs: - [ - check-skip, - detect-changes, - test-core, - approve-flaky-test-core, - test-framework, - test-plugins, - test-bifrost-http, - test-migrations, - core-release, - ] - if: "always() && needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.framework-needs-release == 'true' && (needs.test-core.result == 'success' || needs.test-core.result == 'skipped' || (needs.test-core.result == 'failure' && needs.approve-flaky-test-core.result == 'success')) && needs.test-framework.result == 'success' && (needs.test-plugins.result == 'success' || needs.test-plugins.result == 'skipped') && (needs.test-bifrost-http.result == 'success' || needs.test-bifrost-http.result == 'skipped') && (needs.test-migrations.result == 'success' || needs.test-migrations.result == 'skipped') && (needs.detect-changes.outputs.core-needs-release == 'false' || needs.core-release.result == 'success' || needs.core-release.result == 'skipped')" + needs: [check-skip, detect-changes, core-release] + if: "always() && needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.framework-needs-release == 'true' && (needs.detect-changes.outputs.core-needs-release == 'false' || needs.core-release.result == 'success' || needs.core-release.result == 'skipped')" runs-on: ubuntu-latest permissions: contents: write @@ -835,20 +263,8 @@ jobs: run: ./.github/workflows/scripts/release-framework.sh "${{ needs.detect-changes.outputs.framework-version }}" plugins-release: - needs: - [ - check-skip, - detect-changes, - test-core, - approve-flaky-test-core, - test-framework, - test-plugins, - test-bifrost-http, - test-migrations, - core-release, - framework-release, - ] - if: "always() && needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.plugins-need-release == 'true' && (needs.test-core.result == 'success' || needs.test-core.result == 'skipped' || (needs.test-core.result == 'failure' && needs.approve-flaky-test-core.result == 'success')) && (needs.test-framework.result == 'success' || needs.test-framework.result == 'skipped') && needs.test-plugins.result == 'success' && (needs.test-bifrost-http.result == 'success' || needs.test-bifrost-http.result == 'skipped') && (needs.test-migrations.result == 'success' || needs.test-migrations.result == 'skipped') && (needs.detect-changes.outputs.core-needs-release == 'false' || needs.core-release.result == 'success' || needs.core-release.result == 'skipped') && (needs.detect-changes.outputs.framework-needs-release == 'false' || needs.framework-release.result == 'success' || needs.framework-release.result == 'skipped')" + needs: [check-skip, detect-changes, core-release, framework-release] + if: "always() && needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.plugins-need-release == 'true' && (needs.detect-changes.outputs.core-needs-release == 'false' || needs.core-release.result == 'success' || needs.core-release.result == 'skipped') && (needs.detect-changes.outputs.framework-needs-release == 'false' || needs.framework-release.result == 'success' || needs.framework-release.result == 'skipped')" runs-on: ubuntu-latest permissions: contents: write @@ -958,21 +374,8 @@ jobs: # Prep: update dependencies, validate build, commit/push bifrost-http-prep: - needs: - [ - check-skip, - detect-changes, - test-core, - approve-flaky-test-core, - test-framework, - test-plugins, - test-bifrost-http, - test-migrations, - core-release, - framework-release, - plugins-release, - ] - if: "always() && needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.bifrost-http-needs-release == 'true' && (needs.test-core.result == 'success' || needs.test-core.result == 'skipped' || (needs.test-core.result == 'failure' && needs.approve-flaky-test-core.result == 'success')) && (needs.test-framework.result == 'success' || needs.test-framework.result == 'skipped') && (needs.test-plugins.result == 'success' || needs.test-plugins.result == 'skipped') && needs.test-bifrost-http.result == 'success' && needs.test-migrations.result == 'success' && (needs.detect-changes.outputs.core-needs-release == 'false' || needs.core-release.result == 'success' || needs.core-release.result == 'skipped') && (needs.detect-changes.outputs.framework-needs-release == 'false' || needs.framework-release.result == 'success' || needs.framework-release.result == 'skipped') && (needs.detect-changes.outputs.plugins-need-release == 'false' || needs.plugins-release.result == 'success' || needs.plugins-release.result == 'skipped')" + needs: [check-skip, detect-changes, core-release, framework-release, plugins-release] + if: "always() && needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.bifrost-http-needs-release == 'true' && (needs.detect-changes.outputs.core-needs-release == 'false' || needs.core-release.result == 'success' || needs.core-release.result == 'skipped') && (needs.detect-changes.outputs.framework-needs-release == 'false' || needs.framework-release.result == 'success' || needs.framework-release.result == 'skipped') && (needs.detect-changes.outputs.plugins-need-release == 'false' || needs.plugins-release.result == 'success' || needs.plugins-release.result == 'skipped')" runs-on: ubuntu-latest permissions: contents: write @@ -1214,22 +617,8 @@ jobs: # Docker build amd64 docker-build-amd64: - needs: - [ - check-skip, - detect-changes, - test-core, - approve-flaky-test-core, - test-framework, - test-plugins, - test-bifrost-http, - test-migrations, - core-release, - framework-release, - plugins-release, - bifrost-http-release, - ] - if: "always() && needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.docker-needs-release == 'true' && (needs.test-core.result == 'success' || needs.test-core.result == 'skipped' || (needs.test-core.result == 'failure' && needs.approve-flaky-test-core.result == 'success')) && (needs.test-framework.result == 'success' || needs.test-framework.result == 'skipped') && (needs.test-plugins.result == 'success' || needs.test-plugins.result == 'skipped') && (needs.test-bifrost-http.result == 'success' || needs.test-bifrost-http.result == 'skipped') && (needs.test-migrations.result == 'success' || needs.test-migrations.result == 'skipped') && (needs.detect-changes.outputs.core-needs-release == 'false' || needs.core-release.result == 'success' || needs.core-release.result == 'skipped') && (needs.detect-changes.outputs.framework-needs-release == 'false' || needs.framework-release.result == 'success' || needs.framework-release.result == 'skipped') && (needs.detect-changes.outputs.plugins-need-release == 'false' || needs.plugins-release.result == 'success' || needs.plugins-release.result == 'skipped') && (needs.detect-changes.outputs.bifrost-http-needs-release == 'false' || needs.bifrost-http-release.result == 'success' || needs.bifrost-http-release.result == 'skipped')" + needs: [check-skip, detect-changes, core-release, framework-release, plugins-release, bifrost-http-release] + if: "always() && needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.docker-needs-release == 'true' && (needs.detect-changes.outputs.core-needs-release == 'false' || needs.core-release.result == 'success' || needs.core-release.result == 'skipped') && (needs.detect-changes.outputs.framework-needs-release == 'false' || needs.framework-release.result == 'success' || needs.framework-release.result == 'skipped') && (needs.detect-changes.outputs.plugins-need-release == 'false' || needs.plugins-release.result == 'success' || needs.plugins-release.result == 'skipped') && (needs.detect-changes.outputs.bifrost-http-needs-release == 'false' || needs.bifrost-http-release.result == 'success' || needs.bifrost-http-release.result == 'skipped')" runs-on: ubuntu-latest permissions: contents: write @@ -1299,22 +688,8 @@ jobs: # Docker build arm64 docker-build-arm64: - needs: - [ - check-skip, - detect-changes, - test-core, - approve-flaky-test-core, - test-framework, - test-plugins, - test-bifrost-http, - test-migrations, - core-release, - framework-release, - plugins-release, - bifrost-http-release, - ] - if: "always() && needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.docker-needs-release == 'true' && (needs.test-core.result == 'success' || needs.test-core.result == 'skipped' || (needs.test-core.result == 'failure' && needs.approve-flaky-test-core.result == 'success')) && (needs.test-framework.result == 'success' || needs.test-framework.result == 'skipped') && (needs.test-plugins.result == 'success' || needs.test-plugins.result == 'skipped') && (needs.test-bifrost-http.result == 'success' || needs.test-bifrost-http.result == 'skipped') && (needs.test-migrations.result == 'success' || needs.test-migrations.result == 'skipped') && (needs.detect-changes.outputs.core-needs-release == 'false' || needs.core-release.result == 'success' || needs.core-release.result == 'skipped') && (needs.detect-changes.outputs.framework-needs-release == 'false' || needs.framework-release.result == 'success' || needs.framework-release.result == 'skipped') && (needs.detect-changes.outputs.plugins-need-release == 'false' || needs.plugins-release.result == 'success' || needs.plugins-release.result == 'skipped') && (needs.detect-changes.outputs.bifrost-http-needs-release == 'false' || needs.bifrost-http-release.result == 'success' || needs.bifrost-http-release.result == 'skipped')" + needs: [check-skip, detect-changes, core-release, framework-release, plugins-release, bifrost-http-release] + if: "always() && needs.check-skip.outputs.should-skip != 'true' && needs.detect-changes.outputs.docker-needs-release == 'true' && (needs.detect-changes.outputs.core-needs-release == 'false' || needs.core-release.result == 'success' || needs.core-release.result == 'skipped') && (needs.detect-changes.outputs.framework-needs-release == 'false' || needs.framework-release.result == 'success' || needs.framework-release.result == 'skipped') && (needs.detect-changes.outputs.plugins-need-release == 'false' || needs.plugins-release.result == 'success' || needs.plugins-release.result == 'skipped') && (needs.detect-changes.outputs.bifrost-http-needs-release == 'false' || needs.bifrost-http-release.result == 'success' || needs.bifrost-http-release.result == 'skipped')" runs-on: ubuntu-24.04-arm permissions: contents: write @@ -1417,22 +792,8 @@ jobs: # Push Mintlify changelog push-mintlify-changelog: - needs: - [ - check-skip, - detect-changes, - test-core, - approve-flaky-test-core, - test-framework, - test-plugins, - test-bifrost-http, - test-migrations, - core-release, - framework-release, - plugins-release, - bifrost-http-release, - ] - if: "always() && needs.check-skip.outputs.should-skip != 'true' && (needs.test-core.result == 'success' || needs.test-core.result == 'skipped' || (needs.test-core.result == 'failure' && needs.approve-flaky-test-core.result == 'success')) && (needs.test-framework.result == 'success' || needs.test-framework.result == 'skipped') && (needs.test-plugins.result == 'success' || needs.test-plugins.result == 'skipped') && (needs.test-bifrost-http.result == 'success' || needs.test-bifrost-http.result == 'skipped') && (needs.test-migrations.result == 'success' || needs.test-migrations.result == 'skipped') && (needs.detect-changes.outputs.core-needs-release == 'false' || needs.core-release.result == 'success' || needs.core-release.result == 'skipped') && (needs.detect-changes.outputs.framework-needs-release == 'false' || needs.framework-release.result == 'success' || needs.framework-release.result == 'skipped') && (needs.detect-changes.outputs.plugins-need-release == 'false' || needs.plugins-release.result == 'success' || needs.plugins-release.result == 'skipped') && (needs.detect-changes.outputs.bifrost-http-needs-release == 'false' || needs.bifrost-http-release.result == 'success' || needs.bifrost-http-release.result == 'skipped')" + needs: [check-skip, detect-changes, core-release, framework-release, plugins-release, bifrost-http-release] + if: "always() && needs.check-skip.outputs.should-skip != 'true' && (needs.detect-changes.outputs.core-needs-release == 'false' || needs.core-release.result == 'success' || needs.core-release.result == 'skipped') && (needs.detect-changes.outputs.framework-needs-release == 'false' || needs.framework-release.result == 'success' || needs.framework-release.result == 'skipped') && (needs.detect-changes.outputs.plugins-need-release == 'false' || needs.plugins-release.result == 'success' || needs.plugins-release.result == 'skipped') && (needs.detect-changes.outputs.bifrost-http-needs-release == 'false' || needs.bifrost-http-release.result == 'success' || needs.bifrost-http-release.result == 'skipped')" runs-on: ubuntu-latest permissions: contents: write @@ -1461,11 +822,6 @@ jobs: [ check-skip, detect-changes, - test-core, - test-framework, - test-plugins, - test-bifrost-http, - test-migrations, core-release, framework-release, plugins-release, diff --git a/transports/config.schema.json b/transports/config.schema.json index 04ec3e35ca..9459d18552 100644 --- a/transports/config.schema.json +++ b/transports/config.schema.json @@ -10,6 +10,12 @@ "description": "The schema version. This should be set to \"https://www.getbifrost.ai/schema\"", "const": "https://www.getbifrost.ai/schema" }, + "version": { + "type": "integer", + "description": "Controls how empty arrays in allow-list fields (models, allowed_models, key_ids, tools_to_execute) are interpreted. Omit or set to 2 for v1.5.0+ semantics: empty = deny all, [\"*\"] = allow all. Set to 1 to restore v1.4.x semantics: empty = allow all.", + "enum": [1, 2], + "default": 2 + }, "encryption_key": { "type": "string", "description": "You can set the value as env. to use an environment variable. We also read encryption key from BIFROST_ENCRYPTION_KEY environment variable. Note: once set, the encryption key cannot be changed unless you clean up the database. Accepts any string; a secure 32-byte AES-256 key will be derived using Argon2id KDF. If not provided, data will be saved in plain text. Recommended: use a passphrase of at least 16 bytes for better security" @@ -94,9 +100,29 @@ "minimum": 1, "description": "Maximum request body size in MB" }, - "enable_litellm_fallbacks": { - "type": "boolean", - "description": "Enable litellm-specific fallbacks for text completion for Groq" + "compat": { + "type": "object", + "description": "Compat plugin configuration for request type conversion, parameter dropping, and parameter value conversion", + "properties": { + "convert_text_to_chat": { + "type": "boolean", + "description": "Convert text completion requests to chat for models that only support chat" + }, + "convert_chat_to_responses": { + "type": "boolean", + "description": "Convert chat completion requests to responses for models that only support responses" + }, + "should_drop_params": { + "type": "boolean", + "description": "Drop unsupported parameters based on model catalog allowlist" + }, + "should_convert_params": { + "type": "boolean", + "description": "Converts model parameter values that are not supported by the model.", + "default": false + } + }, + "additionalProperties": false }, "header_filter_config": { "type": "object", @@ -180,6 +206,17 @@ "minimum": 0, "description": "Global tool sync interval in minutes (0 = disabled)", "default": 10 + }, + "mcp_disable_auto_tool_inject": { + "type": "boolean", + "description": "When true, MCP tools are not automatically injected into requests. Tools are only included when explicitly specified via request context filters or headers, such as x-bf-mcp-include-tools or x-bf-mcp-include-clients.", + "default": false + }, + "routing_chain_max_depth": { + "type": "integer", + "minimum": 1, + "description": "Maximum depth for routing rule chain evaluation", + "default": 10 } }, "additionalProperties": false @@ -219,7 +256,7 @@ "$ref": "#/$defs/provider" }, "ollama": { - "$ref": "#/$defs/provider" + "$ref": "#/$defs/provider_with_ollama_config" }, "groq": { "$ref": "#/$defs/provider" @@ -231,7 +268,7 @@ "$ref": "#/$defs/provider" }, "sgl": { - "$ref": "#/$defs/provider" + "$ref": "#/$defs/provider_with_sgl_config" }, "parasail": { "$ref": "#/$defs/provider" @@ -240,7 +277,7 @@ "$ref": "#/$defs/provider" }, "replicate": { - "$ref": "#/$defs/provider" + "$ref": "#/$defs/provider_with_replicate_config" }, "elevenlabs": { "$ref": "#/$defs/provider" @@ -256,6 +293,15 @@ }, "fireworks": { "$ref": "#/$defs/provider" + }, + "nebius": { + "$ref": "#/$defs/provider" + }, + "xai": { + "$ref": "#/$defs/provider" + }, + "runway": { + "$ref": "#/$defs/provider" } }, "additionalProperties": true @@ -292,17 +338,16 @@ "format": "date-time", "description": "Last time budget was reset" }, - "calendar_aligned": { - "type": "boolean", - "description": "Snap resets to calendar boundaries (day/week/month/year start)", - "default": false + "virtual_key_id": { + "type": "string", + "description": "ID of the virtual key this budget belongs to (mutually exclusive with provider_config_id)" + }, + "provider_config_id": { + "type": "integer", + "description": "ID of the provider config this budget belongs to (mutually exclusive with virtual_key_id)" } }, - "required": [ - "id", - "max_limit", - "reset_duration" - ], + "required": ["id", "max_limit", "reset_duration"], "additionalProperties": false } }, @@ -353,9 +398,7 @@ "description": "Last time request counter was reset" } }, - "required": [ - "id" - ], + "required": ["id"], "additionalProperties": false } }, @@ -382,10 +425,7 @@ "description": "Associated rate limit ID" } }, - "required": [ - "id", - "name" - ], + "required": ["id", "name"], "additionalProperties": false } }, @@ -428,10 +468,7 @@ "description": "Team claims data" } }, - "required": [ - "id", - "name" - ], + "required": ["id", "name"], "additionalProperties": false } }, @@ -462,6 +499,11 @@ "description": "Whether the virtual key is active", "default": true }, + "calendar_aligned": { + "type": "boolean", + "description": "Snap all budget resets to calendar boundaries (day, week, month, year)", + "default": false + }, "team_id": { "type": "string", "description": "Associated team ID (mutually exclusive with customer_id)" @@ -470,33 +512,26 @@ "type": "string", "description": "Associated customer ID (mutually exclusive with team_id)" }, - "budget_id": { - "type": "string", - "description": "Associated budget ID" - }, "rate_limit_id": { "type": "string", "description": "Associated rate limit ID" }, "provider_configs": { "type": "array", - "description": "Provider configurations for this virtual key (empty means all providers allowed)", + "description": "Provider configurations for this virtual key (empty means no providers allowed, deny-by-default)", "items": { "$ref": "#/$defs/virtual_key_provider_config" } }, "mcp_configs": { "type": "array", - "description": "MCP configurations for this virtual key", + "description": "MCP configurations for this virtual key (empty array means no MCP tools allowed, deny-by-default)", "items": { "$ref": "#/$defs/virtual_key_mcp_config" } } }, - "required": [ - "id", - "name" - ], + "required": ["id", "name"], "additionalProperties": false } }, @@ -507,6 +542,13 @@ "$ref": "#/$defs/routing_rule" } }, + "pricing_overrides": { + "type": "array", + "description": "Scoped pricing overrides applied at runtime by the model catalog", + "items": { + "$ref": "#/$defs/provider_pricing_override" + } + }, "auth_config": { "$ref": "#/$defs/auth_config" }, @@ -537,10 +579,7 @@ "description": "Rate limit ID to associate with this model" } }, - "required": [ - "id", - "model_name" - ], + "required": ["id", "model_name"], "additionalProperties": false } }, @@ -550,10 +589,18 @@ "items": { "type": "object", "properties": { + "id": { + "type": "string", + "description": "Provider row ID" + }, "name": { "type": "string", "description": "Provider name" }, + "description": { + "type": "string", + "description": "Operator-facing provider description" + }, "budget_id": { "type": "string", "description": "Associated budget ID" @@ -573,6 +620,21 @@ "store_raw_request_response": { "type": "boolean", "description": "Capture raw request/response for internal logging only; strip from API responses returned to clients (default: false)" + }, + "network_config": { + "$ref": "#/$defs/network_config" + }, + "proxy_config": { + "$ref": "#/$defs/proxy_config" + }, + "custom_provider_config": { + "$ref": "#/$defs/custom_provider_config" + }, + "concurrency_and_buffer_size": { + "$ref": "#/$defs/concurrency_and_buffer_size" + }, + "openai_config": { + "$ref": "#/$defs/openai_config" } }, "required": ["name"] @@ -612,12 +674,7 @@ }, "type": { "type": "string", - "enum": [ - "weaviate", - "redis", - "qdrant", - "pinecone" - ], + "enum": ["weaviate", "redis", "qdrant", "pinecone"], "description": "Vector store type (use \"redis\" for Redis or Valkey-compatible endpoints)" }, "config": { @@ -685,10 +742,7 @@ }, "type": { "type": "string", - "enum": [ - "sqlite", - "postgres" - ], + "enum": ["sqlite", "postgres"], "description": "Configuration store type" }, "config": { @@ -709,9 +763,7 @@ "description": "Database file path" } }, - "required": [ - "path" - ], + "required": ["path"], "additionalProperties": false } }, @@ -763,14 +815,7 @@ "default": 50 } }, - "required": [ - "host", - "port", - "user", - "password", - "db_name", - "ssl_mode" - ], + "required": ["host", "port", "user", "password", "db_name", "ssl_mode"], "additionalProperties": false } } @@ -789,10 +834,7 @@ }, "type": { "type": "string", - "enum": [ - "sqlite", - "postgres" - ], + "enum": ["sqlite", "postgres"], "description": "Logs store type" }, "config": { @@ -813,9 +855,7 @@ "description": "Database file path" } }, - "required": [ - "path" - ], + "required": ["path"], "additionalProperties": false } }, @@ -866,43 +906,124 @@ "default": 50 } }, - "required": [ - "host", - "port", - "user", - "password", - "db_name", - "ssl_mode" - ], + "required": ["host", "port", "user", "password", "db_name", "ssl_mode"], "additionalProperties": false } } ] + }, + "object_storage": { + "type": "object", + "description": "Optional object storage for offloading log payloads. When configured, large request/response payloads are stored in S3/GCS while the DB keeps only lightweight index data.", + "properties": { + "type": { + "type": "string", + "enum": ["s3", "gcs"], + "description": "Object storage backend type" + }, + "bucket": { + "type": "string", + "minLength": 1, + "description": "Bucket name. Supports env var reference (e.g. env.S3_BUCKET)" + }, + "prefix": { + "type": "string", + "description": "Key prefix for stored objects (default: bifrost)", + "default": "bifrost" + }, + "compress": { + "type": "boolean", + "description": "Enable gzip compression for stored objects. Default: false", + "default": false + } + }, + "required": ["type", "bucket"], + "if": { + "properties": { + "type": { + "const": "s3" + } + } + }, + "then": { + "properties": { + "type": true, + "bucket": true, + "prefix": true, + "region": { + "type": "string", + "description": "AWS region. Supports env var reference" + }, + "endpoint": { + "type": "string", + "description": "Custom S3-compatible endpoint for MinIO/R2. Supports env var reference" + }, + "access_key_id": { + "type": "string", + "description": "AWS access key ID. Omit to use default credential chain (instance role, env vars, etc.). Supports env var reference" + }, + "secret_access_key": { + "type": "string", + "description": "AWS secret access key. Supports env var reference" + }, + "session_token": { + "type": "string", + "description": "AWS session token for STS temporary credentials. Supports env var reference" + }, + "role_arn": { + "type": "string", + "description": "AWS IAM role ARN for STS AssumeRole. Works with static creds or instance role. Supports env var reference" + }, + "force_path_style": { + "type": "boolean", + "description": "Use path-style URLs for S3 (required for MinIO). Default: false", + "default": false + }, + "compress": true + }, + "dependentRequired": { + "access_key_id": ["secret_access_key"], + "secret_access_key": ["access_key_id"], + "session_token": ["access_key_id", "secret_access_key"] + }, + "additionalProperties": false + }, + "else": { + "properties": { + "type": true, + "bucket": true, + "prefix": true, + "credentials_json": { + "type": "string", + "description": "GCP service account credentials JSON or file path. Omit to use Application Default Credentials. Supports env var reference" + }, + "credentials": { + "type": "string", + "description": "Deprecated: use credentials_json. Kept for backwards compatibility." + }, + "project_id": { + "type": "string", + "description": "GCP project ID override. Supports env var reference" + }, + "compress": true + }, + "additionalProperties": false + } + }, + "retention_days": { + "type": "integer", + "minimum": 0, + "description": "Days to retain log entries. 0 disables retention-based cleanup." } }, "additionalProperties": false }, - "cluster_config": { - "$ref": "#/$defs/cluster_config" - }, - "saml_config": { - "$ref": "#/$defs/saml_config" - }, - "load_balancer_config": { - "$ref": "#/$defs/load_balancer_config" - }, - "guardrails_config": { - "$ref": "#/$defs/guardrails_config" - }, "plugins": { "type": "array", "description": "Plugins configuration", "items": { "type": "object", - "required": [ - "enabled", - "name" - ], + "required": ["enabled", "name"], "properties": { "enabled": { "type": "boolean", @@ -910,7 +1031,7 @@ }, "name": { "type": "string", - "description": "Name of the plugin (built-in: telemetry, logging, governance, maxim, semantic_cache, otel, or custom plugin name)" + "description": "Name of the plugin (built-in: telemetry, prompts, logging, governance, maxim, semantic_cache, otel, or custom plugin name)" }, "config": { "type": "object", @@ -930,8 +1051,8 @@ }, "placement": { "type": "string", - "enum": ["pre_builtin", "post_builtin"], - "description": "Whether this plugin runs before or after built-in plugins. Default: post_builtin", + "enum": ["pre_builtin", "post_builtin", "builtin"], + "description": "Whether this plugin runs before, after, or as a built-in. Default: post_builtin", "optional": true, "default": "post_builtin" }, @@ -952,9 +1073,7 @@ } }, "then": { - "required": [ - "config" - ], + "required": ["config"], "properties": { "config": { "type": "object", @@ -1030,9 +1149,7 @@ } }, "then": { - "required": [ - "config" - ], + "required": ["config"], "properties": { "config": { "type": "object", @@ -1064,9 +1181,7 @@ } }, "then": { - "required": [ - "config" - ], + "required": ["config"], "properties": { "config": { "type": "object", @@ -1102,9 +1217,7 @@ } }, "then": { - "required": [ - "config" - ], + "required": ["config"], "properties": { "config": { "type": "object", @@ -1119,9 +1232,7 @@ "description": "Optional default ID for the Maxim logger instance" } }, - "required": [ - "api_key" - ], + "required": ["api_key"], "additionalProperties": false } } @@ -1136,9 +1247,7 @@ } }, "then": { - "required": [ - "config" - ], + "required": ["config"], "properties": { "config": { "type": "object", @@ -1189,7 +1298,7 @@ "oneOf": [ { "type": "string", - "pattern": "^[0-9]+(ns|us|µs|ms|s|m|h)$" + "pattern": "^[0-9]+(ns|us|\u00b5s|ms|s|m|h)$" }, { "type": "integer", @@ -1234,9 +1343,7 @@ "description": "Exclude system prompt in cache key (default: false)" } }, - "required": [ - "dimension" - ], + "required": ["dimension"], "allOf": [ { "if": { @@ -1246,15 +1353,10 @@ "minLength": 1 } }, - "required": [ - "provider" - ] + "required": ["provider"] }, "then": { - "required": [ - "provider", - "embedding_model" - ], + "required": ["provider", "embedding_model"], "properties": { "dimension": { "type": "integer", @@ -1264,9 +1366,7 @@ }, "else": { "not": { - "required": [ - "embedding_model" - ] + "required": ["embedding_model"] }, "properties": { "dimension": { @@ -1290,9 +1390,7 @@ } }, "then": { - "required": [ - "config" - ], + "required": ["config"], "properties": { "config": { "type": "object", @@ -1318,17 +1416,12 @@ "trace_type": { "type": "string", "description": "Type of trace to use for the OTEL collector", - "enum": [ - "otel" - ] + "enum": ["genai_extension", "vercel", "open_inference"] }, "protocol": { "type": "string", "description": "Protocol to use for the OTEL collector", - "enum": [ - "http", - "grpc" - ] + "enum": ["http", "grpc"] }, "metrics_enabled": { "type": "boolean", @@ -1370,11 +1463,7 @@ "description": "Skip TLS verification (ignored if tls_ca_cert is set)" } }, - "required": [ - "collector_url", - "trace_type", - "protocol" - ], + "required": ["collector_url", "trace_type", "protocol"], "additionalProperties": false } } @@ -1389,9 +1478,7 @@ } }, "then": { - "required": [ - "config" - ], + "required": ["config"], "properties": { "config": { "type": "object", @@ -1437,14 +1524,26 @@ "additionalProperties": false } }, + "websocket": { + "$ref": "#/$defs/websocket_config" + }, + "guardrails_config": { + "$ref": "#/$defs/guardrails_config" + }, "audit_logs": { "$ref": "#/$defs/audit_logs_config" }, + "cluster_config": { + "$ref": "#/$defs/cluster_config" + }, + "load_balancer_config": { + "$ref": "#/$defs/load_balancer_config" + }, "large_payload_optimization": { "$ref": "#/$defs/large_payload_optimization" }, - "websocket": { - "$ref": "#/$defs/websocket_config" + "scim_config": { + "$ref": "#/$defs/scim_config" } }, "additionalProperties": false, @@ -1503,6 +1602,11 @@ "type": "string", "description": "CEL (Common Expression Language) expression for rule evaluation" }, + "chain_rule": { + "type": "boolean", + "default": false, + "description": "If true, re-evaluates routing chain after this rule matches" + }, "targets": { "type": "array", "minItems": 1, @@ -1527,7 +1631,7 @@ "default": "global" }, "scope_id": { - "type": ["string", "null"], + "type": "string", "description": "Entity ID for non-global scopes (required for non-global scope)" }, "priority": { @@ -1542,7 +1646,24 @@ } }, "required": ["id", "name", "targets"], - "additionalProperties": false + "additionalProperties": false, + "if": { + "properties": { + "scope": { + "enum": ["team", "customer", "virtual_key"] + } + }, + "required": ["scope"] + }, + "then": { + "required": ["scope_id"], + "properties": { + "scope_id": { + "type": "string", + "minLength": 1 + } + } + } }, "virtual_key_provider_config": { "type": "object", @@ -1561,276 +1682,30 @@ "description": "Provider name" }, "weight": { - "type": "number", - "description": "Weight for load balancing", - "default": 1.0 + "type": ["number", "null"], + "description": "Weight for load balancing (null opts out of weighted routing)", + "default": null }, "allowed_models": { "type": "array", - "description": "Allowed models for this provider config (empty means all models allowed)", + "description": "Allowed models for this provider config. Use [\"*\"] to allow all models; empty array denies all (deny-by-default).", "items": { "type": "string" } }, - "budget_id": { - "type": "string", - "description": "Associated budget ID" - }, "rate_limit_id": { "type": "string", "description": "Associated rate limit ID" }, - "keys": { + "key_ids": { "type": "array", - "description": "Provider keys for this config (empty means all keys allowed for this provider)", + "description": "Key identifiers allowed for this provider config. Use [\"*\"] to allow all keys; empty array denies all (deny-by-default). In config.json, values are key names. Via the API, values are key UUIDs.", "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Key database ID (auto-generated)" - }, - "key_id": { - "type": "string", - "description": "Key UUID identifier" - }, - "name": { - "type": "string", - "description": "Key name (must be unique)" - }, - "value": { - "type": "string", - "description": "API key value (can use env. prefix)" - }, - "models": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Supported models for this key" - }, - "weight": { - "type": "number", - "minimum": 0, - "default": 1.0, - "description": "Weight for load balancing" - }, - "azure_key_config": { - "type": "object", - "properties": { - "endpoint": { - "type": "string", - "description": "Azure endpoint (can use env. prefix)" - }, - "deployments": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Model to deployment mappings" - }, - "api_version": { - "type": "string", - "description": "Azure API version" - } - }, - "required": [ - "endpoint" - ], - "additionalProperties": false - }, - "vertex_key_config": { - "type": "object", - "properties": { - "project_id": { - "type": "string", - "description": "Google Cloud project ID (can use env. prefix)" - }, - "project_number": { - "type": "string", - "description": "Google Cloud project number" - }, - "region": { - "type": "string", - "description": "Google Cloud region" - }, - "auth_credentials": { - "type": "string", - "description": "Authentication credentials (can use env. prefix)" - }, - "deployments": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Model to deployment mappings" - } - }, - "required": [ - "project_id", - "region" - ], - "additionalProperties": false - }, - "bedrock_key_config": { - "type": "object", - "properties": { - "access_key": { - "type": "string", - "description": "AWS access key (can use env. prefix)" - }, - "secret_key": { - "type": "string", - "description": "AWS secret key (can use env. prefix)" - }, - "session_token": { - "type": "string", - "description": "AWS session token (can use env. prefix)" - }, - "region": { - "type": "string", - "description": "AWS region" - }, - "arn": { - "type": "string", - "description": "AWS ARN" - }, - "role_arn": { - "type": "string", - "description": "AWS IAM role ARN for AssumeRole (can use env. prefix)" - }, - "external_id": { - "type": "string", - "description": "External ID for AssumeRole (can use env. prefix)" - }, - "session_name": { - "type": "string", - "description": "Role session name for AssumeRole (can use env. prefix)" - }, - "deployments": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Model to deployment mappings" - }, - "batch_s3_config": { - "type": "object", - "description": "S3 bucket configuration for Bedrock batch operations", - "properties": { - "buckets": { - "type": "array", - "description": "List of S3 bucket configurations", - "items": { - "type": "object", - "properties": { - "bucket_name": { - "type": "string", - "description": "S3 bucket name" - }, - "prefix": { - "type": "string", - "description": "S3 key prefix for batch files" - }, - "is_default": { - "type": "boolean", - "description": "Whether this is the default bucket for batch operations" - } - }, - "required": ["bucket_name"], - "additionalProperties": false - } - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - "vllm_key_config": { - "type": "object", - "properties": { - "url": { - "type": "string", - "minLength": 1, - "description": "VLLM server base URL (can use env. prefix)" - }, - "model_name": { - "type": "string", - "minLength": 1, - "description": "Exact model name served on this VLLM instance" - } - }, - "required": [ - "url", - "model_name" - ], - "additionalProperties": false - } - }, - "oneOf": [ - { - "not": { - "anyOf": [ - { "required": ["azure_key_config"] }, - { "required": ["vertex_key_config"] }, - { "required": ["bedrock_key_config"] }, - { "required": ["vllm_key_config"] } - ] - } - }, - { - "required": ["azure_key_config"], - "not": { - "anyOf": [ - { "required": ["vertex_key_config"] }, - { "required": ["bedrock_key_config"] }, - { "required": ["vllm_key_config"] } - ] - } - }, - { - "required": ["vertex_key_config"], - "not": { - "anyOf": [ - { "required": ["azure_key_config"] }, - { "required": ["bedrock_key_config"] }, - { "required": ["vllm_key_config"] } - ] - } - }, - { - "required": ["bedrock_key_config"], - "not": { - "anyOf": [ - { "required": ["azure_key_config"] }, - { "required": ["vertex_key_config"] }, - { "required": ["vllm_key_config"] } - ] - } - }, - { - "required": ["vllm_key_config"], - "not": { - "anyOf": [ - { "required": ["azure_key_config"] }, - { "required": ["vertex_key_config"] }, - { "required": ["bedrock_key_config"] } - ] - } - } - ], - "required": [ - "key_id", - "name", - "value" - ] + "type": "string" } } }, - "required": [ - "provider" - ], + "required": ["provider"], "additionalProperties": false }, "virtual_key_mcp_config": { @@ -1847,19 +1722,20 @@ }, "mcp_client_id": { "type": "integer", - "description": "Associated MCP client ID" + "description": "Associated MCP client ID (database format)" + }, + "mcp_client_name": { + "type": "string", + "description": "MCP client name (config file format \u2014 resolved to mcp_client_id at startup)" }, "tools_to_execute": { "type": "array", - "description": "Tools to execute for this MCP config", + "description": "Include-only list of tools this Virtual Key is permitted to execute from this MCP client. ['*'] means all tools allowed, [] means no tools allowed (deny-by-default).", "items": { "type": "string" } } }, - "required": [ - "mcp_client_id" - ], "additionalProperties": false }, "auth_config": { @@ -1904,169 +1780,78 @@ }, "additionalProperties": false }, - "pricing_override_match_type": { - "type": "string", - "enum": [ - "exact", - "wildcard", - "regex" - ] - }, - "pricing_override_request_type": { - "type": "string", - "enum": [ - "text_completion", - "text_completion_stream", - "chat_completion", - "chat_completion_stream", - "responses", - "responses_stream", - "embedding", - "rerank", - "ocr", - "speech", - "speech_stream", - "transcription", - "transcription_stream", - "image_generation", - "image_generation_stream" - ] - }, - "provider_pricing_override": { + "network_config": { "type": "object", "properties": { - "model_pattern": { + "base_url": { "type": "string", - "minLength": 1 + "format": "uri", + "description": "Base URL for the provider (optional, required for Ollama)" }, - "match_type": { - "$ref": "#/$defs/pricing_override_match_type" + "extra_headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Additional headers to send with requests" }, - "request_types": { - "type": "array", - "items": { - "$ref": "#/$defs/pricing_override_request_type" - } + "default_request_timeout_in_seconds": { + "type": "integer", + "minimum": 1, + "description": "Default request timeout in seconds" }, - "input_cost_per_token": { "type": "number", "minimum": 0 }, - "output_cost_per_token": { "type": "number", "minimum": 0 }, - "input_cost_per_video_per_second": { "type": "number", "minimum": 0 }, - "input_cost_per_audio_per_second": { "type": "number", "minimum": 0 }, - "input_cost_per_character": { "type": "number", "minimum": 0 }, - "output_cost_per_character": { "type": "number", "minimum": 0 }, - "input_cost_per_token_above_128k_tokens": { "type": "number", "minimum": 0 }, - "input_cost_per_character_above_128k_tokens": { "type": "number", "minimum": 0 }, - "input_cost_per_image_above_128k_tokens": { "type": "number", "minimum": 0 }, - "input_cost_per_video_per_second_above_128k_tokens": { "type": "number", "minimum": 0 }, - "input_cost_per_audio_per_second_above_128k_tokens": { "type": "number", "minimum": 0 }, - "output_cost_per_token_above_128k_tokens": { "type": "number", "minimum": 0 }, - "output_cost_per_character_above_128k_tokens": { "type": "number", "minimum": 0 }, - "input_cost_per_token_above_200k_tokens": { "type": "number", "minimum": 0 }, - "output_cost_per_token_above_200k_tokens": { "type": "number", "minimum": 0 }, - "cache_creation_input_token_cost_above_200k_tokens": { "type": "number", "minimum": 0 }, - "cache_read_input_token_cost_above_200k_tokens": { "type": "number", "minimum": 0 }, - "cache_read_input_token_cost": { "type": "number", "minimum": 0 }, - "cache_creation_input_token_cost": { "type": "number", "minimum": 0 }, - "input_cost_per_token_batches": { "type": "number", "minimum": 0 }, - "output_cost_per_token_batches": { "type": "number", "minimum": 0 }, - "input_cost_per_image_token": { "type": "number", "minimum": 0 }, - "output_cost_per_image_token": { "type": "number", "minimum": 0 }, - "input_cost_per_image": { "type": "number", "minimum": 0 }, - "output_cost_per_image": { "type": "number", "minimum": 0 }, - "cache_read_input_image_token_cost": { "type": "number", "minimum": 0 } - }, - "required": [ - "model_pattern", - "match_type" - ], - "additionalProperties": false - }, - "custom_provider_config": { - "type": "object", - "description": "Custom provider configuration for extending or customizing provider behavior", - "properties": { - "is_key_less": { + "max_retries": { + "type": "integer", + "minimum": 0, + "description": "Maximum number of retries" + }, + "retry_backoff_initial": { + "type": "integer", + "minimum": 0, + "description": "Initial retry backoff in milliseconds" + }, + "retry_backoff_max": { + "type": "integer", + "minimum": 0, + "description": "Maximum retry backoff in milliseconds" + }, + "enforce_http2": { "type": "boolean", - "description": "Whether the custom provider requires a key" + "description": "Force HTTP/2 on provider connections (relevant for Bedrock and other net/http-based providers)" }, - "base_provider_type": { + "insecure_skip_verify": { + "type": "boolean", + "description": "Disable TLS certificate verification for provider connections. This bypasses server certificate validation and should be used only as a last resort when a trusted CA chain cannot be configured. Prefer ca_cert_pem for self-signed or private CA deployments." + }, + "ca_cert_pem": { "type": "string", - "description": "Base provider type to extend" + "description": "PEM-encoded CA certificate to trust for provider endpoint connections (e.g. self-signed or internal CA)" }, - "allowed_requests": { - "type": "object", - "description": "Allowed request types for the custom provider", - "properties": { - "list_models": { "type": "boolean" }, - "text_completion": { "type": "boolean" }, - "text_completion_stream": { "type": "boolean" }, - "chat_completion": { "type": "boolean" }, - "chat_completion_stream": { "type": "boolean" }, - "responses": { "type": "boolean" }, - "responses_stream": { "type": "boolean" }, - "count_tokens": { "type": "boolean" }, - "embedding": { "type": "boolean" }, - "rerank": { "type": "boolean" }, - "ocr": { "type": "boolean" }, - "speech": { "type": "boolean" }, - "speech_stream": { "type": "boolean" }, - "transcription": { "type": "boolean" }, - "transcription_stream": { "type": "boolean" }, - "image_generation": { "type": "boolean" }, - "image_generation_stream": { "type": "boolean" }, - "image_edit": { "type": "boolean" }, - "image_edit_stream": { "type": "boolean" }, - "image_variation": { "type": "boolean" }, - "video_generation": { "type": "boolean" }, - "video_retrieve": { "type": "boolean" }, - "video_download": { "type": "boolean" }, - "video_delete": { "type": "boolean" }, - "video_list": { "type": "boolean" }, - "video_remix": { "type": "boolean" }, - "batch_create": { "type": "boolean" }, - "batch_list": { "type": "boolean" }, - "batch_retrieve": { "type": "boolean" }, - "batch_cancel": { "type": "boolean" }, - "batch_delete": { "type": "boolean" }, - "batch_results": { "type": "boolean" }, - "file_upload": { "type": "boolean" }, - "file_list": { "type": "boolean" }, - "file_retrieve": { "type": "boolean" }, - "file_delete": { "type": "boolean" }, - "file_content": { "type": "boolean" }, - "container_create": { "type": "boolean" }, - "container_list": { "type": "boolean" }, - "container_retrieve": { "type": "boolean" }, - "container_delete": { "type": "boolean" }, - "container_file_create": { "type": "boolean" }, - "container_file_list": { "type": "boolean" }, - "container_file_retrieve": { "type": "boolean" }, - "container_file_content": { "type": "boolean" }, - "container_file_delete": { "type": "boolean" }, - "passthrough": { "type": "boolean" }, - "passthrough_stream": { "type": "boolean" } - }, - "additionalProperties": false + "stream_idle_timeout_in_seconds": { + "type": "integer", + "minimum": 5, + "maximum": 3600, + "description": "Idle timeout per stream chunk in seconds. If no data is received for this many seconds, the stream is closed. Default: 60." }, - "request_path_overrides": { + "max_conns_per_host": { + "type": "integer", + "minimum": 1, + "maximum": 10000, + "description": "Maximum number of TCP connections per provider host. For HTTP/2 (e.g. Bedrock), each connection supports ~100 concurrent streams. Default: 5000." + }, + "beta_header_overrides": { "type": "object", - "description": "Mapping of request type to custom path overriding the default provider path", "additionalProperties": { - "type": "string" - } + "type": "boolean" + }, + "description": "Override default Anthropic beta header support per provider. Keys are header prefixes (e.g. 'redact-thinking-'), values are true (supported) or false (unsupported). Headers not listed use the built-in defaults." } }, - "required": ["base_provider_type"], "additionalProperties": false }, - "network_config": { + "network_config_without_base_url": { "type": "object", "properties": { - "base_url": { - "type": "string", - "format": "uri", - "description": "Base URL for the provider (optional, required for Ollama)" - }, "extra_headers": { "type": "object", "additionalProperties": { @@ -2084,16 +1869,20 @@ "minimum": 0, "description": "Maximum number of retries" }, - "retry_backoff_initial_ms": { + "retry_backoff_initial": { "type": "integer", "minimum": 0, "description": "Initial retry backoff in milliseconds" }, - "retry_backoff_max_ms": { + "retry_backoff_max": { "type": "integer", "minimum": 0, "description": "Maximum retry backoff in milliseconds" }, + "enforce_http2": { + "type": "boolean", + "description": "Force HTTP/2 on provider connections (relevant for Bedrock and other net/http-based providers)" + }, "insecure_skip_verify": { "type": "boolean", "description": "Disable TLS certificate verification for provider connections. This bypasses server certificate validation and should be used only as a last resort when a trusted CA chain cannot be configured. Prefer ca_cert_pem for self-signed or private CA deployments." @@ -2124,7 +1913,18 @@ }, "additionalProperties": false }, - "concurrency_config": { + "openai_config": { + "type": "object", + "description": "OpenAI-specific provider settings", + "properties": { + "disable_store": { + "type": "boolean", + "description": "Disable OpenAI Responses API conversation storage." + } + }, + "additionalProperties": false + }, + "concurrency_and_buffer_size": { "type": "object", "properties": { "concurrency": { @@ -2138,10 +1938,7 @@ "description": "Buffer size for requests" } }, - "required": [ - "concurrency", - "buffer_size" - ], + "required": ["concurrency", "buffer_size"], "additionalProperties": false }, "base_key": { @@ -2160,8 +1957,7 @@ "items": { "type": "string" }, - "default": [], - "description": "Supported models for this key" + "description": "Models this key can access. Use [\"*\"] to allow all models; empty array denies all (deny-by-default)." }, "weight": { "type": "number", @@ -2172,12 +1968,20 @@ "type": "boolean", "description": "Whether this key can be used for batch API operations (default: false)", "default": false + }, + "aliases": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "propertyNames": { + "minLength": 1 + }, + "description": "Model alias mappings: maps a model name to a provider-specific identifier (deployment name, inference profile ID, fine-tuned model ID, etc.)" } }, - "required": [ - "name", - "weight" - ] + "required": ["name", "weight"] }, "bedrock_key": { "allOf": [ @@ -2260,15 +2064,10 @@ "additionalProperties": false } }, - "required": [ - "region" - ], + "required": ["region"], "additionalProperties": false } - }, - "required": [ - "bedrock_key_config" - ] + } } ] }, @@ -2294,16 +2093,83 @@ "description": "Exact model name served on this VLLM instance" } }, - "required": [ - "url", - "model_name" - ], + "required": ["url", "model_name"], "additionalProperties": false } }, - "required": [ - "vllm_key_config" - ] + "required": ["vllm_key_config"] + } + ] + }, + "replicate_key": { + "allOf": [ + { + "$ref": "#/$defs/base_key" + }, + { + "type": "object", + "properties": { + "replicate_key_config": { + "type": "object", + "properties": { + "use_deployments_endpoint": { + "type": "boolean", + "description": "Whether to use the deployments endpoint instead of the models endpoint (default: false)" + } + }, + "additionalProperties": false + } + } + } + ] + }, + "ollama_key": { + "allOf": [ + { + "$ref": "#/$defs/base_key" + }, + { + "type": "object", + "properties": { + "ollama_key_config": { + "type": "object", + "properties": { + "url": { + "type": "string", + "minLength": 1, + "description": "Ollama server base URL (can use env. prefix)" + } + }, + "required": ["url"], + "additionalProperties": false + } + }, + "required": ["ollama_key_config"] + } + ] + }, + "sgl_key": { + "allOf": [ + { + "$ref": "#/$defs/base_key" + }, + { + "type": "object", + "properties": { + "sgl_key_config": { + "type": "object", + "properties": { + "url": { + "type": "string", + "minLength": 1, + "description": "SGLang server base URL (can use env. prefix)" + } + }, + "required": ["url"], + "additionalProperties": false + } + }, + "required": ["sgl_key_config"] } ] }, @@ -2322,28 +2188,16 @@ "type": "string", "description": "Azure endpoint (can use env. prefix)" }, - "deployments": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Model to deployment mappings" - }, "api_version": { "type": "string", "description": "Azure API version" } }, - "required": [ - "endpoint", - "api_version" - ], + "required": ["endpoint", "api_version"], "additionalProperties": false } }, - "required": [ - "azure_key_config" - ] + "required": ["azure_key_config"] } ] }, @@ -2373,35 +2227,140 @@ "auth_credentials": { "type": "string", "description": "Authentication credentials (can use env. prefix)" - }, - "deployments": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Model to deployment mappings" } }, - "required": [ - "project_id", - "region" - ], + "required": ["project_id", "region"], "additionalProperties": false } }, - "required": [ - "vertex_key_config" - ] + "required": ["vertex_key_config"] + } + ] + }, + "provider": { + "type": "object", + "properties": { + "keys": { + "type": "array", + "items": { + "$ref": "#/$defs/base_key" + }, + "minItems": 1, + "description": "API keys for this provider" + }, + "network_config": { + "$ref": "#/$defs/network_config" + }, + "concurrency_and_buffer_size": { + "$ref": "#/$defs/concurrency_and_buffer_size" + }, + "proxy_config": { + "$ref": "#/$defs/proxy_config" + }, + "send_back_raw_request": { + "type": "boolean", + "description": "Include raw request in BifrostResponse (default: false)" + }, + "send_back_raw_response": { + "type": "boolean", + "description": "Include raw response in BifrostResponse (default: false)" + }, + "store_raw_request_response": { + "type": "boolean", + "description": "Capture raw request/response for internal logging only; strip from API responses returned to clients (default: false)" + }, + "custom_provider_config": { + "$ref": "#/$defs/custom_provider_config" + } + }, + "required": ["keys"], + "additionalProperties": false + }, + "provider_with_bedrock_config": { + "type": "object", + "properties": { + "keys": { + "type": "array", + "items": { + "$ref": "#/$defs/bedrock_key" + }, + "minItems": 1, + "description": "API keys for this provider" + }, + "network_config": { + "$ref": "#/$defs/network_config" + }, + "concurrency_and_buffer_size": { + "$ref": "#/$defs/concurrency_and_buffer_size" + }, + "proxy_config": { + "$ref": "#/$defs/proxy_config" + }, + "send_back_raw_request": { + "type": "boolean", + "description": "Include raw request in BifrostResponse (default: false)" + }, + "send_back_raw_response": { + "type": "boolean", + "description": "Include raw response in BifrostResponse (default: false)" + }, + "store_raw_request_response": { + "type": "boolean", + "description": "Capture raw request/response for internal logging only; strip from API responses returned to clients (default: false)" + }, + "custom_provider_config": { + "$ref": "#/$defs/custom_provider_config" + } + }, + "required": ["keys"], + "additionalProperties": false + }, + "provider_with_vllm_config": { + "type": "object", + "properties": { + "keys": { + "type": "array", + "items": { + "$ref": "#/$defs/vllm_key" + }, + "minItems": 1, + "description": "API keys for this provider" + }, + "network_config": { + "$ref": "#/$defs/network_config_without_base_url" + }, + "concurrency_and_buffer_size": { + "$ref": "#/$defs/concurrency_and_buffer_size" + }, + "proxy_config": { + "$ref": "#/$defs/proxy_config" + }, + "send_back_raw_request": { + "type": "boolean", + "description": "Include raw request in BifrostResponse (default: false)" + }, + "send_back_raw_response": { + "type": "boolean", + "description": "Include raw response in BifrostResponse (default: false)" + }, + "store_raw_request_response": { + "type": "boolean", + "description": "Capture raw request/response for internal logging only; strip from API responses returned to clients (default: false)" + }, + "custom_provider_config": { + "$ref": "#/$defs/custom_provider_config" } - ] + }, + "required": ["keys"], + "additionalProperties": false }, - "provider": { + "provider_with_replicate_config": { "type": "object", "properties": { "keys": { "type": "array", "items": { - "$ref": "#/$defs/base_key" + "$ref": "#/$defs/replicate_key" }, "minItems": 1, "description": "API keys for this provider" @@ -2410,7 +2369,7 @@ "$ref": "#/$defs/network_config" }, "concurrency_and_buffer_size": { - "$ref": "#/$defs/concurrency_config" + "$ref": "#/$defs/concurrency_and_buffer_size" }, "proxy_config": { "$ref": "#/$defs/proxy_config" @@ -2429,27 +2388,18 @@ }, "custom_provider_config": { "$ref": "#/$defs/custom_provider_config" - }, - "pricing_overrides": { - "type": "array", - "items": { - "$ref": "#/$defs/provider_pricing_override" - }, - "description": "Provider-level pricing overrides matched by model pattern" } }, - "required": [ - "keys" - ], + "required": ["keys"], "additionalProperties": false }, - "provider_with_bedrock_config": { + "provider_with_azure_config": { "type": "object", "properties": { "keys": { "type": "array", "items": { - "$ref": "#/$defs/bedrock_key" + "$ref": "#/$defs/azure_key" }, "minItems": 1, "description": "API keys for this provider" @@ -2458,7 +2408,7 @@ "$ref": "#/$defs/network_config" }, "concurrency_and_buffer_size": { - "$ref": "#/$defs/concurrency_config" + "$ref": "#/$defs/concurrency_and_buffer_size" }, "proxy_config": { "$ref": "#/$defs/proxy_config" @@ -2477,27 +2427,18 @@ }, "custom_provider_config": { "$ref": "#/$defs/custom_provider_config" - }, - "pricing_overrides": { - "type": "array", - "items": { - "$ref": "#/$defs/provider_pricing_override" - }, - "description": "Provider-level pricing overrides matched by model pattern" } }, - "required": [ - "keys" - ], + "required": ["keys"], "additionalProperties": false }, - "provider_with_vllm_config": { + "provider_with_vertex_config": { "type": "object", "properties": { "keys": { "type": "array", "items": { - "$ref": "#/$defs/vllm_key" + "$ref": "#/$defs/vertex_key" }, "minItems": 1, "description": "API keys for this provider" @@ -2506,7 +2447,7 @@ "$ref": "#/$defs/network_config" }, "concurrency_and_buffer_size": { - "$ref": "#/$defs/concurrency_config" + "$ref": "#/$defs/concurrency_and_buffer_size" }, "proxy_config": { "$ref": "#/$defs/proxy_config" @@ -2525,36 +2466,27 @@ }, "custom_provider_config": { "$ref": "#/$defs/custom_provider_config" - }, - "pricing_overrides": { - "type": "array", - "items": { - "$ref": "#/$defs/provider_pricing_override" - }, - "description": "Provider-level pricing overrides matched by model pattern" } }, - "required": [ - "keys" - ], + "required": ["keys"], "additionalProperties": false }, - "provider_with_azure_config": { + "provider_with_ollama_config": { "type": "object", "properties": { "keys": { "type": "array", "items": { - "$ref": "#/$defs/azure_key" + "$ref": "#/$defs/ollama_key" }, "minItems": 1, "description": "API keys for this provider" }, "network_config": { - "$ref": "#/$defs/network_config" + "$ref": "#/$defs/network_config_without_base_url" }, "concurrency_and_buffer_size": { - "$ref": "#/$defs/concurrency_config" + "$ref": "#/$defs/concurrency_and_buffer_size" }, "proxy_config": { "$ref": "#/$defs/proxy_config" @@ -2573,36 +2505,27 @@ }, "custom_provider_config": { "$ref": "#/$defs/custom_provider_config" - }, - "pricing_overrides": { - "type": "array", - "items": { - "$ref": "#/$defs/provider_pricing_override" - }, - "description": "Provider-level pricing overrides matched by model pattern" } }, - "required": [ - "keys" - ], + "required": ["keys"], "additionalProperties": false }, - "provider_with_vertex_config": { + "provider_with_sgl_config": { "type": "object", "properties": { "keys": { "type": "array", "items": { - "$ref": "#/$defs/vertex_key" + "$ref": "#/$defs/sgl_key" }, "minItems": 1, "description": "API keys for this provider" }, "network_config": { - "$ref": "#/$defs/network_config" + "$ref": "#/$defs/network_config_without_base_url" }, "concurrency_and_buffer_size": { - "$ref": "#/$defs/concurrency_config" + "$ref": "#/$defs/concurrency_and_buffer_size" }, "proxy_config": { "$ref": "#/$defs/proxy_config" @@ -2621,18 +2544,9 @@ }, "custom_provider_config": { "$ref": "#/$defs/custom_provider_config" - }, - "pricing_overrides": { - "type": "array", - "items": { - "$ref": "#/$defs/provider_pricing_override" - }, - "description": "Provider-level pricing overrides matched by model pattern" } }, - "required": [ - "keys" - ], + "required": ["keys"], "additionalProperties": false }, "mcp_client_config": { @@ -2652,12 +2566,7 @@ }, "connection_type": { "type": "string", - "enum": [ - "stdio", - "websocket", - "http", - "sse" - ], + "enum": ["stdio", "http", "sse", "inprocess"], "description": "Connection type for MCP client" }, "connection_string": { @@ -2666,12 +2575,12 @@ }, "auth_type": { "type": "string", - "enum": ["none", "headers", "oauth"], + "enum": ["none", "headers", "oauth", "per_user_oauth"], "description": "Authentication type for MCP connection" }, "oauth_config_id": { "type": "string", - "description": "OAuth config ID reference (for oauth auth type)" + "description": "OAuth config ID reference (required when auth_type is 'oauth' or 'per_user_oauth')" }, "headers": { "type": "object", @@ -2702,37 +2611,7 @@ "description": "Environment variables" } }, - "required": [ - "command" - ], - "additionalProperties": false - }, - "websocket_config": { - "type": "object", - "properties": { - "url": { - "type": "string", - "format": "uri", - "description": "WebSocket URL" - } - }, - "required": [ - "url" - ], - "additionalProperties": false - }, - "http_config": { - "type": "object", - "properties": { - "url": { - "type": "string", - "format": "uri", - "description": "HTTP URL" - } - }, - "required": [ - "url" - ], + "required": ["command"], "additionalProperties": false }, "tools_to_execute": { @@ -2753,6 +2632,13 @@ "type": "string", "description": "Per-client override for tool sync interval (Go duration, e.g. '10m', '1h', 0 = use global, negative = disabled)" }, + "allowed_extra_headers": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Allowlist of request-level headers that callers may forward to this MCP server at execution time. Use ['*'] to allow all headers." + }, "is_ping_available": { "type": "boolean", "description": "Whether the MCP server supports ping for health checks (default: true)", @@ -2765,13 +2651,26 @@ "type": "number", "minimum": 0 } + }, + "allow_on_all_virtual_keys": { + "type": "boolean", + "description": "When true, this MCP server is accessible to all virtual keys without requiring explicit per-key assignment. All tools are allowed by default. If a virtual key has an explicit MCP config for this server, that config takes precedence and overrides this behaviour.", + "default": false } }, - "required": [ - "name", - "connection_type" - ], + "required": ["name", "connection_type"], "additionalProperties": false, + "if": { + "properties": { + "auth_type": { + "enum": ["oauth", "per_user_oauth"] + } + }, + "required": ["auth_type"] + }, + "then": { + "required": ["oauth_config_id"] + }, "oneOf": [ { "properties": { @@ -2779,9 +2678,7 @@ "const": "stdio" } }, - "required": [ - "stdio_config" - ] + "required": ["stdio_config"] }, { "properties": { @@ -2789,9 +2686,7 @@ "const": "websocket" } }, - "required": [ - "websocket_config" - ] + "required": ["websocket_config"] }, { "properties": { @@ -2800,8 +2695,12 @@ } }, "anyOf": [ - { "required": ["http_config"] }, - { "required": ["connection_string"] } + { + "required": ["http_config"] + }, + { + "required": ["connection_string"] + } ] }, { @@ -2810,9 +2709,7 @@ "const": "sse" } }, - "required": [ - "connection_string" - ] + "required": ["connection_string"] } ] }, @@ -2835,6 +2732,11 @@ "type": "string", "enum": ["server", "tool"], "description": "How tools are exposed in VFS for code execution" + }, + "disable_auto_tool_inject": { + "type": "boolean", + "description": "When true, MCP tools are not automatically injected into requests. Tools are only included when explicitly specified via request context filters or headers, such as x-bf-mcp-include-tools or x-bf-mcp-include-clients.", + "default": false } } }, @@ -2873,7 +2775,7 @@ }, "timeout": { "type": "string", - "pattern": "^[0-9]+(ns|us|µs|ms|s|m|h)$", + "pattern": "^[0-9]+(ns|us|\u00b5s|ms|s|m|h)$", "description": "Timeout for Weaviate operations (e.g., '5s')" }, "class_name": { @@ -2888,10 +2790,7 @@ "description": "Properties for Weaviate vector store" } }, - "required": [ - "scheme", - "host" - ], + "required": ["scheme", "host"], "additionalProperties": false }, "redis_config": { @@ -2952,38 +2851,36 @@ }, "conn_max_lifetime": { "type": "string", - "pattern": "^[0-9]+(ns|us|µs|ms|s|m|h)$", + "pattern": "^[0-9]+(ns|us|\u00b5s|ms|s|m|h)$", "description": "Connection maximum lifetime (e.g., '30m')" }, "conn_max_idle_time": { "type": "string", - "pattern": "^[0-9]+(ns|us|µs|ms|s|m|h)$", + "pattern": "^[0-9]+(ns|us|\u00b5s|ms|s|m|h)$", "description": "Connection maximum idle time (e.g., '5m')" }, "dial_timeout": { "type": "string", - "pattern": "^[0-9]+(ns|us|µs|ms|s|m|h)$", + "pattern": "^[0-9]+(ns|us|\u00b5s|ms|s|m|h)$", "description": "Timeout for socket connection (e.g., '5s')" }, "read_timeout": { "type": "string", - "pattern": "^[0-9]+(ns|us|µs|ms|s|m|h)$", + "pattern": "^[0-9]+(ns|us|\u00b5s|ms|s|m|h)$", "description": "Timeout for socket reads (e.g., '3s')" }, "write_timeout": { "type": "string", - "pattern": "^[0-9]+(ns|us|µs|ms|s|m|h)$", + "pattern": "^[0-9]+(ns|us|\u00b5s|ms|s|m|h)$", "description": "Timeout for socket writes (e.g., '3s')" }, "context_timeout": { "type": "string", - "pattern": "^[0-9]+(ns|us|µs|ms|s|m|h)$", + "pattern": "^[0-9]+(ns|us|\u00b5s|ms|s|m|h)$", "description": "Timeout for Redis operations (e.g., '10s')" } }, - "required": [ - "addr" - ], + "required": ["addr"], "additionalProperties": false }, "qdrant_config": { @@ -3009,9 +2906,7 @@ "default": false } }, - "required": [ - "host" - ], + "required": ["host"], "additionalProperties": false }, "pinecone_config": { @@ -3027,10 +2922,7 @@ "description": "Index host URL from Pinecone console - REQUIRED (e.g., your-index.svc.environment.pinecone.io)" } }, - "required": [ - "api_key", - "index_host" - ], + "required": ["api_key", "index_host"], "additionalProperties": false }, "proxy_config": { @@ -3039,12 +2931,7 @@ "properties": { "type": { "type": "string", - "enum": [ - "none", - "http", - "socks5", - "environment" - ], + "enum": ["none", "http", "socks5", "environment"], "description": "Type of proxy to use" }, "url": { @@ -3065,9 +2952,7 @@ "description": "PEM-encoded CA certificate to trust for TLS connections through the proxy (for SSL-intercepting proxies)" } }, - "required": [ - "type" - ], + "required": ["type"], "additionalProperties": false }, "cluster_config": { @@ -3120,18 +3005,11 @@ "description": "Number of failed probes before marking as failed" } }, - "required": [ - "timeout_seconds", - "success_threshold", - "failure_threshold" - ], + "required": ["timeout_seconds", "success_threshold", "failure_threshold"], "additionalProperties": false } }, - "required": [ - "port", - "config" - ], + "required": ["port", "config"], "additionalProperties": false }, "discovery": { @@ -3144,14 +3022,7 @@ }, "type": { "type": "string", - "enum": [ - "kubernetes", - "dns", - "udp", - "consul", - "etcd", - "mdns" - ], + "enum": ["kubernetes", "dns", "udp", "consul", "etcd", "mdns"], "description": "Discovery mechanism type" }, "service_name": { @@ -3165,9 +3036,8 @@ "description": "Port to bind for cluster communication" }, "dial_timeout": { - "type": "integer", - "minimum": 0, - "description": "Timeout for discovery dial operations in nanoseconds" + "type": "string", + "description": "Timeout for discovery dial operations as a Go duration string (e.g. '5s', '1m')" }, "allowed_address_space": { "type": "array", @@ -3213,18 +3083,14 @@ "description": "mDNS service name for local network discovery" } }, - "required": [ - "type" - ], + "required": ["type"], "additionalProperties": false } }, - "required": [ - "enabled" - ], + "required": ["enabled"], "additionalProperties": false }, - "saml_config": { + "scim_config": { "type": "object", "description": "SAML/SCIM (System for Cross-domain Identity Management) configuration", "properties": { @@ -3234,10 +3100,7 @@ }, "provider": { "type": "string", - "enum": [ - "okta", - "entra" - ], + "enum": ["okta", "entra"], "description": "SCIM provider type" }, "config": { @@ -3245,9 +3108,7 @@ "description": "Provider-specific configuration" } }, - "required": [ - "enabled" - ], + "required": ["enabled"], "additionalProperties": false, "allOf": [ { @@ -3299,7 +3160,11 @@ }, "clientSecret": { "type": "string", - "description": "Okta client secret (optional, required for token revocation)" + "description": "Okta client secret" + }, + "apiToken": { + "type": "string", + "description": "Okta API token for Admin API access" }, "audience": { "type": "string", @@ -3321,10 +3186,7 @@ "default": "roles" } }, - "required": [ - "issuerUrl", - "clientId" - ], + "required": ["issuerUrl", "clientId", "clientSecret", "apiToken"], "additionalProperties": false }, "entra_config": { @@ -3374,10 +3236,7 @@ "default": "roles" } }, - "required": [ - "tenantId", - "clientId" - ], + "required": ["tenantId", "clientId"], "additionalProperties": false }, "load_balancer_config": { @@ -3411,9 +3270,7 @@ } } }, - "required": [ - "enabled" - ], + "required": ["enabled"], "additionalProperties": false }, "guardrails_config": { @@ -3448,11 +3305,7 @@ }, "apply_to": { "type": "string", - "enum": [ - "input", - "output", - "both" - ], + "enum": ["input", "output", "both"], "description": "When to apply the guardrail (input, output, or both)" }, "sampling_rate": { @@ -3474,13 +3327,7 @@ "description": "IDs of provider configurations to use with this rule" } }, - "required": [ - "id", - "name", - "enabled", - "cel_expression", - "apply_to" - ], + "required": ["id", "name", "enabled", "cel_expression", "apply_to"], "additionalProperties": false } }, @@ -3516,12 +3363,7 @@ "description": "Provider-specific configuration" } }, - "required": [ - "id", - "provider_name", - "policy_name", - "enabled" - ], + "required": ["id", "provider_name", "policy_name", "enabled"], "additionalProperties": false } } @@ -3640,6 +3482,297 @@ } }, "additionalProperties": false + }, + "provider_pricing_override": { + "type": "object", + "description": "Scoped pricing override applied at runtime by the model catalog", + "properties": { + "id": { + "type": "string", + "description": "Unique pricing override ID" + }, + "name": { + "type": "string", + "description": "Human-readable name for this override" + }, + "scope_kind": { + "type": "string", + "description": "Scope level for this override", + "enum": [ + "global", + "provider", + "provider_key", + "virtual_key", + "virtual_key_provider", + "virtual_key_provider_key" + ] + }, + "virtual_key_id": { + "type": "string", + "description": "Virtual key ID (required for virtual_key* scopes)" + }, + "provider_id": { + "type": "string", + "description": "Provider ID (required for provider* scopes)" + }, + "provider_key_id": { + "type": "string", + "description": "Provider key ID (required for provider_key and virtual_key_provider_key scopes)" + }, + "match_type": { + "type": "string", + "description": "How the pattern is matched against model names", + "enum": ["exact", "wildcard"] + }, + "pattern": { + "type": "string", + "description": "Model name pattern to match (exact name or wildcard prefix ending with *)" + }, + "request_types": { + "type": "array", + "description": "Request types this override applies to. At least one value is required.", + "minItems": 1, + "items": { + "type": "string" + } + }, + "pricing_patch": { + "type": "string", + "description": "JSON-encoded pricing fields to override (e.g. '{\"input_cost_per_token\":0.000001}')" + }, + "config_hash": { + "type": "string", + "description": "Internal hash for change detection (auto-managed)" + } + }, + "required": ["id", "name", "scope_kind", "match_type", "pattern", "request_types"], + "additionalProperties": false + }, + "pricing_override_match_type": { + "type": "string", + "enum": ["exact", "wildcard"] + }, + "pricing_override_request_type": { + "type": "string", + "enum": [ + "chat_completion", + "text_completion", + "responses", + "embedding", + "rerank", + "speech", + "transcription", + "image_generation", + "image_variation", + "image_edit", + "video_generation", + "video_remix" + ] + }, + "custom_provider_config": { + "type": "object", + "description": "Custom provider configuration for extending or customizing provider behavior", + "properties": { + "is_key_less": { + "type": "boolean", + "description": "Whether the custom provider requires a key" + }, + "base_provider_type": { + "type": "string", + "enum": [ + "openai", + "azure", + "anthropic", + "bedrock", + "cohere", + "vertex", + "mistral", + "ollama", + "groq", + "sgl", + "parasail", + "perplexity", + "cerebras", + "gemini", + "openrouter", + "elevenlabs", + "huggingface", + "nebius", + "xai", + "replicate", + "vllm", + "runway", + "fireworks" + ], + "description": "Base provider type to extend" + }, + "request_path_overrides": { + "type": "object", + "description": "Mapping of request type to custom path overriding the default provider path", + "additionalProperties": { + "type": "string" + } + }, + "allowed_requests": { + "type": "object", + "description": "Allowed request types for the custom provider", + "properties": { + "list_models": { + "type": "boolean" + }, + "text_completion": { + "type": "boolean" + }, + "text_completion_stream": { + "type": "boolean" + }, + "chat_completion": { + "type": "boolean" + }, + "chat_completion_stream": { + "type": "boolean" + }, + "responses": { + "type": "boolean" + }, + "responses_stream": { + "type": "boolean" + }, + "count_tokens": { + "type": "boolean" + }, + "embedding": { + "type": "boolean" + }, + "rerank": { + "type": "boolean" + }, + "ocr": { + "type": "boolean" + }, + "speech": { + "type": "boolean" + }, + "speech_stream": { + "type": "boolean" + }, + "transcription": { + "type": "boolean" + }, + "transcription_stream": { + "type": "boolean" + }, + "image_generation": { + "type": "boolean" + }, + "image_generation_stream": { + "type": "boolean" + }, + "image_edit": { + "type": "boolean" + }, + "image_edit_stream": { + "type": "boolean" + }, + "image_variation": { + "type": "boolean" + }, + "video_generation": { + "type": "boolean" + }, + "video_retrieve": { + "type": "boolean" + }, + "video_download": { + "type": "boolean" + }, + "video_delete": { + "type": "boolean" + }, + "video_list": { + "type": "boolean" + }, + "video_remix": { + "type": "boolean" + }, + "batch_create": { + "type": "boolean" + }, + "batch_list": { + "type": "boolean" + }, + "batch_retrieve": { + "type": "boolean" + }, + "batch_cancel": { + "type": "boolean" + }, + "batch_delete": { + "type": "boolean" + }, + "batch_results": { + "type": "boolean" + }, + "file_upload": { + "type": "boolean" + }, + "file_list": { + "type": "boolean" + }, + "file_retrieve": { + "type": "boolean" + }, + "file_delete": { + "type": "boolean" + }, + "file_content": { + "type": "boolean" + }, + "container_create": { + "type": "boolean" + }, + "container_list": { + "type": "boolean" + }, + "container_retrieve": { + "type": "boolean" + }, + "container_delete": { + "type": "boolean" + }, + "container_file_create": { + "type": "boolean" + }, + "container_file_list": { + "type": "boolean" + }, + "container_file_retrieve": { + "type": "boolean" + }, + "container_file_content": { + "type": "boolean" + }, + "container_file_delete": { + "type": "boolean" + }, + "passthrough": { + "type": "boolean" + }, + "passthrough_stream": { + "type": "boolean" + }, + "websocket_responses": { + "type": "boolean" + }, + "realtime": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "required": ["base_provider_type"], + "additionalProperties": false } } -} \ No newline at end of file +}