diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ad0da74ff5..25fa245ac1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,33 +1,266 @@ version: 2 updates: - - package-ecosystem: "gomod" - directories: - - "/core" - - "/framework" - - "/transports" - - "/plugins/*" - - "/examples/**" + - package-ecosystem: "docker" + directory: "/transports" schedule: interval: "weekly" open-pull-requests-limit: 0 - - package-ecosystem: "npm" - directories: - - "/ui" - - "/npx" - - "/examples/**" + - package-ecosystem: "github-actions" + directory: "/" schedule: interval: "weekly" open-pull-requests-limit: 0 - - package-ecosystem: "docker" - directory: "/transports" + # Go / npm / Rust (daily). Docker + GitHub Actions: weekly entries above. + - package-ecosystem: gomod + directory: /cli schedule: - interval: "weekly" + interval: daily open-pull-requests-limit: 0 - - package-ecosystem: "github-actions" - directory: "/" + - package-ecosystem: gomod + directory: /core schedule: - interval: "weekly" + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /examples/mcps/auth-demo-server + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /examples/mcps/edge-case-server + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: npm + directory: /examples/mcps/edge-case-server + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /examples/mcps/error-test-server + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: npm + directory: /examples/mcps/error-test-server + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /examples/mcps/go-test-server + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /examples/mcps/http-no-ping-server + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /examples/mcps/parallel-test-server + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: npm + directory: /examples/mcps/parallel-test-server + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: npm + directory: /examples/mcps/temperature + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: npm + directory: /examples/mcps/test-tools-server + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /examples/plugins/hello-world-wasm-go + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: cargo + directory: /examples/plugins/hello-world-wasm-rust + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: npm + directory: /examples/plugins/hello-world-wasm-typescript + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /examples/plugins/hello-world + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /examples/plugins/http-transport-only + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /examples/plugins/llm-only + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /examples/plugins/mcp-only + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /examples/plugins/multi-interface + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /framework + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: npm + directory: /npx/bifrost-cli + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: npm + directory: /npx/bifrost + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /plugins/governance + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /plugins/jsonparser + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /plugins/litellmcompat + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /plugins/logging + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /plugins/maxim + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /plugins/mocker + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /plugins/otel + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /plugins/semanticcache + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /plugins/telemetry + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: npm + directory: /tests/e2e/api/newman-reporter-dbverify + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: npm + directory: /tests/e2e/api + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: npm + directory: /tests/e2e + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /tests/governance + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: npm + directory: /tests/integrations/typescript + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /tests/scripts/1millogs + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /tests/scripts/migration-checker + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: gomod + directory: /transports + schedule: + interval: daily + open-pull-requests-limit: 0 + + - package-ecosystem: npm + directory: /ui + schedule: + interval: daily open-pull-requests-limit: 0 diff --git a/.github/workflows/dependabot-alerts.yml b/.github/workflows/dependabot-alerts.yml index 26effeb18f..f92d41280c 100644 --- a/.github/workflows/dependabot-alerts.yml +++ b/.github/workflows/dependabot-alerts.yml @@ -12,6 +12,11 @@ jobs: create-issues: runs-on: ubuntu-latest steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Create issues from Dependabot alerts env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000000..60d8715ebc --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,27 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, +# surfacing known-vulnerable versions of the packages declared or updated in the PR. +# Once installed, if the workflow run is marked as required, +# PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + + - name: 'Checkout Repository' + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: 'Dependency Review' + uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0 diff --git a/.github/workflows/docs-validation.yml b/.github/workflows/docs-validation.yml index a4a2ca3e63..772fd50f0a 100644 --- a/.github/workflows/docs-validation.yml +++ b/.github/workflows/docs-validation.yml @@ -17,6 +17,11 @@ jobs: name: Check Broken Links runs-on: ubuntu-latest steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index f171c326a6..e48994c743 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -8,6 +8,9 @@ concurrency: group: e2e-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: test-e2e-ui: name: E2E UI (Playwright) @@ -16,6 +19,11 @@ jobs: permissions: contents: read steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/helm-release.yml b/.github/workflows/helm-release.yml index 77682648a9..69128ae643 100644 --- a/.github/workflows/helm-release.yml +++ b/.github/workflows/helm-release.yml @@ -16,6 +16,11 @@ jobs: release: runs-on: ubuntu-latest steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/npx-publish.yml b/.github/workflows/npx-publish.yml index 404c3fd0d8..e930e79298 100644 --- a/.github/workflows/npx-publish.yml +++ b/.github/workflows/npx-publish.yml @@ -14,6 +14,9 @@ concurrency: group: npx-publish-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: # Check if pipeline should be skipped based on first line of commit message check-skip: @@ -23,6 +26,11 @@ jobs: outputs: should-skip: ${{ steps.check.outputs.should-skip }} steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -45,6 +53,11 @@ jobs: contents: write id-token: write # Required for npm provenance steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -163,6 +176,11 @@ jobs: contents: write id-token: write # Required for npm provenance steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/openapi-bundle.yml b/.github/workflows/openapi-bundle.yml index 3cafc49569..7cd8c232e8 100644 --- a/.github/workflows/openapi-bundle.yml +++ b/.github/workflows/openapi-bundle.yml @@ -20,6 +20,11 @@ jobs: name: Bundle OpenAPI Spec runs-on: ubuntu-latest steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/pr-test-notifier.yml b/.github/workflows/pr-test-notifier.yml index 571c085703..2453a3ad00 100644 --- a/.github/workflows/pr-test-notifier.yml +++ b/.github/workflows/pr-test-notifier.yml @@ -16,6 +16,11 @@ jobs: outputs: should-skip: ${{ steps.check.outputs.should-skip }} steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -36,6 +41,11 @@ jobs: name: Post Test Instructions runs-on: ubuntu-latest steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Post comment with test trigger instructions env: GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/pr-tests.yml b/.github/workflows/pr-tests.yml index 7f79885c6b..efe18f3013 100644 --- a/.github/workflows/pr-tests.yml +++ b/.github/workflows/pr-tests.yml @@ -14,6 +14,9 @@ concurrency: group: pr-tests-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: # Check if pipeline should be skipped based on first line of commit message check-skip: @@ -23,6 +26,11 @@ jobs: outputs: should-skip: ${{ steps.check.outputs.should-skip }} steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -55,6 +63,11 @@ jobs: pull-requests: write steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 13b16efc38..360d6ca754 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -10,6 +10,9 @@ concurrency: group: release-cli cancel-in-progress: false +permissions: + contents: read + jobs: check-version: runs-on: ubuntu-latest @@ -17,6 +20,11 @@ jobs: version: ${{ steps.get-version.outputs.version }} tag_exists: ${{ steps.check-tag.outputs.exists }} steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -43,6 +51,11 @@ jobs: permissions: contents: read steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -67,6 +80,11 @@ jobs: outputs: success: ${{ steps.release.outputs.success }} steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -101,6 +119,11 @@ jobs: permissions: contents: write steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/release-pipeline.yml b/.github/workflows/release-pipeline.yml index e02fddfd9e..1ea87774e2 100644 --- a/.github/workflows/release-pipeline.yml +++ b/.github/workflows/release-pipeline.yml @@ -10,6 +10,9 @@ concurrency: group: release-pipeline cancel-in-progress: false +permissions: + contents: read + jobs: # Check if pipeline should be skipped based on first line of commit message check-skip: @@ -17,6 +20,11 @@ jobs: outputs: should-skip: ${{ steps.check.outputs.should-skip }} steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Check if pipeline should be skipped id: check env: @@ -28,7 +36,7 @@ jobs: else echo "should-skip=false" >> $GITHUB_OUTPUT fi - + # Detect what needs to be released detect-changes: needs: [check-skip] @@ -46,6 +54,11 @@ jobs: framework-version: ${{ steps.detect.outputs.framework-version }} transport-version: ${{ steps.detect.outputs.transport-version }} steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -69,6 +82,11 @@ jobs: permissions: contents: read steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -138,6 +156,11 @@ jobs: outputs: approved: ${{ steps.approve.outputs.approved }} steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Display failed test info run: | echo "::warning::test-core failed. Review the logs to determine if this is a flaky test." @@ -154,6 +177,11 @@ jobs: permissions: contents: read steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -219,6 +247,11 @@ jobs: permissions: contents: read steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -289,6 +322,11 @@ jobs: permissions: contents: read steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -334,6 +372,11 @@ jobs: permissions: contents: read steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -376,6 +419,11 @@ jobs: permissions: contents: read steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -428,6 +476,11 @@ jobs: permissions: contents: read steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -487,6 +540,11 @@ jobs: permissions: contents: read steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -556,6 +614,11 @@ jobs: permissions: contents: read steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -639,6 +702,11 @@ jobs: success: ${{ steps.release.outputs.success }} version: ${{ needs.detect-changes.outputs.core-version }} steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -724,6 +792,11 @@ jobs: success: ${{ steps.release.outputs.success }} version: ${{ needs.detect-changes.outputs.framework-version }} steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -817,6 +890,11 @@ jobs: outputs: success: ${{ steps.release.outputs.success }} steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -922,6 +1000,11 @@ jobs: outputs: success: ${{ steps.prep.outputs.success }} steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -958,6 +1041,11 @@ jobs: permissions: contents: read steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -1009,6 +1097,11 @@ jobs: permissions: contents: read steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -1060,6 +1153,11 @@ jobs: success: ${{ steps.release.outputs.success }} version: ${{ needs.detect-changes.outputs.transport-version }} steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -1113,6 +1211,11 @@ jobs: ACCOUNT: maximhq IMAGE_NAME: bifrost steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -1184,6 +1287,11 @@ jobs: ACCOUNT: maximhq IMAGE_NAME: bifrost steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -1237,6 +1345,11 @@ jobs: ACCOUNT: maximhq IMAGE_NAME: bifrost steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -1274,6 +1387,11 @@ jobs: permissions: contents: write steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -1308,6 +1426,11 @@ jobs: if: "always() && needs.check-skip.outputs.should-skip != 'true'" runs-on: ubuntu-latest steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Install jq run: | sudo apt-get update diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml new file mode 100644 index 0000000000..33206cdb3e --- /dev/null +++ b/.github/workflows/scorecards.yml @@ -0,0 +1,81 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '20 7 * * 2' + push: + branches: ["main"] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + contents: read + actions: read + # To allow GraphQL ListCommits to work + issues: read + pull-requests: read + # To detect SAST tools + checks: read + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + + - name: "Checkout code" + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecards on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@5c8a8a642e79153f5d047b10ec1cba1d1cc65699 # v3.35.1 + with: + sarif_file: results.sarif diff --git a/.github/workflows/scripts/run-migration-tests.sh b/.github/workflows/scripts/run-migration-tests.sh index 402ad4d961..06fd700e66 100755 --- a/.github/workflows/scripts/run-migration-tests.sh +++ b/.github/workflows/scripts/run-migration-tests.sh @@ -481,10 +481,10 @@ VALUES ON CONFLICT DO NOTHING; -- config_providers (with all JSON config fields and governance fields including budget_id, rate_limit_id) -INSERT INTO config_providers (name, send_back_raw_request, send_back_raw_response, network_config_json, concurrency_buffer_json, proxy_config_json, custom_provider_config_json, budget_id, rate_limit_id, config_hash, created_at, updated_at) -VALUES - ('openai', false, false, '{"timeout": 30}', '{"buffer_size": 100}', NULL, NULL, 'budget-migration-test-1', 'ratelimit-migration-test-1', 'provider-hash-openai', $now, $now), - ('anthropic', true, true, '{"timeout": 60}', '{"buffer_size": 200}', '{"url": "http://proxy.test"}', NULL, NULL, NULL, 'provider-hash-anthropic', $now, $now) +INSERT INTO config_providers (name, send_back_raw_request, send_back_raw_response, network_config_json, concurrency_buffer_json, proxy_config_json, custom_provider_config_json, open_ai_config_json, budget_id, rate_limit_id, config_hash, created_at, updated_at) +VALUES + ('openai', false, false, '{"timeout": 30}', '{"buffer_size": 100}', NULL, NULL, '{"organization": "org-test"}', 'budget-migration-test-1', 'ratelimit-migration-test-1', 'provider-hash-openai', $now, $now), + ('anthropic', true, true, '{"timeout": 60}', '{"buffer_size": 200}', '{"url": "http://proxy.test"}', NULL, NULL, NULL, NULL, 'provider-hash-anthropic', $now, $now) ON CONFLICT DO NOTHING; -- framework_configs @@ -563,13 +563,13 @@ ON CONFLICT DO NOTHING; -- config_keys (references config_providers) - with ALL columns including Azure/Vertex/Bedrock/Replicate fields -- NOTE: azure_scopes column is added dynamically via append_dynamic_inserts() for schema compatibility -INSERT INTO config_keys (name, provider_id, provider, key_id, value, models_json, weight, enabled, config_hash, azure_endpoint, azure_api_version, azure_deployments_json, azure_client_id, azure_client_secret, azure_tenant_id, vertex_project_id, vertex_project_number, vertex_region, vertex_auth_credentials, vertex_deployments_json, bedrock_access_key, bedrock_secret_key, bedrock_session_token, bedrock_region, bedrock_arn, bedrock_deployments_json, bedrock_batch_s3_config_json, use_for_batch_api, replicate_deployments_json, created_at, updated_at) -SELECT 'migration-test-key-openai', id, 'openai', 'key-migration-uuid-001', 'sk-migration-test-fake-key-value-openai', '["gpt-4", "gpt-3.5-turbo"]', 1.0, true, 'key-hash-001', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, false, NULL, $now, $now +INSERT INTO config_keys (name, provider_id, provider, key_id, value, models_json, blacklisted_models_json, weight, enabled, config_hash, azure_endpoint, azure_api_version, azure_deployments_json, azure_client_id, azure_client_secret, azure_tenant_id, vertex_project_id, vertex_project_number, vertex_region, vertex_auth_credentials, vertex_deployments_json, bedrock_access_key, bedrock_secret_key, bedrock_session_token, bedrock_region, bedrock_arn, bedrock_deployments_json, bedrock_batch_s3_config_json, use_for_batch_api, replicate_deployments_json, created_at, updated_at) +SELECT 'migration-test-key-openai', id, 'openai', 'key-migration-uuid-001', 'sk-migration-test-fake-key-value-openai', '["gpt-4", "gpt-3.5-turbo"]', '["gpt-4-32k"]', 1.0, true, 'key-hash-001', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, false, NULL, $now, $now FROM config_providers WHERE name = 'openai' ON CONFLICT DO NOTHING; -INSERT INTO config_keys (name, provider_id, provider, key_id, value, models_json, weight, enabled, config_hash, azure_endpoint, azure_api_version, azure_deployments_json, azure_client_id, azure_client_secret, azure_tenant_id, vertex_project_id, vertex_project_number, vertex_region, vertex_auth_credentials, vertex_deployments_json, bedrock_access_key, bedrock_secret_key, bedrock_session_token, bedrock_region, bedrock_arn, bedrock_deployments_json, bedrock_batch_s3_config_json, use_for_batch_api, replicate_deployments_json, created_at, updated_at) -SELECT 'migration-test-key-anthropic', id, 'anthropic', 'key-migration-uuid-002', 'sk-ant-migration-test-fake-key', '["claude-3-opus"]', 0.8, true, 'key-hash-002', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, false, NULL, $now, $now +INSERT INTO config_keys (name, provider_id, provider, key_id, value, models_json, blacklisted_models_json, weight, enabled, config_hash, azure_endpoint, azure_api_version, azure_deployments_json, azure_client_id, azure_client_secret, azure_tenant_id, vertex_project_id, vertex_project_number, vertex_region, vertex_auth_credentials, vertex_deployments_json, bedrock_access_key, bedrock_secret_key, bedrock_session_token, bedrock_region, bedrock_arn, bedrock_deployments_json, bedrock_batch_s3_config_json, use_for_batch_api, replicate_deployments_json, created_at, updated_at) +SELECT 'migration-test-key-anthropic', id, 'anthropic', 'key-migration-uuid-002', 'sk-ant-migration-test-fake-key', '["claude-3-opus"]', '[]', 0.8, true, 'key-hash-002', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, false, NULL, $now, $now FROM config_providers WHERE name = 'anthropic' ON CONFLICT DO NOTHING; @@ -1189,6 +1189,22 @@ append_dynamic_columns_postgres() { echo "UPDATE governance_model_pricing SET code_interpreter_cost_per_session = NULL WHERE id = 1;" >> "$output_file" echo "UPDATE governance_model_pricing SET code_interpreter_cost_per_session = NULL WHERE id = 2;" >> "$output_file" fi + + # ------------------------------------------------------------------------- + # v1.4.17 columns + # ------------------------------------------------------------------------- + + # config_keys.blacklisted_models_json (added in v1.4.17 - per-key model deny list) + if column_exists_postgres "config_keys" "blacklisted_models_json"; then + echo "UPDATE config_keys SET blacklisted_models_json = '[]' WHERE name = 'migration-test-key-openai';" >> "$output_file" + echo "UPDATE config_keys SET blacklisted_models_json = '[\"gpt-4-vision\"]' WHERE name = 'migration-test-key-anthropic';" >> "$output_file" + fi + + # config_providers.open_ai_config_json (added in v1.4.17 - OpenAI-specific provider config) + if column_exists_postgres "config_providers" "open_ai_config_json"; then + echo "UPDATE config_providers SET open_ai_config_json = '{\"disable_store\":false}' WHERE name = 'openai';" >> "$output_file" + echo "UPDATE config_providers SET open_ai_config_json = '' WHERE name = 'anthropic';" >> "$output_file" + fi } # Append dynamic column UPDATEs for columns that may not exist in older schemas (SQLite) @@ -1638,6 +1654,24 @@ append_dynamic_columns_sqlite() { echo "UPDATE logs SET cached_read_tokens = 128 WHERE id = 'log-migration-test-001';" >> "$output_file" echo "UPDATE logs SET cached_read_tokens = 0 WHERE id = 'log-migration-test-002';" >> "$output_file" echo "UPDATE logs SET cached_read_tokens = 0 WHERE id = 'log-migration-test-003';" >> "$output_file" + + # ------------------------------------------------------------------------- + # v1.4.17 columns + # ------------------------------------------------------------------------- + + if [ -f "$config_db" ]; then + # config_keys.blacklisted_models_json (added in v1.4.17 - per-key model deny list) + if column_exists_sqlite "$config_db" "config_keys" "blacklisted_models_json"; then + echo "UPDATE config_keys SET blacklisted_models_json = '[]' WHERE name = 'migration-test-key-openai';" >> "$output_file" + echo "UPDATE config_keys SET blacklisted_models_json = '[\"gpt-4-vision\"]' WHERE name = 'migration-test-key-anthropic';" >> "$output_file" + fi + + # config_providers.open_ai_config_json (added in v1.4.17 - OpenAI-specific provider config) + if column_exists_sqlite "$config_db" "config_providers" "open_ai_config_json"; then + echo "UPDATE config_providers SET open_ai_config_json = '{\"disable_store\":false}' WHERE name = 'openai';" >> "$output_file" + echo "UPDATE config_providers SET open_ai_config_json = '' WHERE name = 'anthropic';" >> "$output_file" + fi + fi } # ============================================================================ diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index 0d42b3d5fd..3afbede301 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -18,6 +18,11 @@ jobs: outputs: should-skip: ${{ steps.check.outputs.should-skip }} steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -38,6 +43,11 @@ jobs: name: Snyk Open Source (deps) runs-on: ubuntu-latest steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -46,16 +56,24 @@ jobs: with: node-version: "25" - - name: Setup Python (for tests tooling) - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + - name: Install uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 with: + version: "0.11.0" python-version: "3.11" + - name: Sync Python dependencies (integrations) + working-directory: tests/integrations/python + run: uv sync --frozen + - name: Setup Go uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version: "1.26.1" + - name: Build + run: make build + - name: Install Snyk CLI uses: maximhq/snyk-actions/setup@9adf32b1121593767fc3c057af55b55db032dc04 # v1.0.0 with: @@ -64,10 +82,10 @@ jobs: - name: Snyk test (all projects) env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - run: snyk test --all-projects --detection-depth=4 --sarif-file-output=snyk.sarif || true + run: snyk test --all-projects --detection-depth=4 --exclude=examples,tests/scripts --sarif-file-output=snyk.sarif || true - name: Upload SARIF - if: always() + if: always() && hashFiles('snyk.sarif') != '' uses: github/codeql-action/upload-sarif@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 with: sarif_file: snyk.sarif @@ -78,6 +96,11 @@ jobs: name: Snyk Code (SAST) runs-on: ubuntu-latest steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 + with: + egress-policy: audit + - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -86,25 +109,15 @@ jobs: with: node-version: "25" - - name: Setup Python (for tests tooling) - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + - name: Install uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 with: + version: "0.11.0" python-version: "3.11" - - name: Setup Python (for tests tooling) - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 - with: - python-version: "3.11" - cache: "pip" - cache-dependency-path: | - tests/integrations/requirements.txt - tests/governance/requirements.txt - - - name: Install Python dependencies (tests tooling) - run: | - python -m pip install --disable-pip-version-check \ - -r tests/integrations/requirements.txt \ - -r tests/governance/requirements.txt + - name: Sync Python dependencies (integrations) + working-directory: tests/integrations/python + run: uv sync --frozen - name: Setup Go uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 @@ -125,7 +138,7 @@ jobs: run: snyk code test --sarif-file-output=snyk-code.sarif || true - name: Upload SARIF - if: always() + if: always() && hashFiles('snyk-code.sarif') != '' uses: github/codeql-action/upload-sarif@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1 with: sarif_file: snyk-code.sarif diff --git a/.next/trace b/.next/trace deleted file mode 100644 index 0c463e8fc7..0000000000 --- a/.next/trace +++ /dev/null @@ -1 +0,0 @@ -[{"name":"generate-buildid","duration":97,"timestamp":1580954340784,"id":4,"parentId":1,"tags":{},"startTime":1773945838012,"traceId":"9700219a91545e75"},{"name":"load-custom-routes","duration":127,"timestamp":1580954340919,"id":5,"parentId":1,"tags":{},"startTime":1773945838012,"traceId":"9700219a91545e75"},{"name":"next-build","duration":51155,"timestamp":1580954291111,"id":1,"tags":{"buildMode":"default","isTurboBuild":"false","version":"15.3.5"},"startTime":1773945837962,"traceId":"9700219a91545e75"}] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..44fd27e83a --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,31 @@ +repos: +- repo: https://github.com/gitleaks/gitleaks + rev: v8.16.3 + hooks: + - id: gitleaks +- repo: https://github.com/golangci/golangci-lint + rev: v2.9.0 + hooks: + - id: golangci-lint +- repo: https://github.com/jumanjihouse/pre-commit-hooks + rev: 3.0.0 + hooks: + - id: shellcheck +- repo: local + hooks: + - id: eslint + name: eslint (ui) + language: system + entry: ui/node_modules/.bin/eslint + args: [-c, ui/eslint.config.mjs, --max-warnings=0, --fix] + files: ^ui/.*\.(mjs|cjs|jsx|tsx?|js)$ + exclude: ^ui/node_modules/ +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: end-of-file-fixer + - id: trailing-whitespace +- repo: https://github.com/pylint-dev/pylint + rev: v2.17.2 + hooks: + - id: pylint diff --git a/Makefile b/Makefile index 493bbcf5a5..bf88ace2ba 100644 --- a/Makefile +++ b/Makefile @@ -265,15 +265,25 @@ _build-with-docker: # Internal target for Docker-based cross-compilation exit 1; \ fi -docker-image: build-ui ## Build Docker image +docker-image: build-ui ## Build Docker image (LOCAL=1 to use Dockerfile.local) @echo "$(GREEN)Building Docker image...$(NC)" $(eval GIT_SHA=$(shell git rev-parse --short HEAD)) - @docker build -f transports/Dockerfile -t bifrost -t bifrost:$(GIT_SHA) -t bifrost:latest . - @echo "$(GREEN)Docker image built: bifrost, bifrost:$(GIT_SHA), bifrost:latest$(NC)" + $(eval DOCKERFILE=$(if $(LOCAL),transports/Dockerfile.local,transports/Dockerfile)) + @docker build -f $(DOCKERFILE) -t bifrost -t bifrost:$(GIT_SHA) -t bifrost:latest . + @echo "$(GREEN)Docker image built: bifrost, bifrost:$(GIT_SHA), bifrost:latest (using $(DOCKERFILE))$(NC)" -docker-run: ## Run Docker container +docker-run: ## Run Docker container (Usage: make docker-run [CONFIG=path/to/config.json or path/to/dir/]) @echo "$(GREEN)Running Docker container...$(NC)" - @docker run -e APP_PORT=$(PORT) -e APP_HOST=0.0.0.0 -p $(PORT):$(PORT) -e LOG_LEVEL=$(LOG_LEVEL) -e LOG_STYLE=$(LOG_STYLE) -v $(shell pwd):/app/data bifrost + @CONFIG_PATH="$(abspath $(CONFIG))"; \ + if [ -n "$(CONFIG)" ]; then \ + if [ -d "$$CONFIG_PATH" ]; then \ + CONFIG_PATH="$$CONFIG_PATH/config.json"; \ + fi; \ + CONFIG_MOUNT="-v $$CONFIG_PATH:/app/data/config.json"; \ + else \ + CONFIG_MOUNT=""; \ + fi; \ + docker run -e APP_PORT=$(PORT) -e APP_HOST=0.0.0.0 -p $(PORT):$(PORT) -e LOG_LEVEL=$(LOG_LEVEL) -e LOG_STYLE=$(LOG_STYLE) -v $(shell pwd):/app/data $$CONFIG_MOUNT bifrost docs: ## Prepare local docs @echo "$(GREEN)Preparing local docs...$(NC)" diff --git a/config.json b/config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/core/internal/llmtests/account.go b/core/internal/llmtests/account.go index 1a37958ea3..da6842f861 100644 --- a/core/internal/llmtests/account.go +++ b/core/internal/llmtests/account.go @@ -27,8 +27,9 @@ type TestScenarios struct { MultiTurnConversation bool ToolCalls bool ToolCallsStreaming bool // Streaming tool calls functionality - MultipleToolCalls bool - End2EndToolCalling bool + MultipleToolCalls bool + MultipleToolCallsStreaming bool // Streaming multiple tool calls (some providers only return 1 tool call in streaming) + End2EndToolCalling bool AutomaticFunctionCall bool ImageURL bool ImageBase64 bool @@ -808,7 +809,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ CompletionStream: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, @@ -863,7 +865,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ CompletionStream: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, @@ -905,7 +908,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ CompletionStream: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, @@ -950,7 +954,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ CompletionStream: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: false, // May not support automatic ImageURL: false, // Check if supported @@ -989,7 +994,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ CompletionStream: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, @@ -1037,7 +1043,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ CompletionStream: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, @@ -1071,7 +1078,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ SimpleChat: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, @@ -1105,7 +1113,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ CompletionStream: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, @@ -1139,7 +1148,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ CompletionStream: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, @@ -1173,7 +1183,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ CompletionStream: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: false, @@ -1212,7 +1223,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ CompletionStream: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, @@ -1256,7 +1268,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ CompletionStream: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, @@ -1333,7 +1346,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ CompletionStream: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, @@ -1365,7 +1379,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ CompletionStream: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, @@ -1406,7 +1421,8 @@ var AllProviderConfigs = []ComprehensiveTestConfig{ CompletionStream: true, MultiTurnConversation: true, ToolCalls: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, }, Fallbacks: []schemas.Fallback{ diff --git a/core/internal/llmtests/multiple_tool_calls.go b/core/internal/llmtests/multiple_tool_calls.go index fc4c7e5ef8..57dc22abf1 100644 --- a/core/internal/llmtests/multiple_tool_calls.go +++ b/core/internal/llmtests/multiple_tool_calls.go @@ -196,6 +196,9 @@ func RunMultipleToolCallsTest(t *testing.T, client *bifrost.Bifrost, ctx context // Streaming Chat Completions with multiple tool calls (validates sequential indices 0, 1, 2, ...) t.Run("MultipleToolCallsStreamingChatCompletions", func(t *testing.T) { + if !testConfig.Scenarios.MultipleToolCallsStreaming { + t.Skip("Multiple tool calls streaming not supported for this provider") + } if os.Getenv("SKIP_PARALLEL_TESTS") != "true" { t.Parallel() } @@ -343,6 +346,9 @@ func RunMultipleToolCallsTest(t *testing.T, client *bifrost.Bifrost, ctx context // Streaming Responses API with multiple tool calls t.Run("MultipleToolCallsStreamingResponses", func(t *testing.T) { + if !testConfig.Scenarios.MultipleToolCallsStreaming { + t.Skip("Multiple tool calls streaming not supported for this provider") + } if os.Getenv("SKIP_PARALLEL_TESTS") != "true" { t.Parallel() } diff --git a/core/internal/llmtests/validation_presets.go b/core/internal/llmtests/validation_presets.go index 56cf793ca6..76ed076315 100644 --- a/core/internal/llmtests/validation_presets.go +++ b/core/internal/llmtests/validation_presets.go @@ -131,6 +131,12 @@ func CountTokensExpectations() ResponseExpectations { func StreamingExpectations() ResponseExpectations { expectations := BasicChatExpectations() + // Streaming consolidated responses are assembled from chunks. + // The last chunk often does not carry created/model fields, + // so we cannot reliably validate them on the consolidated response. + expectations.ShouldHaveTimestamps = false + expectations.ShouldHaveModel = false + return expectations } @@ -318,7 +324,7 @@ func GetExpectationsForScenario(scenarioName string, testConfig ComprehensiveTes case "FileInput": expectations = FileInputExpectations() - case "ChatCompletionStream": + case "ChatCompletionStream", "TextCompletionStream": expectations = StreamingExpectations() case "MultiTurnConversation": @@ -439,7 +445,7 @@ func ModifyExpectationsForProvider(expectations ResponseExpectations, provider s case schemas.Cohere: expectations.ShouldHaveUsageStats = true expectations.ShouldHaveTimestamps = true - expectations.ShouldHaveModel = true + expectations.ShouldHaveModel = false // Cohere does not return model field in all response types expectations.ShouldHaveLatency = true case schemas.Vertex: @@ -476,8 +482,8 @@ func ModifyExpectationsForProvider(expectations ResponseExpectations, provider s case schemas.Perplexity: expectations.ShouldHaveUsageStats = true - expectations.ShouldHaveTimestamps = true - expectations.ShouldHaveModel = true + expectations.ShouldHaveTimestamps = false // Perplexity does not return created timestamps + expectations.ShouldHaveModel = false // Perplexity does not return model field expectations.ShouldHaveLatency = true case schemas.Cerebras: @@ -514,8 +520,8 @@ func ModifyExpectationsForProvider(expectations ResponseExpectations, provider s case schemas.Parasail: expectations.ShouldHaveUsageStats = true - expectations.ShouldHaveTimestamps = true - expectations.ShouldHaveModel = true + expectations.ShouldHaveTimestamps = false // Parasail does not return created timestamps + expectations.ShouldHaveModel = false // Parasail does not return model field in streaming expectations.ShouldHaveLatency = true case schemas.Elevenlabs: diff --git a/core/providers/anthropic/anthropic_test.go b/core/providers/anthropic/anthropic_test.go index 9b15f18f39..d64b10aa82 100644 --- a/core/providers/anthropic/anthropic_test.go +++ b/core/providers/anthropic/anthropic_test.go @@ -41,7 +41,8 @@ func TestAnthropic(t *testing.T) { MultiTurnConversation: true, ToolCalls: true, ToolCallsStreaming: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, WebSearchTool: true, diff --git a/core/providers/azure/azure_test.go b/core/providers/azure/azure_test.go index d646449adf..41b32077e7 100644 --- a/core/providers/azure/azure_test.go +++ b/core/providers/azure/azure_test.go @@ -48,7 +48,8 @@ func TestAzure(t *testing.T) { MultiTurnConversation: true, ToolCalls: true, ToolCallsStreaming: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, diff --git a/core/providers/bedrock/bedrock_test.go b/core/providers/bedrock/bedrock_test.go index a73cc0f2e2..1949051c44 100644 --- a/core/providers/bedrock/bedrock_test.go +++ b/core/providers/bedrock/bedrock_test.go @@ -191,7 +191,8 @@ func TestBedrock(t *testing.T) { MultiTurnConversation: true, ToolCalls: true, ToolCallsStreaming: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: false, // Bedrock doesn't support image URL diff --git a/core/providers/cohere/cohere_test.go b/core/providers/cohere/cohere_test.go index 0022a87fcc..f2fca1028f 100644 --- a/core/providers/cohere/cohere_test.go +++ b/core/providers/cohere/cohere_test.go @@ -38,7 +38,8 @@ func TestCohere(t *testing.T) { MultiTurnConversation: true, ToolCalls: true, ToolCallsStreaming: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, // May not support automatic ImageURL: false, // Supported by c4ai-aya-vision-8b model diff --git a/core/providers/gemini/gemini_test.go b/core/providers/gemini/gemini_test.go index faed0c1bbd..ba8a7b9339 100644 --- a/core/providers/gemini/gemini_test.go +++ b/core/providers/gemini/gemini_test.go @@ -53,7 +53,8 @@ func TestGemini(t *testing.T) { MultiTurnConversation: true, ToolCalls: true, ToolCallsStreaming: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, WebSearchTool: true, diff --git a/core/providers/groq/groq_test.go b/core/providers/groq/groq_test.go index 49f2ce2367..54b7945d76 100644 --- a/core/providers/groq/groq_test.go +++ b/core/providers/groq/groq_test.go @@ -45,7 +45,8 @@ func TestGroq(t *testing.T) { MultiTurnConversation: true, ToolCalls: true, ToolCallsStreaming: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: false, diff --git a/core/providers/nebius/nebius_test.go b/core/providers/nebius/nebius_test.go index 311069cbc3..898617cb1e 100644 --- a/core/providers/nebius/nebius_test.go +++ b/core/providers/nebius/nebius_test.go @@ -39,7 +39,8 @@ func TestNebius(t *testing.T) { MultiTurnConversation: true, ToolCalls: true, ToolCallsStreaming: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, diff --git a/core/providers/ollama/ollama_test.go b/core/providers/ollama/ollama_test.go index 0dba4df0bc..ad31297046 100644 --- a/core/providers/ollama/ollama_test.go +++ b/core/providers/ollama/ollama_test.go @@ -35,7 +35,8 @@ func TestOllama(t *testing.T) { MultiTurnConversation: true, ToolCalls: true, ToolCallsStreaming: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: false, diff --git a/core/providers/openai/openai_test.go b/core/providers/openai/openai_test.go index f8e691d0c5..c37040ce62 100644 --- a/core/providers/openai/openai_test.go +++ b/core/providers/openai/openai_test.go @@ -53,7 +53,8 @@ func TestOpenAI(t *testing.T) { MultiTurnConversation: true, ToolCalls: true, ToolCallsStreaming: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, WebSearchTool: true, diff --git a/core/providers/openrouter/openrouter_test.go b/core/providers/openrouter/openrouter_test.go index 517590fe8c..2847aeaf7c 100644 --- a/core/providers/openrouter/openrouter_test.go +++ b/core/providers/openrouter/openrouter_test.go @@ -37,7 +37,8 @@ func TestOpenRouter(t *testing.T) { MultiTurnConversation: true, ToolCalls: true, ToolCallsStreaming: false, // OpenRouter's responses API is in Beta - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: false, // OpenRouter's responses API is in Beta diff --git a/core/providers/sgl/sgl_test.go b/core/providers/sgl/sgl_test.go index 8b930ae039..11447f58b4 100644 --- a/core/providers/sgl/sgl_test.go +++ b/core/providers/sgl/sgl_test.go @@ -35,7 +35,8 @@ func TestSGL(t *testing.T) { MultiTurnConversation: true, ToolCalls: true, ToolCallsStreaming: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, diff --git a/core/providers/vertex/vertex_test.go b/core/providers/vertex/vertex_test.go index 1962556d9d..7203bf3080 100644 --- a/core/providers/vertex/vertex_test.go +++ b/core/providers/vertex/vertex_test.go @@ -44,7 +44,8 @@ func TestVertex(t *testing.T) { MultiTurnConversation: true, ToolCalls: true, ToolCallsStreaming: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: false, diff --git a/core/providers/vllm/vllm_test.go b/core/providers/vllm/vllm_test.go index be30c33a71..a9d7a1c17d 100644 --- a/core/providers/vllm/vllm_test.go +++ b/core/providers/vllm/vllm_test.go @@ -44,7 +44,8 @@ func TestVLLM(t *testing.T) { MultiTurnConversation: true, ToolCalls: true, ToolCallsStreaming: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: false, diff --git a/core/providers/xai/xai_test.go b/core/providers/xai/xai_test.go index 5c46e5b0f0..81c479bb7f 100644 --- a/core/providers/xai/xai_test.go +++ b/core/providers/xai/xai_test.go @@ -38,7 +38,8 @@ func TestXAI(t *testing.T) { MultiTurnConversation: true, ToolCalls: true, ToolCallsStreaming: true, - MultipleToolCalls: true, + MultipleToolCalls: true, + MultipleToolCallsStreaming: true, End2EndToolCalling: true, AutomaticFunctionCall: true, ImageURL: true, diff --git a/core/version b/core/version index 323afbcd2a..976182be5d 100644 --- a/core/version +++ b/core/version @@ -1 +1 @@ -1.4.14 +1.4.15 \ No newline at end of file diff --git a/docs/changelogs/v1.4.18.mdx b/docs/changelogs/v1.4.18.mdx new file mode 100644 index 0000000000..4cbacd8b56 --- /dev/null +++ b/docs/changelogs/v1.4.18.mdx @@ -0,0 +1,95 @@ +--- +title: "v1.4.18" +description: "v1.4.18 changelog - 2026-03-29" +--- + + + ```bash + npx -y @maximhq/bifrost --transport-version v1.4.18 + ``` + + + ```bash + docker pull maximhq/bifrost:v1.4.18 + docker run -p 8080:8080 maximhq/bifrost:v1.4.18 + ``` + + + + +## ✨ Features + +- **Calendar-Aligned Budgets** — Added calendar alignment support for budget periods in governance + +## 🐞 Fixed + +- **SSE Error Events** — Handle SSE error events for 429 rate-limit and other error status codes during streaming +- **Anthropic Max Tokens** — Pick max tokens for Anthropic from model params cache instead of hardcoded values +- **Anthropic Streaming Usage** — Fixed usage token reporting for Anthropic streaming responses +- **Anthropic Cache Tokens** — Fixed Anthropic cache token reporting in non-streaming responses +- **Embedding Precision** — Preserved provider precision in embedding responses instead of truncating float values +- **Provider Caching** — Removed pending marshal-to-map to fix caching issues at provider level +- **Claude Office Suite** — Fixed support for Claude office suite add-on model routing +- **Semantic Cache Config** — Hardened direct-only config handling and aligned UI types for semantic cache +- **Semantic Cache Count Tokens** — Skip unsupported count_tokens requests in semantic cache plugin +- **Telemetry Events** — Removed reason field from telemetry events +- **CORS Headers** — Fixed wildcard allowed headers for CORS +- **UI Routing Display** — Shows selected virtual key and routing rule in UI + + + +- fix: handle SSE error events for 429s and other error status codes during streaming +- fix: pick max tokens for Anthropic from model params cache +- fix: fixed Anthropic streaming usage token reporting +- fix: fixed Anthropic cache token reporting +- fix: preserved provider precision in embedding responses +- fix: removed pending marshal-to-map to fix caching issues at provider level +- fix: fixed support for Claude office suite add-on model routing + + + +- feat: added CalendarAligned budget field, GetCalendarPeriodStart and IsCalendarAlignableDuration helpers +- fix: pick max tokens for Anthropic from model params cache (model catalog) +- fix: preserved provider precision in embedding responses (log store) +- fix: added migration for calendar_aligned field + + + +- feat: snap LastReset to calendar boundary for calendar-aligned budgets +- feat: added calendar alignment support for budget periods + + + +- chore: upgraded core to v1.4.15 and framework to v1.2.34 + + + +- chore: upgraded core to v1.4.15 and framework to v1.2.34 + + + +- chore: upgraded core to v1.4.15 and framework to v1.2.34 + + + +- chore: upgraded core to v1.4.15 and framework to v1.2.34 + + + +- chore: upgraded core to v1.4.15 and framework to v1.2.34 + + + +- chore: upgraded core to v1.4.15 and framework to v1.2.34 + + + +- fix: hardened direct-only config handling and aligned UI types +- fix: preserved provider precision in embedding responses +- fix: skip unsupported count_tokens requests + + + +- fix: removed reason field from telemetry events + + diff --git a/docs/docs.json b/docs/docs.json index 0649e840ab..b00e56f175 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -452,6 +452,13 @@ } ] }, + { + "tab": "Security", + "icon": "shield", + "pages": [ + "security" + ] + }, { "tab": "Benchmarks", "icon": "chart-line", @@ -470,6 +477,7 @@ "item": "Open Source", "icon": "rocket", "pages": [ + "changelogs/v1.4.18", "changelogs/v1.4.17", "changelogs/v1.4.16", "changelogs/v1.4.15", diff --git a/docs/media/security/codeowners.png b/docs/media/security/codeowners.png new file mode 100644 index 0000000000..de6f881b80 Binary files /dev/null and b/docs/media/security/codeowners.png differ diff --git a/docs/media/security/codeql.png b/docs/media/security/codeql.png new file mode 100644 index 0000000000..ac02a8c694 Binary files /dev/null and b/docs/media/security/codeql.png differ diff --git a/docs/media/security/dep-pinning.png b/docs/media/security/dep-pinning.png new file mode 100644 index 0000000000..6369e279e0 Binary files /dev/null and b/docs/media/security/dep-pinning.png differ diff --git a/docs/media/security/hardned-base-image.png b/docs/media/security/hardned-base-image.png new file mode 100644 index 0000000000..777af9a5f4 Binary files /dev/null and b/docs/media/security/hardned-base-image.png differ diff --git a/docs/media/security/scout-image-score.png b/docs/media/security/scout-image-score.png new file mode 100644 index 0000000000..4e3355fc97 Binary files /dev/null and b/docs/media/security/scout-image-score.png differ diff --git a/docs/media/security/step-security.png b/docs/media/security/step-security.png new file mode 100644 index 0000000000..1d85b04dd3 Binary files /dev/null and b/docs/media/security/step-security.png differ diff --git a/docs/security.mdx b/docs/security.mdx new file mode 100644 index 0000000000..a3c4ecc1b8 --- /dev/null +++ b/docs/security.mdx @@ -0,0 +1,289 @@ +--- +title: "Security at Bifrost" +description: "Overview of security practices across Bifrost's CI/CD pipelines, container images, supply chain, and deployment infrastructure." +icon: "shield" +sidebarTitle: "Security" +--- + +Bifrost applies defense-in-depth across its open-source and enterprise repositories. Every pull request, +dependency update, and container image goes through multiple layers of automated security checks before +reaching production. + +| Domain | Tool / Practice | Coverage | +| --- | --- | --- | +| Dependency Scanning | Snyk Open Source | Go, Node, Python — all projects | +| SAST | Snyk Code, CodeQL | Full codebase static analysis | +| Container Scanning | Docker Scout | Docker Hub auto-scan on push | +| Artifact Scanning | GCP Artifact Registry | Enterprise container images | +| Dependency Updates | Dependabot | gomod, npm, Docker, GitHub Actions | +| Supply Chain | SHA pinning, npm provenance | 100 % of GitHub Actions (OSS) | +| Container Hardening | FIPS base image, non-root user | Production Dockerfile | +| Security Hardening | StepSecurity, CODEOWNERS | Workflow hardening, code review gates | +| Network Security | Tailscale VPN | Enterprise deployments | + +--- + +## Vulnerability Scanning — Snyk + +Bifrost runs two Snyk scanning jobs on every push and pull request. Results are uploaded as +[SARIF](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning) +to the GitHub Security tab. + + + + The **Snyk Open Source** job scans all Go, Node, and Python dependencies for known vulnerabilities. + + ```yaml + snyk test --all-projects --detection-depth=4 --sarif-file-output=snyk.sarif + ``` + + - Scans every module across `core/`, `framework/`, `transports/`, `plugins/`, `ui/`, and `tests/` + - Detection depth of 4 catches transitive dependencies + - Snyk CLI pinned to `v1.1303.2` + + + + + The **Snyk Code** job performs Static Application Security Testing on the full codebase. + A complete build is performed first (Go + Node + Python) so Snyk can analyze compiled artifacts. + + ```yaml + snyk code test --sarif-file-output=snyk-code.sarif + ``` + + - Detects injection flaws, hardcoded secrets, insecure crypto, and other code-level vulnerabilities + - Builds the full project before scanning for accurate analysis + + + + + Snyk checks can be skipped by including `--skip-pipeline` in the first line of a commit message. This is + intended for documentation-only or CI configuration changes. + + +--- + +## Container Image Security + +### Dockerfile Hardening + +Production containers follow a strict hardening checklist: + + + + Three stages — UI builder (Node), Go builder, and minimal Alpine runtime — ensure no build tools or + source code leak into the final image. + + + Production images use a FIPS 140-2 validated Alpine base image with compliant OpenSSL. + + + The FIPS base image includes a dedicated `appuser`. The container runs as this unprivileged user — + never as root. + + + Go binaries are compiled with `-ldflags="-w -s"` to strip debug symbols and DWARF information, + reducing attack surface and image size. + + + +Additional hardening measures: + +- **Static builds** — Compiled with `-tags "sqlite_static"` and `-extldflags '-static'` for fully static linking +- **Build verification** — `RUN test -f /app/main || exit 1` ensures the binary exists before proceeding +- **CVE patching** — Enterprise Dockerfiles include targeted patches (e.g., `apk upgrade --no-cache openssl` for CVE-2026-22796) +- **Minimal runtime dependencies** — The FIPS base image provides only essential libraries (`musl`, `libgcc`, `ca-certificates`) + +```dockerfile +# Runtime stage excerpt (production) +FROM +WORKDIR /app + +COPY --from=builder /app/main . +COPY --from=builder /app/docker-entrypoint.sh . + +RUN mkdir -p $APP_DIR/logs +USER appuser + +ENTRYPOINT ["/app/docker-entrypoint.sh"] +CMD ["/app/main"] +``` + + + FIPS-compliant hardened base image in Dockerfile + + +### Docker Scout + +[Docker Scout](https://docs.docker.com/scout/) is enabled at the Docker Hub repository level for the +`maximhq/bifrost` image. Every image pushed to Docker Hub is automatically scanned for CVEs against +continuously updated vulnerability databases. + + + Docker Scout image score showing vulnerability assessment + + +### GCP Artifact Registry Scanning + +Enterprise images are pushed to **GCP Artifact Registry** (and AWS ECR for select environments). +GCP Artifact Registry provides [built-in vulnerability scanning](https://cloud.google.com/artifact-registry/docs/analysis) +that automatically analyzes container images for OS and language package vulnerabilities. + +--- + +## Supply Chain Security + +### GitHub Actions SHA Pinning + +All GitHub Actions in the open-source repository are pinned to exact commit SHAs — not mutable version +tags. This prevents supply chain attacks where a compromised action maintainer could push malicious code +to an existing tag. + +```yaml +# Every action is pinned to a full SHA with a version comment +- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 +- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 +- uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 +``` + +**Coverage:** + +| Repository | Pinning Strategy | Actions Pinned | +| --- | --- | --- | +| `bifrost` (OSS) | Full SHA with version comment | 103 / 103 (100 %) | +| `bifrost-enterprise` | Full SHA with version comment | 103 / 103 (100 %) | + +### NPM Provenance + +Published npm packages include [SLSA provenance attestations](https://docs.npmjs.com/generating-provenance-statements), +providing a verifiable link between the published package and its source commit. + +```yaml +permissions: + id-token: write # Required for npm provenance + +# ... +npm publish --provenance --access public +``` + +### Dependency Pinning in CI + +All language runtimes are pinned to specific versions across CI workflows to ensure reproducible builds +and prevent unexpected behavior from runtime updates. + +| Runtime | Pinned Version | Used For | +| --- | --- | --- | +| Go | `1.26.1` | Core build, tests | +| Node | `25` | UI build, npm packages | +| Python | `3.11` | Integration and governance tests | +| uv | SHA-pinned via `astral-sh/setup-uv` | Python package management | + +Python test dependencies are locked via `uv.lock` files for deterministic, reproducible installs: + +``` +tests/integrations/uv.lock +tests/governance/uv.lock +``` + + + Pinned dependency versions across CI workflows + + +--- + +## Dependency Management — Dependabot + +Dependabot monitors four ecosystems on a weekly schedule, automatically opening pull requests for +outdated or vulnerable dependencies. + + + + Covers `/core`, `/framework`, `/transports`, `/plugins/*`, and `/examples/**` + + + Covers `/ui`, `/npx`, and `/examples/**` + + + Monitors base images in `/transports` + + + Tracks action version updates across all workflows + + + +A separate **Dependabot Alerts** workflow runs daily and automatically +creates GitHub issues for any open Dependabot security alerts, categorized by severity and ecosystem. + +--- + +## Code Analysis — CodeQL + +GitHub's [CodeQL](https://codeql.github.com/) performs semantic code analysis on every push and pull request, +complementing Snyk's SAST coverage with GitHub-native findings. + +- Analyzes Go and JavaScript/TypeScript codebases +- Detects security vulnerabilities, bugs, and code quality issues using GitHub's query suites +- Results appear directly in the GitHub Security tab alongside Snyk findings + + + CodeQL analysis results in GitHub Security tab + + +--- + +## Workflow & Network Security + +### Principle of Least Privilege + +All GitHub Actions workflows follow the principle of least privilege. Permissions are set at the +job level, not the workflow level, and are scoped to the minimum required. + +| Permission | Granted To | Reason | +| --- | --- | --- | +| `contents: read` | All jobs (default) | Read repository code | +| `contents: write` | Tag creation, releases | Create git tags and releases | +| `security-events: write` | Snyk jobs | Upload SARIF to Security tab | +| `id-token: write` | Cloud auth, npm publish | OIDC federation, npm provenance | +| `pull-requests: write` | PR test reporters | Post test results as PR comments | + +### Tailscale VPN + +Enterprise deployment workflows authenticate through [Tailscale](https://tailscale.com/) before +accessing any infrastructure. This ensures that CI/CD runners can only reach deployment targets +through an encrypted, identity-aware network — never over the public internet. + +```yaml +- name: Authenticate Tailscale + uses: tailscale/github-action@v4 + with: + oauth-client-id: '${{ secrets.TS_OAUTH_CLIENT_ID }}' + oauth-secret: '${{ secrets.TS_OAUTH_SECRET }}' + tags: 'tag:gha-ci' + version: 1.84.0 +``` + +- **OAuth-based authentication** — No long-lived API keys; runners authenticate via OAuth client credentials +- **Version pinned** — Tailscale `1.84.0` to prevent unexpected behavior from updates +- **Tagged runners** — `gha-ci` tag enables Tailscale ACL policies scoped to CI/CD access + +### StepSecurity + +[StepSecurity](https://www.stepsecurity.io/) automatically hardens GitHub Actions workflows by applying +security best practices across all CI/CD pipelines. + +- Adds `permissions` blocks to workflows that are missing them +- Pins action versions to full SHAs where mutable tags were used +- Detects insecure patterns like unquoted interpolations and artifact poisoning risks + + + StepSecurity automated security hardening applied to GitHub Actions workflows + + +### CODEOWNERS + +Critical paths in the repository are protected by a `CODEOWNERS` file, ensuring that changes to +security-sensitive areas require review from designated maintainers before merging. + + + CODEOWNERS file enforcing review gates on security-critical paths + diff --git a/examples/plugins/hello-world/go.mod b/examples/plugins/hello-world/go.mod index 7f52fcc4b8..4e1251f499 100644 --- a/examples/plugins/hello-world/go.mod +++ b/examples/plugins/hello-world/go.mod @@ -2,7 +2,7 @@ module github.com/maximhq/bifrost/examples/plugins/hello-world go 1.26.1 -require github.com/maximhq/bifrost/core v1.4.14 +require github.com/maximhq/bifrost/core v1.4.15 require ( github.com/andybalholm/brotli v1.2.0 // indirect diff --git a/examples/plugins/hello-world/go.sum b/examples/plugins/hello-world/go.sum index 4fb8771184..a31aa03a95 100644 --- a/examples/plugins/hello-world/go.sum +++ b/examples/plugins/hello-world/go.sum @@ -39,8 +39,8 @@ github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8 github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mark3labs/mcp-go v0.43.2 h1:21PUSlWWiSbUPQwXIJ5WKlETixpFpq+WBpbMGDSVy/I= github.com/mark3labs/mcp-go v0.43.2/go.mod h1:YnJfOL382MIWDx1kMY+2zsRHU/q78dBg9aFb8W6Thdw= -github.com/maximhq/bifrost/core v1.4.14 h1:apR7IsaXYlcciNrjotjqRngfYesI5YNleBQ3/bgxGiA= -github.com/maximhq/bifrost/core v1.4.14/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= +github.com/maximhq/bifrost/core v1.4.15 h1:usgMeCQFZJRp5bSshJKbs+10bOYOWq/X3H4fUr78ZrA= +github.com/maximhq/bifrost/core v1.4.15/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/framework/changelog.md b/framework/changelog.md index be8dba01f2..555b02a39b 100644 --- a/framework/changelog.md +++ b/framework/changelog.md @@ -1,6 +1,6 @@ -- feat: migrate VK provider config allowed keys to explicit allow-list semantics — add AllowAllKeys bool to TableVirtualKeyProviderConfig; backfill existing configs with allow_all_keys=true; empty keys now denies all, ["*"] allows all -- feat: add MCPDisableAutoToolInject column to TableClientConfig -- refactor: standardize empty array conventions in modelcatalog and tables. -- feat: add AllowedExtraHeadersJSON column to TableMCPClient -- feat: add AllowOnAllVirtualKeys column to TableMCPClient -- feat: add CalendarAligned budget field, GetCalendarPeriodStart and IsCalendarAlignableDuration helpers [@17jmumford](https://github.com/17jmumford) +- feat: migrate VK provider config allowed keys to explicit allow-list semantics — add AllowAllKeys bool to TableVirtualKeyProviderConfig; backfill existing configs with allow_all_keys=true; empty keys now denies all, ["*"] allows all +- feat: add MCPDisableAutoToolInject column to TableClientConfig +- refactor: standardize empty array conventions in modelcatalog and tables. +- feat: add AllowedExtraHeadersJSON column to TableMCPClient +- feat: add AllowOnAllVirtualKeys column to TableMCPClient +- feat: add CalendarAligned budget field, GetCalendarPeriodStart and IsCalendarAlignableDuration helpers [@17jmumford](https://github.com/17jmumford) diff --git a/framework/go.mod b/framework/go.mod index 9cad727eb0..cfc2e008a9 100644 --- a/framework/go.mod +++ b/framework/go.mod @@ -4,7 +4,7 @@ go 1.26.1 require ( github.com/google/uuid v1.6.0 - github.com/maximhq/bifrost/core v1.4.14 + github.com/maximhq/bifrost/core v1.4.15 github.com/pinecone-io/go-pinecone/v5 v5.3.0 github.com/qdrant/go-client v1.16.2 github.com/redis/go-redis/v9 v9.17.2 diff --git a/framework/go.sum b/framework/go.sum index cb1eb25153..01dcd4a578 100644 --- a/framework/go.sum +++ b/framework/go.sum @@ -193,8 +193,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/maximhq/bifrost/core v1.4.14 h1:apR7IsaXYlcciNrjotjqRngfYesI5YNleBQ3/bgxGiA= -github.com/maximhq/bifrost/core v1.4.14/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= +github.com/maximhq/bifrost/core v1.4.15 h1:usgMeCQFZJRp5bSshJKbs+10bOYOWq/X3H4fUr78ZrA= +github.com/maximhq/bifrost/core v1.4.15/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= diff --git a/framework/modelcatalog/main.go b/framework/modelcatalog/main.go index c40af1271f..315ba36887 100644 --- a/framework/modelcatalog/main.go +++ b/framework/modelcatalog/main.go @@ -128,6 +128,9 @@ type PricingOptions struct { OutputCostPerVideoPerSecond *float64 `json:"output_cost_per_video_per_second,omitempty"` OutputCostPerSecond *float64 `json:"output_cost_per_second,omitempty"` + // Model parameters + MaxOutputTokens *int `json:"max_output_tokens,omitempty"` + // Costs - Other // // SearchContextCostPerQuery is stored as a single float64, but the pricing datasheet diff --git a/framework/modelcatalog/sync.go b/framework/modelcatalog/sync.go index e5ae7599de..29c88542a6 100644 --- a/framework/modelcatalog/sync.go +++ b/framework/modelcatalog/sync.go @@ -124,10 +124,30 @@ func (mc *ModelCatalog) syncPricing(ctx context.Context) error { return fmt.Errorf("failed to reload pricing cache: %w", err) } + // Populate model params cache from pricing datasheet max_output_tokens + mc.populateModelParamsFromPricing(pricingData) + mc.logger.Info("successfully synced %d pricing records", len(pricingData)) return nil } +// populateModelParamsFromPricing extracts max_output_tokens from pricing entries +// and populates the model params cache so that providers can look up max output +// tokens without a separate model-parameters sync. +func (mc *ModelCatalog) populateModelParamsFromPricing(pricingData map[string]PricingEntry) { + modelParamsEntries := make(map[string]providerUtils.ModelParams) + for modelKey, entry := range pricingData { + if entry.MaxOutputTokens != nil { + modelName := extractModelName(modelKey) + modelParamsEntries[modelName] = providerUtils.ModelParams{MaxOutputTokens: entry.MaxOutputTokens} + } + } + if len(modelParamsEntries) > 0 { + providerUtils.BulkSetModelParams(modelParamsEntries) + mc.logger.Debug("populated %d model params entries from pricing datasheet", len(modelParamsEntries)) + } +} + // loadPricingFromURL loads pricing data from the remote URL func (mc *ModelCatalog) loadPricingFromURL(ctx context.Context) (map[string]PricingEntry, error) { // Create HTTP client with timeout @@ -183,6 +203,9 @@ func (mc *ModelCatalog) loadPricingIntoMemory(ctx context.Context) error { mc.pricingData[key] = pricing } + // Populate model params cache from pricing datasheet max_output_tokens + mc.populateModelParamsFromPricing(pricingData) + return nil } diff --git a/framework/modelcatalog/utils.go b/framework/modelcatalog/utils.go index 4808ee844d..db4fae6285 100644 --- a/framework/modelcatalog/utils.go +++ b/framework/modelcatalog/utils.go @@ -79,18 +79,21 @@ func normalizeStreamRequestType(rt schemas.RequestType) schemas.RequestType { } } -// convertPricingDataToTableModelPricing converts the pricing data to a TableModelPricing struct -func convertPricingDataToTableModelPricing(modelKey string, entry PricingEntry) configstoreTables.TableModelPricing { - provider := normalizeProvider(entry.Provider) - - // Handle provider/model format - extract just the model name - modelName := modelKey +// extractModelName extracts the model name from a model key that may be in provider/model format +func extractModelName(modelKey string) string { if strings.Contains(modelKey, "/") { parts := strings.Split(modelKey, "/") if len(parts) > 1 { - modelName = strings.Join(parts[1:], "/") + return strings.Join(parts[1:], "/") } } + return modelKey +} + +// convertPricingDataToTableModelPricing converts the pricing data to a TableModelPricing struct +func convertPricingDataToTableModelPricing(modelKey string, entry PricingEntry) configstoreTables.TableModelPricing { + provider := normalizeProvider(entry.Provider) + modelName := extractModelName(modelKey) return configstoreTables.TableModelPricing{ Model: modelName, diff --git a/framework/version b/framework/version index 47f5bfd9fa..dcbb259019 100644 --- a/framework/version +++ b/framework/version @@ -1 +1 @@ -1.2.33 +1.2.34 \ No newline at end of file diff --git a/helm-charts/bifrost/values.schema.json b/helm-charts/bifrost/values.schema.json index a86ec8b616..4d15e9a712 100644 --- a/helm-charts/bifrost/values.schema.json +++ b/helm-charts/bifrost/values.schema.json @@ -714,9 +714,7 @@ "properties": { "config": { "required": [ - "dimension", - "keys", - "provider" + "dimension" ] } } diff --git a/plugins/governance/changelog.md b/plugins/governance/changelog.md index dd0bbfc9a1..8b2384a219 100644 --- a/plugins/governance/changelog.md +++ b/plugins/governance/changelog.md @@ -1,4 +1,4 @@ -- feat: migrate VK provider config allowed keys to deny-by-default — `key_ids: ["*"]` (API) / `allowed_keys: ["*"]` (config) maps to `AllowAllKeys=true` without DB lookups; empty list sets `AllowAllKeys=false` and blocks all keys; resolver populates `includeOnlyKeys` for specific keys or clears it to nil for allow-all to prevent stale filters -- feat: enforce VK MCPConfigs as an execution-time allow-list — empty MCPConfigs denies all MCP tools, non-empty validates each tool in both PreMCPHook and evaluateGovernanceRequest; respects disable_auto_tool_inject toggle (transport config key: mcp_disable_auto_tool_inject) and skips auto-injection header when caller already set it -- feat: adds handling for MCP clients with AllowOnAllVirtualKeys to allow all tools for all virtual keys. -- feat: snap LastReset to calendar boundary for calendar-aligned budgets [@17jmumford](https://github.com/17jmumford) +- feat: migrate VK provider config allowed keys to deny-by-default — `key_ids: ["*"]` (API) / `allowed_keys: ["*"]` (config) maps to `AllowAllKeys=true` without DB lookups; empty list sets `AllowAllKeys=false` and blocks all keys; resolver populates `includeOnlyKeys` for specific keys or clears it to nil for allow-all to prevent stale filters +- feat: enforce VK MCPConfigs as an execution-time allow-list — empty MCPConfigs denies all MCP tools, non-empty validates each tool in both PreMCPHook and evaluateGovernanceRequest; respects disable_auto_tool_inject toggle (transport config key: mcp_disable_auto_tool_inject) and skips auto-injection header when caller already set it +- feat: adds handling for MCP clients with AllowOnAllVirtualKeys to allow all tools for all virtual keys. +- feat: snap LastReset to calendar boundary for calendar-aligned budgets [@17jmumford](https://github.com/17jmumford) diff --git a/plugins/governance/go.mod b/plugins/governance/go.mod index 7abb1aaedc..3505674260 100644 --- a/plugins/governance/go.mod +++ b/plugins/governance/go.mod @@ -8,8 +8,8 @@ require ( github.com/bytedance/sonic v1.15.0 github.com/google/cel-go v0.26.1 github.com/google/uuid v1.6.0 - github.com/maximhq/bifrost/core v1.4.14 - github.com/maximhq/bifrost/framework v1.2.33 + github.com/maximhq/bifrost/core v1.4.15 + github.com/maximhq/bifrost/framework v1.2.34 github.com/stretchr/testify v1.11.1 github.com/valyala/fasthttp v1.68.0 ) diff --git a/plugins/governance/go.sum b/plugins/governance/go.sum index da97c76f85..947d9641bc 100644 --- a/plugins/governance/go.sum +++ b/plugins/governance/go.sum @@ -199,10 +199,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/maximhq/bifrost/core v1.4.14 h1:apR7IsaXYlcciNrjotjqRngfYesI5YNleBQ3/bgxGiA= -github.com/maximhq/bifrost/core v1.4.14/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= -github.com/maximhq/bifrost/framework v1.2.33 h1:KpaOJu8eIqVDwnAKOPvQHPIWoAjiezKhm3IcPF4pMmw= -github.com/maximhq/bifrost/framework v1.2.33/go.mod h1:WA3/MbwTFS3tvz/fC5CRRiODzIlGn7rkwJ3WdcRi+y8= +github.com/maximhq/bifrost/core v1.4.15 h1:usgMeCQFZJRp5bSshJKbs+10bOYOWq/X3H4fUr78ZrA= +github.com/maximhq/bifrost/core v1.4.15/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= +github.com/maximhq/bifrost/framework v1.2.34 h1:K877/GBMB/VG+YWXNKuNEmbg6XG7IKlgFS0jcoe5EaA= +github.com/maximhq/bifrost/framework v1.2.34/go.mod h1:C8tzNb8tnOdauF6sycjjKTq/RjuUpB3wb0MhKnJoXmM= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= diff --git a/plugins/governance/version b/plugins/governance/version index 9baec2fdfe..f32f94b9f6 100644 --- a/plugins/governance/version +++ b/plugins/governance/version @@ -1 +1 @@ -1.4.33 +1.4.34 \ No newline at end of file diff --git a/plugins/jsonparser/go.mod b/plugins/jsonparser/go.mod index fea31a40a5..2f76b95b6a 100644 --- a/plugins/jsonparser/go.mod +++ b/plugins/jsonparser/go.mod @@ -2,7 +2,7 @@ module github.com/maximhq/bifrost/plugins/jsonparser go 1.26.1 -require github.com/maximhq/bifrost/core v1.4.14 +require github.com/maximhq/bifrost/core v1.4.15 require ( cloud.google.com/go v0.123.0 // indirect diff --git a/plugins/jsonparser/go.sum b/plugins/jsonparser/go.sum index 55a09e1524..338f34cdc1 100644 --- a/plugins/jsonparser/go.sum +++ b/plugins/jsonparser/go.sum @@ -109,8 +109,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/maximhq/bifrost/core v1.4.14 h1:apR7IsaXYlcciNrjotjqRngfYesI5YNleBQ3/bgxGiA= -github.com/maximhq/bifrost/core v1.4.14/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= +github.com/maximhq/bifrost/core v1.4.15 h1:usgMeCQFZJRp5bSshJKbs+10bOYOWq/X3H4fUr78ZrA= +github.com/maximhq/bifrost/core v1.4.15/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/plugins/jsonparser/version b/plugins/jsonparser/version index 00bbe72aea..5d3dbca71a 100644 --- a/plugins/jsonparser/version +++ b/plugins/jsonparser/version @@ -1 +1 @@ -1.4.32 +1.4.33 \ No newline at end of file diff --git a/plugins/litellmcompat/go.mod b/plugins/litellmcompat/go.mod index 57d3591b5c..f9c9a193a3 100644 --- a/plugins/litellmcompat/go.mod +++ b/plugins/litellmcompat/go.mod @@ -3,8 +3,8 @@ module github.com/maximhq/bifrost/plugins/litellmcompat go 1.26.1 require ( - github.com/maximhq/bifrost/core v1.4.14 - github.com/maximhq/bifrost/framework v1.2.33 + github.com/maximhq/bifrost/core v1.4.15 + github.com/maximhq/bifrost/framework v1.2.34 ) require ( diff --git a/plugins/litellmcompat/go.sum b/plugins/litellmcompat/go.sum index d7ee8582d0..31f3e52166 100644 --- a/plugins/litellmcompat/go.sum +++ b/plugins/litellmcompat/go.sum @@ -193,10 +193,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/maximhq/bifrost/core v1.4.14 h1:apR7IsaXYlcciNrjotjqRngfYesI5YNleBQ3/bgxGiA= -github.com/maximhq/bifrost/core v1.4.14/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= -github.com/maximhq/bifrost/framework v1.2.33 h1:KpaOJu8eIqVDwnAKOPvQHPIWoAjiezKhm3IcPF4pMmw= -github.com/maximhq/bifrost/framework v1.2.33/go.mod h1:WA3/MbwTFS3tvz/fC5CRRiODzIlGn7rkwJ3WdcRi+y8= +github.com/maximhq/bifrost/core v1.4.15 h1:usgMeCQFZJRp5bSshJKbs+10bOYOWq/X3H4fUr78ZrA= +github.com/maximhq/bifrost/core v1.4.15/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= +github.com/maximhq/bifrost/framework v1.2.34 h1:K877/GBMB/VG+YWXNKuNEmbg6XG7IKlgFS0jcoe5EaA= +github.com/maximhq/bifrost/framework v1.2.34/go.mod h1:C8tzNb8tnOdauF6sycjjKTq/RjuUpB3wb0MhKnJoXmM= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= diff --git a/plugins/litellmcompat/version b/plugins/litellmcompat/version index 818944f5b8..95dfee2488 100644 --- a/plugins/litellmcompat/version +++ b/plugins/litellmcompat/version @@ -1 +1 @@ -0.0.22 +0.0.23 \ No newline at end of file diff --git a/plugins/logging/go.mod b/plugins/logging/go.mod index e7187bc4e4..8ebe614ea1 100644 --- a/plugins/logging/go.mod +++ b/plugins/logging/go.mod @@ -4,8 +4,8 @@ go 1.26.1 require ( github.com/bytedance/sonic v1.15.0 - github.com/maximhq/bifrost/core v1.4.14 - github.com/maximhq/bifrost/framework v1.2.33 + github.com/maximhq/bifrost/core v1.4.15 + github.com/maximhq/bifrost/framework v1.2.34 ) require ( diff --git a/plugins/logging/go.sum b/plugins/logging/go.sum index d7ee8582d0..31f3e52166 100644 --- a/plugins/logging/go.sum +++ b/plugins/logging/go.sum @@ -193,10 +193,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/maximhq/bifrost/core v1.4.14 h1:apR7IsaXYlcciNrjotjqRngfYesI5YNleBQ3/bgxGiA= -github.com/maximhq/bifrost/core v1.4.14/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= -github.com/maximhq/bifrost/framework v1.2.33 h1:KpaOJu8eIqVDwnAKOPvQHPIWoAjiezKhm3IcPF4pMmw= -github.com/maximhq/bifrost/framework v1.2.33/go.mod h1:WA3/MbwTFS3tvz/fC5CRRiODzIlGn7rkwJ3WdcRi+y8= +github.com/maximhq/bifrost/core v1.4.15 h1:usgMeCQFZJRp5bSshJKbs+10bOYOWq/X3H4fUr78ZrA= +github.com/maximhq/bifrost/core v1.4.15/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= +github.com/maximhq/bifrost/framework v1.2.34 h1:K877/GBMB/VG+YWXNKuNEmbg6XG7IKlgFS0jcoe5EaA= +github.com/maximhq/bifrost/framework v1.2.34/go.mod h1:C8tzNb8tnOdauF6sycjjKTq/RjuUpB3wb0MhKnJoXmM= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= diff --git a/plugins/logging/version b/plugins/logging/version index 9baec2fdfe..f32f94b9f6 100644 --- a/plugins/logging/version +++ b/plugins/logging/version @@ -1 +1 @@ -1.4.33 +1.4.34 \ No newline at end of file diff --git a/plugins/maxim/go.mod b/plugins/maxim/go.mod index de87544ce1..5743456ae7 100644 --- a/plugins/maxim/go.mod +++ b/plugins/maxim/go.mod @@ -3,8 +3,8 @@ module github.com/maximhq/bifrost/plugins/maxim go 1.26.1 require ( - github.com/maximhq/bifrost/core v1.4.14 - github.com/maximhq/bifrost/framework v1.2.33 + github.com/maximhq/bifrost/core v1.4.15 + github.com/maximhq/bifrost/framework v1.2.34 github.com/maximhq/maxim-go v0.2.0 ) diff --git a/plugins/maxim/go.sum b/plugins/maxim/go.sum index 878b3de101..fb0355d80d 100644 --- a/plugins/maxim/go.sum +++ b/plugins/maxim/go.sum @@ -193,10 +193,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/maximhq/bifrost/core v1.4.14 h1:apR7IsaXYlcciNrjotjqRngfYesI5YNleBQ3/bgxGiA= -github.com/maximhq/bifrost/core v1.4.14/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= -github.com/maximhq/bifrost/framework v1.2.33 h1:KpaOJu8eIqVDwnAKOPvQHPIWoAjiezKhm3IcPF4pMmw= -github.com/maximhq/bifrost/framework v1.2.33/go.mod h1:WA3/MbwTFS3tvz/fC5CRRiODzIlGn7rkwJ3WdcRi+y8= +github.com/maximhq/bifrost/core v1.4.15 h1:usgMeCQFZJRp5bSshJKbs+10bOYOWq/X3H4fUr78ZrA= +github.com/maximhq/bifrost/core v1.4.15/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= +github.com/maximhq/bifrost/framework v1.2.34 h1:K877/GBMB/VG+YWXNKuNEmbg6XG7IKlgFS0jcoe5EaA= +github.com/maximhq/bifrost/framework v1.2.34/go.mod h1:C8tzNb8tnOdauF6sycjjKTq/RjuUpB3wb0MhKnJoXmM= github.com/maximhq/maxim-go v0.2.0 h1:3SNpna+Z9bDcUBqPLV/pfaZaxTEtsyix7Rn1KtwoEp4= github.com/maximhq/maxim-go v0.2.0/go.mod h1:RvESsFEUWSJmIypHtV+vHN2EWjp+HoqWGStEvB/cPBU= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= diff --git a/plugins/maxim/version b/plugins/maxim/version index 6062bd9648..f79cc1c383 100644 --- a/plugins/maxim/version +++ b/plugins/maxim/version @@ -1 +1 @@ -1.5.32 +1.5.33 \ No newline at end of file diff --git a/plugins/mocker/go.mod b/plugins/mocker/go.mod index b3ef7e9ec3..e78ddf590c 100644 --- a/plugins/mocker/go.mod +++ b/plugins/mocker/go.mod @@ -4,7 +4,7 @@ go 1.26.1 require ( github.com/jaswdr/faker/v2 v2.8.0 - github.com/maximhq/bifrost/core v1.4.14 + github.com/maximhq/bifrost/core v1.4.15 ) require ( diff --git a/plugins/mocker/go.sum b/plugins/mocker/go.sum index d530baba38..ace0049a63 100644 --- a/plugins/mocker/go.sum +++ b/plugins/mocker/go.sum @@ -111,8 +111,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/maximhq/bifrost/core v1.4.14 h1:apR7IsaXYlcciNrjotjqRngfYesI5YNleBQ3/bgxGiA= -github.com/maximhq/bifrost/core v1.4.14/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= +github.com/maximhq/bifrost/core v1.4.15 h1:usgMeCQFZJRp5bSshJKbs+10bOYOWq/X3H4fUr78ZrA= +github.com/maximhq/bifrost/core v1.4.15/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/plugins/mocker/version b/plugins/mocker/version index 00bbe72aea..5d3dbca71a 100644 --- a/plugins/mocker/version +++ b/plugins/mocker/version @@ -1 +1 @@ -1.4.32 +1.4.33 \ No newline at end of file diff --git a/plugins/otel/go.mod b/plugins/otel/go.mod index 95e98ba095..e6775da882 100644 --- a/plugins/otel/go.mod +++ b/plugins/otel/go.mod @@ -3,8 +3,8 @@ module github.com/maximhq/bifrost/plugins/otel go 1.26.1 require ( - github.com/maximhq/bifrost/core v1.4.14 - github.com/maximhq/bifrost/framework v1.2.33 + github.com/maximhq/bifrost/core v1.4.15 + github.com/maximhq/bifrost/framework v1.2.34 go.opentelemetry.io/otel v1.40.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.40.0 diff --git a/plugins/otel/go.sum b/plugins/otel/go.sum index bf00bb4c13..580ddc194a 100644 --- a/plugins/otel/go.sum +++ b/plugins/otel/go.sum @@ -197,10 +197,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/maximhq/bifrost/core v1.4.14 h1:apR7IsaXYlcciNrjotjqRngfYesI5YNleBQ3/bgxGiA= -github.com/maximhq/bifrost/core v1.4.14/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= -github.com/maximhq/bifrost/framework v1.2.33 h1:KpaOJu8eIqVDwnAKOPvQHPIWoAjiezKhm3IcPF4pMmw= -github.com/maximhq/bifrost/framework v1.2.33/go.mod h1:WA3/MbwTFS3tvz/fC5CRRiODzIlGn7rkwJ3WdcRi+y8= +github.com/maximhq/bifrost/core v1.4.15 h1:usgMeCQFZJRp5bSshJKbs+10bOYOWq/X3H4fUr78ZrA= +github.com/maximhq/bifrost/core v1.4.15/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= +github.com/maximhq/bifrost/framework v1.2.34 h1:K877/GBMB/VG+YWXNKuNEmbg6XG7IKlgFS0jcoe5EaA= +github.com/maximhq/bifrost/framework v1.2.34/go.mod h1:C8tzNb8tnOdauF6sycjjKTq/RjuUpB3wb0MhKnJoXmM= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= diff --git a/plugins/otel/version b/plugins/otel/version index ba7b2f76d1..d28d4019a0 100644 --- a/plugins/otel/version +++ b/plugins/otel/version @@ -1 +1 @@ -1.1.32 +1.1.33 \ No newline at end of file diff --git a/plugins/semanticcache/go.mod b/plugins/semanticcache/go.mod index 2ee43b374f..f45ae6aad9 100644 --- a/plugins/semanticcache/go.mod +++ b/plugins/semanticcache/go.mod @@ -5,8 +5,8 @@ go 1.26.1 require ( github.com/cespare/xxhash/v2 v2.3.0 github.com/google/uuid v1.6.0 - github.com/maximhq/bifrost/core v1.4.14 - github.com/maximhq/bifrost/framework v1.2.33 + github.com/maximhq/bifrost/core v1.4.15 + github.com/maximhq/bifrost/framework v1.2.34 github.com/maximhq/bifrost/plugins/mocker v1.4.17 ) diff --git a/plugins/semanticcache/go.sum b/plugins/semanticcache/go.sum index 71a0202687..e23ab1356b 100644 --- a/plugins/semanticcache/go.sum +++ b/plugins/semanticcache/go.sum @@ -195,10 +195,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/maximhq/bifrost/core v1.4.14 h1:apR7IsaXYlcciNrjotjqRngfYesI5YNleBQ3/bgxGiA= -github.com/maximhq/bifrost/core v1.4.14/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= -github.com/maximhq/bifrost/framework v1.2.33 h1:KpaOJu8eIqVDwnAKOPvQHPIWoAjiezKhm3IcPF4pMmw= -github.com/maximhq/bifrost/framework v1.2.33/go.mod h1:WA3/MbwTFS3tvz/fC5CRRiODzIlGn7rkwJ3WdcRi+y8= +github.com/maximhq/bifrost/core v1.4.15 h1:usgMeCQFZJRp5bSshJKbs+10bOYOWq/X3H4fUr78ZrA= +github.com/maximhq/bifrost/core v1.4.15/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= +github.com/maximhq/bifrost/framework v1.2.34 h1:K877/GBMB/VG+YWXNKuNEmbg6XG7IKlgFS0jcoe5EaA= +github.com/maximhq/bifrost/framework v1.2.34/go.mod h1:C8tzNb8tnOdauF6sycjjKTq/RjuUpB3wb0MhKnJoXmM= github.com/maximhq/bifrost/plugins/mocker v1.4.17 h1:CEItx77k22fS/N5K8/dCQpse88yfbgzVebQWJXOH4NY= github.com/maximhq/bifrost/plugins/mocker v1.4.17/go.mod h1:RrA/XyRkggxYiK10k6D6r9VjfmRyiGBIW92ZvhWAtUw= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= diff --git a/plugins/semanticcache/version b/plugins/semanticcache/version index d156665af4..7e0d42a49d 100644 --- a/plugins/semanticcache/version +++ b/plugins/semanticcache/version @@ -1 +1 @@ -1.4.31 +1.4.32 \ No newline at end of file diff --git a/plugins/telemetry/go.mod b/plugins/telemetry/go.mod index 1936785d9b..6d28e68d3e 100644 --- a/plugins/telemetry/go.mod +++ b/plugins/telemetry/go.mod @@ -3,8 +3,8 @@ module github.com/maximhq/bifrost/plugins/telemetry go 1.26.1 require ( - github.com/maximhq/bifrost/core v1.4.14 - github.com/maximhq/bifrost/framework v1.2.33 + github.com/maximhq/bifrost/core v1.4.15 + github.com/maximhq/bifrost/framework v1.2.34 github.com/prometheus/client_golang v1.23.2 github.com/valyala/fasthttp v1.68.0 ) diff --git a/plugins/telemetry/go.sum b/plugins/telemetry/go.sum index 65efa790e3..3c7ff08608 100644 --- a/plugins/telemetry/go.sum +++ b/plugins/telemetry/go.sum @@ -195,10 +195,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/maximhq/bifrost/core v1.4.14 h1:apR7IsaXYlcciNrjotjqRngfYesI5YNleBQ3/bgxGiA= -github.com/maximhq/bifrost/core v1.4.14/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= -github.com/maximhq/bifrost/framework v1.2.33 h1:KpaOJu8eIqVDwnAKOPvQHPIWoAjiezKhm3IcPF4pMmw= -github.com/maximhq/bifrost/framework v1.2.33/go.mod h1:WA3/MbwTFS3tvz/fC5CRRiODzIlGn7rkwJ3WdcRi+y8= +github.com/maximhq/bifrost/core v1.4.15 h1:usgMeCQFZJRp5bSshJKbs+10bOYOWq/X3H4fUr78ZrA= +github.com/maximhq/bifrost/core v1.4.15/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= +github.com/maximhq/bifrost/framework v1.2.34 h1:K877/GBMB/VG+YWXNKuNEmbg6XG7IKlgFS0jcoe5EaA= +github.com/maximhq/bifrost/framework v1.2.34/go.mod h1:C8tzNb8tnOdauF6sycjjKTq/RjuUpB3wb0MhKnJoXmM= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= diff --git a/plugins/telemetry/version b/plugins/telemetry/version index 9baec2fdfe..f32f94b9f6 100644 --- a/plugins/telemetry/version +++ b/plugins/telemetry/version @@ -1 +1 @@ -1.4.33 +1.4.34 \ No newline at end of file diff --git a/transports/Dockerfile b/transports/Dockerfile index 6a42373fcf..4789eb60bf 100644 --- a/transports/Dockerfile +++ b/transports/Dockerfile @@ -1,106 +1,106 @@ # --- UI Build Stage: Build the Next.js frontend --- -FROM node:25-alpine3.23 AS ui-builder -WORKDIR /app - -# Copy UI package files and install dependencies -COPY ui/package*.json ./ -RUN npm ci - -# Copy UI source code -COPY ui/ ./ - -# Build UI (skip the copy-build step) -RUN npx next build -RUN node scripts/fix-paths.js -# Skip the copy-build step since we'll copy the files in the Go build stage - -# --- Go Build Stage: Compile the Go binary --- -FROM golang:1.26.1-alpine3.23 AS builder -WORKDIR /app - -# Install dependencies including gcc for CGO and sqlite -RUN apk add --no-cache gcc musl-dev sqlite-dev binutils binutils-gold - -# Set environment for CGO-enabled build (required for go-sqlite3) -ENV CGO_ENABLED=1 GOOS=linux - -COPY transports/go.mod transports/go.sum ./ -RUN ls -RUN cat go.mod -RUN go mod download - -# Copy source code and dependencies -COPY transports/ ./ - -COPY --from=ui-builder /app/out ./bifrost-http/ui - -# Build the binary with CGO enabled and static SQLite linking -ENV GOWORK=off -ARG VERSION=unknown -RUN go build \ - -ldflags="-w -s -X main.Version=v${VERSION} -extldflags '-static'" \ - -a -trimpath \ - -tags "sqlite_static" \ - -o /app/main \ - ./bifrost-http - -# Verify build succeeded -RUN test -f /app/main || (echo "Build failed" && exit 1) - -# --- Runtime Stage: Minimal runtime image --- -FROM alpine:3.23.3 -WORKDIR /app - -# Install runtime dependencies for CGO-enabled binary -# musl: C standard library (required for CGO binaries) -# libgcc: GCC runtime library -# ca-certificates: For HTTPS connections -RUN apk add --no-cache musl libgcc ca-certificates wget - -# Create data directory and set up user -COPY --from=builder /app/main . -COPY --from=builder /app/docker-entrypoint.sh . - -# Getting arguments -ARG ARG_APP_PORT=8080 -ARG ARG_APP_HOST=0.0.0.0 -ARG ARG_LOG_LEVEL=info -ARG ARG_LOG_STYLE=json -ARG ARG_APP_DIR=/app/data - -# Environment variables with defaults (can be overridden at runtime) -ENV APP_PORT=$ARG_APP_PORT \ - APP_HOST=$ARG_APP_HOST \ - LOG_LEVEL=$ARG_LOG_LEVEL \ - LOG_STYLE=$ARG_LOG_STYLE \ - APP_DIR=$ARG_APP_DIR - -# Go runtime performance tuning (override at runtime for your workload) -# GOGC: GC target percentage. Higher = less frequent GC, more memory usage. -# Default: 100. For high-throughput with available memory, try 200-400. -# GOMEMLIMIT: Soft memory limit for Go runtime. Set to ~90% of container memory limit. -# Example: "1800MiB" for a 2GB container, "3600MiB" for 4GB. -# When set, Go will be more aggressive about GC as it approaches this limit. -# Note: GOMAXPROCS is automatically detected from cgroup CPU limits via automaxprocs. -ENV GOGC="" \ - GOMEMLIMIT="" - - -RUN mkdir -p $APP_DIR/logs && \ - adduser -D -s /bin/sh appuser && \ - chown -R appuser:appuser /app && \ - chmod +x /app/docker-entrypoint.sh -USER appuser - - -# Declare volume for data persistence -VOLUME ["/app/data"] -EXPOSE $APP_PORT - -# Health check for container status monitoring -HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ - CMD wget --no-verbose --tries=1 -O /dev/null http://127.0.0.1:${APP_PORT}/health || exit 1 - -# Use entrypoint script that handles volume permissions and argument processing -ENTRYPOINT ["/app/docker-entrypoint.sh"] -CMD ["/app/main"] + FROM node:25-alpine3.23@sha256:cf38e1f3c28ac9d81cdc0c51d8220320b3b618780e44ef96a39f76f7dbfef023 AS ui-builder + WORKDIR /app + + # Copy UI package files and install dependencies + COPY ui/package*.json ./ + RUN npm ci + + # Copy UI source code + COPY ui/ ./ + + # Build UI (skip the copy-build step) + RUN npx next build + RUN node scripts/fix-paths.js + # Skip the copy-build step since we'll copy the files in the Go build stage + + # --- Go Build Stage: Compile the Go binary --- + FROM golang:1.26.1-alpine3.23@sha256:2389ebfa5b7f43eeafbd6be0c3700cc46690ef842ad962f6c5bd6be49ed82039 AS builder + WORKDIR /app + + # Install dependencies including gcc for CGO and sqlite + RUN apk add --no-cache gcc musl-dev sqlite-dev binutils binutils-gold + + # Set environment for CGO-enabled build (required for go-sqlite3) + ENV CGO_ENABLED=1 GOOS=linux + + COPY transports/go.mod transports/go.sum ./ + RUN ls + RUN cat go.mod + RUN go mod download + + # Copy source code and dependencies + COPY transports/ ./ + + COPY --from=ui-builder /app/out ./bifrost-http/ui + + # Build the binary with CGO enabled and static SQLite linking + ENV GOWORK=off + ARG VERSION=unknown + RUN go build \ + -ldflags="-w -s -X main.Version=v${VERSION} -extldflags '-static'" \ + -a -trimpath \ + -tags "sqlite_static" \ + -o /app/main \ + ./bifrost-http + + # Verify build succeeded + RUN test -f /app/main || (echo "Build failed" && exit 1) + + # --- Runtime Stage: Minimal runtime image --- + FROM alpine:3.23.3@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659 + WORKDIR /app + + # Install runtime dependencies for CGO-enabled binary + # musl: C standard library (required for CGO binaries) + # libgcc: GCC runtime library + # ca-certificates: For HTTPS connections + RUN apk add --no-cache musl libgcc ca-certificates wget + + # Create data directory and set up user + COPY --from=builder /app/main . + COPY --from=builder /app/docker-entrypoint.sh . + + # Getting arguments + ARG ARG_APP_PORT=8080 + ARG ARG_APP_HOST=0.0.0.0 + ARG ARG_LOG_LEVEL=info + ARG ARG_LOG_STYLE=json + ARG ARG_APP_DIR=/app/data + + # Environment variables with defaults (can be overridden at runtime) + ENV APP_PORT=$ARG_APP_PORT \ + APP_HOST=$ARG_APP_HOST \ + LOG_LEVEL=$ARG_LOG_LEVEL \ + LOG_STYLE=$ARG_LOG_STYLE \ + APP_DIR=$ARG_APP_DIR + + # Go runtime performance tuning (override at runtime for your workload) + # GOGC: GC target percentage. Higher = less frequent GC, more memory usage. + # Default: 100. For high-throughput with available memory, try 200-400. + # GOMEMLIMIT: Soft memory limit for Go runtime. Set to ~90% of container memory limit. + # Example: "1800MiB" for a 2GB container, "3600MiB" for 4GB. + # When set, Go will be more aggressive about GC as it approaches this limit. + # Note: GOMAXPROCS is automatically detected from cgroup CPU limits via automaxprocs. + ENV GOGC="" \ + GOMEMLIMIT="" + + + RUN mkdir -p $APP_DIR/logs && \ + adduser -D -s /bin/sh appuser && \ + chown -R appuser:appuser /app && \ + chmod +x /app/docker-entrypoint.sh + USER appuser + + + # Declare volume for data persistence + VOLUME ["/app/data"] + EXPOSE $APP_PORT + + # Health check for container status monitoring + HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 -O /dev/null http://127.0.0.1:${APP_PORT}/health || exit 1 + + # Use entrypoint script that handles volume permissions and argument processing + ENTRYPOINT ["/app/docker-entrypoint.sh"] + CMD ["/app/main"] \ No newline at end of file diff --git a/transports/Dockerfile.local b/transports/Dockerfile.local index 124e507d68..257a1e47c0 100644 --- a/transports/Dockerfile.local +++ b/transports/Dockerfile.local @@ -2,115 +2,115 @@ # For pre-release CI builds where module versions aren't published yet # --- UI Build Stage: Build the Next.js frontend --- -FROM node:25-alpine3.23 AS ui-builder -WORKDIR /app - -# Copy UI package files and install dependencies -COPY ui/package*.json ./ -RUN npm ci - -# Copy UI source code -COPY ui/ ./ - -# Build UI (skip the copy-build step) -RUN npx next build -RUN node scripts/fix-paths.js -# Skip the copy-build step since we'll copy the files in the Go build stage - -# --- Go Build Stage: Compile the Go binary using local modules --- -FROM golang:1.26.1-alpine3.23 AS builder -WORKDIR /build - -# Install dependencies including gcc for CGO and sqlite -RUN apk add --no-cache gcc musl-dev sqlite-dev binutils binutils-gold - -# Set environment for CGO-enabled build (required for go-sqlite3) -ENV CGO_ENABLED=1 GOOS=linux - -# Copy all local modules -COPY core/ ./core/ -COPY framework/ ./framework/ -COPY plugins/ ./plugins/ -COPY transports/ ./transports/ - -# Set up go workspace to resolve local module dependencies -RUN go work init && \ - go work use ./core && \ - go work use ./framework && \ - go work use ./plugins/governance && \ - go work use ./plugins/jsonparser && \ - go work use ./plugins/litellmcompat && \ - go work use ./plugins/logging && \ - go work use ./plugins/maxim && \ - go work use ./plugins/mocker && \ - go work use ./plugins/otel && \ - go work use ./plugins/semanticcache && \ - go work use ./plugins/telemetry && \ - go work use ./transports - -# Download external (non-local) dependencies -RUN cd /build/transports && go mod download - -# Copy UI build output into transports -COPY --from=ui-builder /app/out ./transports/bifrost-http/ui - -# Build the binary with CGO enabled and static SQLite linking -ARG VERSION=unknown -RUN cd /build/transports && \ - go build \ - -ldflags="-w -s -X main.Version=v${VERSION} -extldflags '-static'" \ - -a -trimpath \ - -tags "sqlite_static" \ - -o /app/main \ - ./bifrost-http - -# Verify build succeeded -RUN test -f /app/main || (echo "Build failed" && exit 1) - -# --- Runtime Stage: Minimal runtime image --- -FROM alpine:3.23.3 -WORKDIR /app - -# Install runtime dependencies for CGO-enabled binary -# musl: C standard library (required for CGO binaries) -# libgcc: GCC runtime library -# ca-certificates: For HTTPS connections -RUN apk add --no-cache musl libgcc ca-certificates wget - -# Create data directory and set up user -COPY --from=builder /app/main . -COPY --from=builder /build/transports/docker-entrypoint.sh . - -# Getting arguments -ARG ARG_APP_PORT=8080 -ARG ARG_APP_HOST=0.0.0.0 -ARG ARG_LOG_LEVEL=info -ARG ARG_LOG_STYLE=json -ARG ARG_APP_DIR=/app/data - -# Environment variables with defaults (can be overridden at runtime) -ENV APP_PORT=$ARG_APP_PORT \ - APP_HOST=$ARG_APP_HOST \ - LOG_LEVEL=$ARG_LOG_LEVEL \ - LOG_STYLE=$ARG_LOG_STYLE \ - APP_DIR=$ARG_APP_DIR - - -RUN mkdir -p $APP_DIR/logs && \ - adduser -D -s /bin/sh appuser && \ - chown -R appuser:appuser /app && \ - chmod +x /app/docker-entrypoint.sh -USER appuser - - -# Declare volume for data persistence -VOLUME ["/app/data"] -EXPOSE $APP_PORT - -# Health check for container status monitoring -HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ - CMD wget --no-verbose --tries=1 -O /dev/null http://127.0.0.1:${APP_PORT}/health || exit 1 - -# Use entrypoint script that handles volume permissions and argument processing -ENTRYPOINT ["/app/docker-entrypoint.sh"] -CMD ["/app/main"] + FROM node:25-alpine3.23 AS ui-builder + WORKDIR /app + + # Copy UI package files and install dependencies + COPY ui/package*.json ./ + RUN npm ci + + # Copy UI source code + COPY ui/ ./ + + # Build UI (skip the copy-build step) + RUN npx next build + RUN node scripts/fix-paths.js + # Skip the copy-build step since we'll copy the files in the Go build stage + + # --- Go Build Stage: Compile the Go binary using local modules --- + FROM golang:1.26.1-alpine3.23 AS builder + WORKDIR /build + + # Install dependencies including gcc for CGO and sqlite + RUN apk add --no-cache gcc musl-dev sqlite-dev binutils binutils-gold + + # Set environment for CGO-enabled build (required for go-sqlite3) + ENV CGO_ENABLED=1 GOOS=linux + + # Copy all local modules + COPY core/ ./core/ + COPY framework/ ./framework/ + COPY plugins/ ./plugins/ + COPY transports/ ./transports/ + + # Set up go workspace to resolve local module dependencies + RUN go work init && \ + go work use ./core && \ + go work use ./framework && \ + go work use ./plugins/governance && \ + go work use ./plugins/jsonparser && \ + go work use ./plugins/litellmcompat && \ + go work use ./plugins/logging && \ + go work use ./plugins/maxim && \ + go work use ./plugins/mocker && \ + go work use ./plugins/otel && \ + go work use ./plugins/semanticcache && \ + go work use ./plugins/telemetry && \ + go work use ./transports + + # Download external (non-local) dependencies + RUN cd /build/transports && go mod download + + # Copy UI build output into transports + COPY --from=ui-builder /app/out ./transports/bifrost-http/ui + + # Build the binary with CGO enabled and static SQLite linking + ARG VERSION=unknown + RUN cd /build/transports && \ + go build \ + -ldflags="-w -s -X main.Version=v${VERSION} -extldflags '-static'" \ + -a -trimpath \ + -tags "sqlite_static" \ + -o /app/main \ + ./bifrost-http + + # Verify build succeeded + RUN test -f /app/main || (echo "Build failed" && exit 1) + + # --- Runtime Stage: Minimal runtime image --- + FROM alpine:3.23.3 + WORKDIR /app + + # Install runtime dependencies for CGO-enabled binary + # musl: C standard library (required for CGO binaries) + # libgcc: GCC runtime library + # ca-certificates: For HTTPS connections + RUN apk add --no-cache musl libgcc ca-certificates wget + + # Create data directory and set up user + COPY --from=builder /app/main . + COPY --from=builder /build/transports/docker-entrypoint.sh . + + # Getting arguments + ARG ARG_APP_PORT=8080 + ARG ARG_APP_HOST=0.0.0.0 + ARG ARG_LOG_LEVEL=info + ARG ARG_LOG_STYLE=json + ARG ARG_APP_DIR=/app/data + + # Environment variables with defaults (can be overridden at runtime) + ENV APP_PORT=$ARG_APP_PORT \ + APP_HOST=$ARG_APP_HOST \ + LOG_LEVEL=$ARG_LOG_LEVEL \ + LOG_STYLE=$ARG_LOG_STYLE \ + APP_DIR=$ARG_APP_DIR + + + RUN mkdir -p $APP_DIR/logs && \ + adduser -D -s /bin/sh appuser && \ + chown -R appuser:appuser /app && \ + chmod +x /app/docker-entrypoint.sh + USER appuser + + + # Declare volume for data persistence + VOLUME ["/app/data"] + EXPOSE $APP_PORT + + # Health check for container status monitoring + HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 -O /dev/null http://127.0.0.1:${APP_PORT}/health || exit 1 + + # Use entrypoint script that handles volume permissions and argument processing + ENTRYPOINT ["/app/docker-entrypoint.sh"] + CMD ["/app/main"] \ No newline at end of file diff --git a/transports/config.schema.json b/transports/config.schema.json index 9cd015c1a9..d090eb132c 100644 --- a/transports/config.schema.json +++ b/transports/config.schema.json @@ -286,6 +286,11 @@ "type": "string", "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 } }, "required": [ diff --git a/transports/go.mod b/transports/go.mod index 728d4cc1bc..134e67e1f9 100644 --- a/transports/go.mod +++ b/transports/go.mod @@ -12,15 +12,15 @@ require ( github.com/google/uuid v1.6.0 github.com/klauspost/compress v1.18.2 github.com/mark3labs/mcp-go v0.43.2 - github.com/maximhq/bifrost/core v1.4.14 - github.com/maximhq/bifrost/framework v1.2.33 - github.com/maximhq/bifrost/plugins/governance v1.4.33 - github.com/maximhq/bifrost/plugins/litellmcompat v0.0.22 - github.com/maximhq/bifrost/plugins/logging v1.4.33 - github.com/maximhq/bifrost/plugins/maxim v1.5.32 - github.com/maximhq/bifrost/plugins/otel v1.1.32 - github.com/maximhq/bifrost/plugins/semanticcache v1.4.31 - github.com/maximhq/bifrost/plugins/telemetry v1.4.33 + github.com/maximhq/bifrost/core v1.4.15 + github.com/maximhq/bifrost/framework v1.2.34 + github.com/maximhq/bifrost/plugins/governance v1.4.34 + github.com/maximhq/bifrost/plugins/litellmcompat v0.0.23 + github.com/maximhq/bifrost/plugins/logging v1.4.34 + github.com/maximhq/bifrost/plugins/maxim v1.5.33 + github.com/maximhq/bifrost/plugins/otel v1.1.33 + github.com/maximhq/bifrost/plugins/semanticcache v1.4.32 + github.com/maximhq/bifrost/plugins/telemetry v1.4.34 github.com/prometheus/client_golang v1.23.2 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 github.com/stretchr/testify v1.11.1 @@ -111,7 +111,7 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.32 // indirect - github.com/maximhq/bifrost/plugins/mocker v1.4.32 // indirect + github.com/maximhq/bifrost/plugins/mocker v1.4.33 // indirect github.com/maximhq/maxim-go v0.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oapi-codegen/runtime v1.1.1 // indirect diff --git a/transports/go.sum b/transports/go.sum index f1de0cd432..e93943bee8 100644 --- a/transports/go.sum +++ b/transports/go.sum @@ -213,26 +213,26 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/maximhq/bifrost/core v1.4.14 h1:apR7IsaXYlcciNrjotjqRngfYesI5YNleBQ3/bgxGiA= -github.com/maximhq/bifrost/core v1.4.14/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= -github.com/maximhq/bifrost/framework v1.2.33 h1:KpaOJu8eIqVDwnAKOPvQHPIWoAjiezKhm3IcPF4pMmw= -github.com/maximhq/bifrost/framework v1.2.33/go.mod h1:WA3/MbwTFS3tvz/fC5CRRiODzIlGn7rkwJ3WdcRi+y8= -github.com/maximhq/bifrost/plugins/governance v1.4.33 h1:s8XLOqBmwaz6Fa8J5pRd4UxRi4I1LP/cHOXAfEoeXTM= -github.com/maximhq/bifrost/plugins/governance v1.4.33/go.mod h1:C867hyl3e1KepzNkZ6UimUm96GVU/0IjTB1KielyL48= -github.com/maximhq/bifrost/plugins/litellmcompat v0.0.22 h1:U2IRVzmBz69nvlojT+DRrfUdaoXF4xZ22cR+eIo0UeM= -github.com/maximhq/bifrost/plugins/litellmcompat v0.0.22/go.mod h1:Vd3mn99YjwAUmhM6tn6TN9VK0aHqQhuxoOZqfENab7A= -github.com/maximhq/bifrost/plugins/logging v1.4.33 h1:K2nrB3It67bMNb+mGrDmm6fhaRf5dbOFs25nPO0WGB0= -github.com/maximhq/bifrost/plugins/logging v1.4.33/go.mod h1:5C7q21JCuathNX5uWM0s4RYhnqbqZWIDyfW29Jn/4V0= -github.com/maximhq/bifrost/plugins/maxim v1.5.32 h1:m+Vh+6h5pYGh58seyYKZ5gC7+Bf2N5H/fjNi4lpmGqA= -github.com/maximhq/bifrost/plugins/maxim v1.5.32/go.mod h1:pfbUA9yjMn8vUNYAaFosHWSa7NQBODdZCUqe7Nd2mzg= -github.com/maximhq/bifrost/plugins/mocker v1.4.32 h1:AWiIjCLw9AnZpmr7BXvmf3NZWA0KCgzFlCwpm/puraM= -github.com/maximhq/bifrost/plugins/mocker v1.4.32/go.mod h1:RIyPJtRP2l6WzwpJsThIfXWOT5+pYs2a8gIknTf1Kf0= -github.com/maximhq/bifrost/plugins/otel v1.1.32 h1:g17+AVuhID+RXSuFrN5//rIg1H9YYSiN4O1oWPgUmE0= -github.com/maximhq/bifrost/plugins/otel v1.1.32/go.mod h1:PiTBInKjHNPhgdxSUEM3+73eyUEv6FF0IIJkl/NL4s4= -github.com/maximhq/bifrost/plugins/semanticcache v1.4.31 h1:yYiTLXVvOH/0E381E0grTLa9omBRA36DXq2VnPib8Zk= -github.com/maximhq/bifrost/plugins/semanticcache v1.4.31/go.mod h1:ByejDIl1A58VI7l5k3sfsVrU9hVNq94srbVMUw/SbWs= -github.com/maximhq/bifrost/plugins/telemetry v1.4.33 h1:lOuV7Hm/lbdfoZBg7z6t9x+pgOaGa4HQ2T4u305JWV8= -github.com/maximhq/bifrost/plugins/telemetry v1.4.33/go.mod h1:7DJavMvOFrhITnlr5tB+CXRU4xzU9E4oRwwR4sKhlMU= +github.com/maximhq/bifrost/core v1.4.15 h1:usgMeCQFZJRp5bSshJKbs+10bOYOWq/X3H4fUr78ZrA= +github.com/maximhq/bifrost/core v1.4.15/go.mod h1:A+AHUm/jf2lWFz5RNSxcJD/ozPlFJIVK9riMM1nyjt8= +github.com/maximhq/bifrost/framework v1.2.34 h1:K877/GBMB/VG+YWXNKuNEmbg6XG7IKlgFS0jcoe5EaA= +github.com/maximhq/bifrost/framework v1.2.34/go.mod h1:C8tzNb8tnOdauF6sycjjKTq/RjuUpB3wb0MhKnJoXmM= +github.com/maximhq/bifrost/plugins/governance v1.4.34 h1:1GQT8pasHKs70dNjOFAeZm+UWe2Ib9AqxQyhzHDvRJ8= +github.com/maximhq/bifrost/plugins/governance v1.4.34/go.mod h1:WZrrj3bM9jTgcC3nMaR6dk+bdK/uGjfdFt+MsUX6rcg= +github.com/maximhq/bifrost/plugins/litellmcompat v0.0.23 h1:YlOIUUJtd8JABkaF58R4DtUWJWul0C+QMYsrl7hAakw= +github.com/maximhq/bifrost/plugins/litellmcompat v0.0.23/go.mod h1:aSGR264jSQPJB6WnTSuP9a9W+2cPTPIVn5XFqc2ZZyI= +github.com/maximhq/bifrost/plugins/logging v1.4.34 h1:n1PpbClCmqV+tQ1qZCI/rmY/WSx/oFdVX8Gf/0uV50s= +github.com/maximhq/bifrost/plugins/logging v1.4.34/go.mod h1:tN0UjURQwYQKcAgzCbKjWA2Xamm5Peoj7b7grCi6Oco= +github.com/maximhq/bifrost/plugins/maxim v1.5.33 h1:2dPy5K1W5Q5hyfgwryvsL1ngVHeSDNQkK+q+mPaNJxE= +github.com/maximhq/bifrost/plugins/maxim v1.5.33/go.mod h1:IIEqWUGeiY5OWmAZush2dt4UfEDGch70fsH/3maD7wU= +github.com/maximhq/bifrost/plugins/mocker v1.4.33 h1:TErHmfS4a73GXf8GfIVkFlPteiRYfe7IFNumh8PH3Hk= +github.com/maximhq/bifrost/plugins/mocker v1.4.33/go.mod h1:z1hNuqqwCxshOFWfCbLaCn0tJG+GZdwYo/F+gfuYVis= +github.com/maximhq/bifrost/plugins/otel v1.1.33 h1:6YMkNW32t6sKM98tnXArxh7A3HXaqz78C8NNP+UiZCU= +github.com/maximhq/bifrost/plugins/otel v1.1.33/go.mod h1:1QPHFkGS31pB7tcT4lQV9SYXHF2LeoFj1qrVPRxrgFw= +github.com/maximhq/bifrost/plugins/semanticcache v1.4.32 h1:y21XFlL+B7uu4AbKIq6/spKhmTvzpm+ys/lx7qy1W/k= +github.com/maximhq/bifrost/plugins/semanticcache v1.4.32/go.mod h1:f6N4/kLd6Bn01HrvNt+RsGhCTKla2xxp2Vnch2k+lVs= +github.com/maximhq/bifrost/plugins/telemetry v1.4.34 h1:JMomnrnpvUDO3cQrz1GTw4t/P3bnk2zQX3ATBAG7xZE= +github.com/maximhq/bifrost/plugins/telemetry v1.4.34/go.mod h1:Aq2pGKvitjDczuFj3TpKTLjrVmjz24YFYqypQNOGLcU= github.com/maximhq/maxim-go v0.2.0 h1:3SNpna+Z9bDcUBqPLV/pfaZaxTEtsyix7Rn1KtwoEp4= github.com/maximhq/maxim-go v0.2.0/go.mod h1:RvESsFEUWSJmIypHtV+vHN2EWjp+HoqWGStEvB/cPBU= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= diff --git a/transports/version b/transports/version index 04e0d3f14f..4007e54579 100644 --- a/transports/version +++ b/transports/version @@ -1 +1 @@ -1.4.17 +1.4.18 \ No newline at end of file diff --git a/ui/app/_fallbacks/enterprise/components/api-keys/apiKeysIndexView.tsx b/ui/app/_fallbacks/enterprise/components/api-keys/apiKeysIndexView.tsx index 7929d9bea0..8687f4490a 100644 --- a/ui/app/_fallbacks/enterprise/components/api-keys/apiKeysIndexView.tsx +++ b/ui/app/_fallbacks/enterprise/components/api-keys/apiKeysIndexView.tsx @@ -3,10 +3,10 @@ import { Alert, AlertDescription } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import { useGetCoreConfigQuery } from "@/lib/store"; +import { useCopyToClipboard } from "@/hooks/useCopyToClipboard"; import { Copy, InfoIcon, KeyRound } from "lucide-react"; import Link from "next/link"; import { useMemo } from "react"; -import { toast } from "sonner"; import ContactUsView from "../views/contactUsView"; export default function APIKeysView() { @@ -31,10 +31,7 @@ curl --location 'http://localhost:8080/v1/chat/completions' ] }'`; - const copyToClipboard = (text: string) => { - navigator.clipboard.writeText(text); - toast.success("Copied to clipboard"); - }; + const { copy: copyToClipboard } = useCopyToClipboard(); if (isLoading) { return
Loading...
; diff --git a/ui/app/workspace/logs/sheets/logDetailsSheet.tsx b/ui/app/workspace/logs/sheets/logDetailsSheet.tsx index b698ef01a1..7bbda63cc1 100644 --- a/ui/app/workspace/logs/sheets/logDetailsSheet.tsx +++ b/ui/app/workspace/logs/sheets/logDetailsSheet.tsx @@ -1,5 +1,8 @@ "use client"; +import { useEffect, useState } from "react"; +import { useHotkeys } from "react-hotkeys-hook"; +import { useGetLogByIdQuery } from "@/lib/store/apis/logsApi"; import { AlertDialog, AlertDialogAction, @@ -13,14 +16,7 @@ import { } from "@/components/ui/alertDialog"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; -import { CodeEditor } from "@/components/ui/codeEditor"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "@/components/ui/dropdownMenu"; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdownMenu"; import { DottedSeparator } from "@/components/ui/separator"; import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet"; import { ProviderIconType, RenderProviderIcon, RoutingEngineUsedIcons } from "@/lib/constants/icons"; @@ -32,11 +28,10 @@ import { Status, StatusColors, } from "@/lib/constants/logs"; -import { useLazyGetLogByIdQuery } from "@/lib/store/apis/logsApi"; import { LogEntry } from "@/lib/types/logs"; -import { Clipboard, Loader2, MoreVertical, Trash2 } from "lucide-react"; +import { useCopyToClipboard } from "@/hooks/useCopyToClipboard"; +import { ChevronDown, ChevronUp, Clipboard, Loader2, MoreVertical, Trash2 } from "lucide-react"; import moment from "moment"; -import { useEffect } from "react"; import { toast } from "sonner"; import BlockHeader from "../views/blockHeader"; import CollapsibleBox from "../views/collapsibleBox"; @@ -44,10 +39,10 @@ import ImageView from "../views/imageView"; import LogChatMessageView from "../views/logChatMessageView"; import LogEntryDetailsView from "../views/logEntryDetailsView"; import LogResponsesMessageView from "../views/logResponsesMessageView"; -import PluginLogsView from "../views/pluginLogsView"; import SpeechView from "../views/speechView"; import TranscriptionView from "../views/transcriptionView"; import VideoView from "../views/videoView"; +import { CodeEditor } from "@/components/ui/codeEditor"; const formatJsonSafe = (str: string | undefined): string => { try { @@ -62,6 +57,9 @@ interface LogDetailSheetProps { open: boolean; onOpenChange: (open: boolean) => void; handleDelete: (log: LogEntry) => void; + onNavigate?: (direction: "prev" | "next") => void; + hasPrev?: boolean; + hasNext?: boolean; } // Helper to detect passthrough operations @@ -83,19 +81,42 @@ const isContainerOperation = (object: string) => { return containerTypes.includes(object?.toLowerCase()); }; -export function LogDetailSheet({ log, open, onOpenChange, handleDelete }: LogDetailSheetProps) { - const [fetchLog, { data: fullLog, isFetching }] = useLazyGetLogByIdQuery(); - +export function LogDetailSheet({ + log, + open, + onOpenChange, + handleDelete, + onNavigate, + hasPrev = false, + hasNext = false, +}: LogDetailSheetProps) { + const { copy: copyRequestId } = useCopyToClipboard({ successMessage: "Request ID copied" }); + const { copy: copyBody } = useCopyToClipboard({ + successMessage: "Request body copied to clipboard", + errorMessage: "Failed to copy request body", + }); + const [pollingInterval, setPollingInterval] = useState(0); + const { + data: fullLog, + isLoading, + isError, + } = useGetLogByIdQuery(log?.id ?? "", { + skip: !open || !log?.id, + pollingInterval, + }); + const shouldPoll = isError || fullLog?.status === "processing"; useEffect(() => { - if (open && log?.id) { - fetchLog(log.id); - } - }, [open, log?.id, fetchLog]); + setPollingInterval(shouldPoll ? 2000 : 0); + }, [shouldPoll]); + + // Keyboard navigation: arrow up/down to navigate between logs + useHotkeys("up", () => onNavigate?.("prev"), { enabled: open && hasPrev, preventDefault: true }); + useHotkeys("down", () => onNavigate?.("next"), { enabled: open && hasNext, preventDefault: true }); if (!log) return null; - // Show a loader until the full log data is fetched from the dedicated single-log endpoint. - const isFullDataReady = fullLog?.id === log.id && !isFetching; + // Show a loader only on the initial fetch, not during background polling refetches. + const isFullDataReady = fullLog?.id === log.id && !isLoading; const displayLog = isFullDataReady ? fullLog : log; const isContainer = isContainerOperation(displayLog.object); @@ -132,25 +153,18 @@ export function LogDetailSheet({ log, open, onOpenChange, handleDelete }: LogDet {!isFullDataReady ? (
+ Loading log details
) : ( <> - -
- + +
+ {displayLog.id && (

Request ID:{" "} - { - navigator.clipboard - .writeText(displayLog.id) - .then(() => toast.success("Request ID copied")) - .catch(() => toast.error("Failed to copy")); - }} - > + copyRequestId(displayLog.id)}> {displayLog.id}

@@ -173,19 +187,45 @@ export function LogDetailSheet({ log, open, onOpenChange, handleDelete }: LogDet )}
+
+ + +
- - copyRequestBody(displayLog)} data-testid="logdetails-copy-request-body-button"> + copyRequestBody(displayLog, copyBody)} + data-testid="logdetails-copy-request-body-button" + > Copy request body - @@ -592,7 +632,6 @@ export function LogDetailSheet({ log, open, onOpenChange, handleDelete }: LogDet
)} - {displayLog.plugin_logs && } {toolsParameter && ( toolsParameter}> )} - {(displayLog.image_generation_input || - displayLog.image_edit_input || - displayLog.image_variation_input || - displayLog.image_generation_output) && ( + {(displayLog.image_generation_input || displayLog.image_generation_output) && ( @@ -928,7 +962,7 @@ const normalizeObjectForCopy = (object: string | undefined): string => { return mapping[normalized] ?? normalized; }; -const copyRequestBody = async (log: LogEntry) => { +const copyRequestBody = async (log: LogEntry, copy: (text: string) => Promise) => { try { // Check if request is for responses, chat, speech, text completion, or embedding (exclude transcriptions) const object = normalizeObjectForCopy(log.object); @@ -1037,14 +1071,7 @@ const copyRequestBody = async (log: LogEntry) => { } const requestBodyJson = JSON.stringify(requestBody, null, 2); - navigator.clipboard - .writeText(requestBodyJson) - .then(() => { - toast.success("Request body copied to clipboard"); - }) - .catch((error) => { - toast.error("Failed to copy request body"); - }); + await copy(requestBodyJson); } catch (error) { toast.error("Failed to copy request body"); } diff --git a/ui/app/workspace/logs/views/collapsibleBox.tsx b/ui/app/workspace/logs/views/collapsibleBox.tsx index dc1a8b41c3..09d8a109ac 100644 --- a/ui/app/workspace/logs/views/collapsibleBox.tsx +++ b/ui/app/workspace/logs/views/collapsibleBox.tsx @@ -1,7 +1,7 @@ import { Button } from "@/components/ui/button"; +import { useCopyToClipboard } from "@/hooks/useCopyToClipboard"; import { ChevronDown, ChevronUp, Copy } from "lucide-react"; import { useEffect, useRef, useState } from "react"; -import { toast } from "sonner"; interface CollapsibleBoxProps { title: string; @@ -15,6 +15,7 @@ export default function CollapsibleBox({ title, children, collapsedHeight = 60, const [isExpanded, setIsExpanded] = useState(false); const [needsExpansion, setNeedsExpansion] = useState(false); const innerContentRef = useRef(null); + const { copy } = useCopyToClipboard(); useEffect(() => { if (!innerContentRef.current) return; @@ -39,15 +40,7 @@ export default function CollapsibleBox({ title, children, collapsedHeight = 60, const handleCopy = () => { if (!onCopy) return; - - navigator.clipboard - .writeText(onCopy()) - .then(() => { - toast.success("Copied to clipboard"); - }) - .catch(() => { - toast.error("Failed to copy"); - }); + copy(onCopy()); }; return ( diff --git a/ui/app/workspace/logs/views/emptyState.tsx b/ui/app/workspace/logs/views/emptyState.tsx index 5ecb632f92..c4027ef4bc 100644 --- a/ui/app/workspace/logs/views/emptyState.tsx +++ b/ui/app/workspace/logs/views/emptyState.tsx @@ -6,9 +6,9 @@ import { CodeEditor } from "@/components/ui/codeEditor"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { getExampleBaseUrl } from "@/lib/utils/port"; +import { useCopyToClipboard } from "@/hooks/useCopyToClipboard"; import { AlertTriangle, Copy } from "lucide-react"; import { useMemo, useState } from "react"; -import { toast } from "sonner"; type Provider = "openai" | "anthropic" | "genai" | "litellm" | "langchain"; type Language = "python" | "typescript"; @@ -42,10 +42,7 @@ interface CodeBlockProps { } function CodeBlock({ code, language, onLanguageChange, showLanguageSelect = false, readonly = true }: CodeBlockProps) { - const copyToClipboard = () => { - navigator.clipboard.writeText(code); - toast.success("Copied to clipboard"); - }; + const { copy: copyToClipboard } = useCopyToClipboard(); return (
@@ -65,7 +62,7 @@ function CodeBlock({ code, language, onLanguageChange, showLanguageSelect = fals )} -
diff --git a/ui/app/workspace/mcp-logs/views/emptyState.tsx b/ui/app/workspace/mcp-logs/views/emptyState.tsx index 45412fb4dd..a0250ad603 100644 --- a/ui/app/workspace/mcp-logs/views/emptyState.tsx +++ b/ui/app/workspace/mcp-logs/views/emptyState.tsx @@ -6,9 +6,9 @@ import { CodeEditor } from "@/components/ui/codeEditor"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { getExampleBaseUrl } from "@/lib/utils/port"; +import { useCopyToClipboard } from "@/hooks/useCopyToClipboard"; import { AlertTriangle, Copy } from "lucide-react"; import { useMemo, useState } from "react"; -import { toast } from "sonner"; type Language = "python" | "typescript"; @@ -41,10 +41,7 @@ interface CodeBlockProps { } function CodeBlock({ code, language, onLanguageChange, showLanguageSelect = false, readonly = true }: CodeBlockProps) { - const copyToClipboard = () => { - navigator.clipboard.writeText(code); - toast.success("Copied to clipboard"); - }; + const { copy: copyToClipboard } = useCopyToClipboard(); return (
@@ -64,7 +61,7 @@ function CodeBlock({ code, language, onLanguageChange, showLanguageSelect = fals )} -
diff --git a/ui/app/workspace/mcp-logs/views/mcpLogDetailsSheet.tsx b/ui/app/workspace/mcp-logs/views/mcpLogDetailsSheet.tsx index cc43f36e85..37bb027691 100644 --- a/ui/app/workspace/mcp-logs/views/mcpLogDetailsSheet.tsx +++ b/ui/app/workspace/mcp-logs/views/mcpLogDetailsSheet.tsx @@ -83,17 +83,17 @@ export function MCPLogDetailSheet({ log, open, onOpenChange, handleDelete, onNav
- -
- diff --git a/ui/app/workspace/observability/fragments/prometheusFormFragment.tsx b/ui/app/workspace/observability/fragments/prometheusFormFragment.tsx index e5e36aedf2..1492536bd2 100644 --- a/ui/app/workspace/observability/fragments/prometheusFormFragment.tsx +++ b/ui/app/workspace/observability/fragments/prometheusFormFragment.tsx @@ -10,6 +10,7 @@ import { prometheusFormSchema, type PrometheusFormSchema } from "@/lib/types/sch import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib"; import { zodResolver } from "@hookform/resolvers/zod"; import { Switch } from "@/components/ui/switch"; +import { useCopyToClipboard } from "@/hooks/useCopyToClipboard"; import { AlertTriangle, Copy, Eye, EyeOff, Info, Plus, Trash, Trash2 } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm, type Resolver } from "react-hook-form"; @@ -44,7 +45,7 @@ export function PrometheusFormFragment({ const hasPrometheusAccess = useRbac(RbacResource.Observability, RbacOperation.Update); const [showPassword, setShowPassword] = useState(false); const [isSaving, setIsSaving] = useState(false); - const [copied, setCopied] = useState(false); + const { copy, copied } = useCopyToClipboard(); const [showBasicAuth, setShowBasicAuth] = useState(!!(initialConfig?.basic_auth?.username || initialConfig?.basic_auth?.password)); const form = useForm({ @@ -86,9 +87,7 @@ export function PrometheusFormFragment({ const handleCopyEndpoint = () => { if (metricsEndpoint) { - navigator.clipboard.writeText(metricsEndpoint); - setCopied(true); - setTimeout(() => setCopied(false), 2000); + copy(metricsEndpoint); } }; diff --git a/ui/app/workspace/plugins/sheets/pluginSequenceSheet.tsx b/ui/app/workspace/plugins/sheets/pluginSequenceSheet.tsx index 900c8128f4..eb87819741 100644 --- a/ui/app/workspace/plugins/sheets/pluginSequenceSheet.tsx +++ b/ui/app/workspace/plugins/sheets/pluginSequenceSheet.tsx @@ -177,7 +177,7 @@ export default function PluginSequenceSheet({ open, onClose, plugins }: PluginSe - diff --git a/ui/app/workspace/routing-rules/components/celBuilder/celRuleBuilder.tsx b/ui/app/workspace/routing-rules/components/celBuilder/celRuleBuilder.tsx index 801e544e89..2ce19b2df5 100644 --- a/ui/app/workspace/routing-rules/components/celBuilder/celRuleBuilder.tsx +++ b/ui/app/workspace/routing-rules/components/celBuilder/celRuleBuilder.tsx @@ -11,6 +11,7 @@ import { Textarea } from "@/components/ui/textarea"; import { getRoutingFields } from "@/lib/config/celFieldsRouting"; import { celOperatorsRouting } from "@/lib/config/celOperatorsRouting"; import { convertRuleGroupToCEL } from "@/lib/utils/celConverterRouting"; +import { useCopyToClipboard } from "@/hooks/useCopyToClipboard"; import { Check, Copy, Loader2 } from "lucide-react"; import { useEffect, useMemo, useRef, useState } from "react"; import { Field, QueryBuilder, RuleGroupType } from "react-querybuilder"; @@ -46,7 +47,7 @@ export function CELRuleBuilder({ }: CELRuleBuilderProps) { const [query, setQuery] = useState(initialQuery || defaultQuery); const [celExpression, setCelExpression] = useState(""); - const [copied, setCopied] = useState(false); + const { copy, copied } = useCopyToClipboard(); const onChangeRef = useRef(onChange); // Keep ref updated so the query effect always invokes the latest callback @@ -69,11 +70,7 @@ export function CELRuleBuilder({ onChangeRef.current?.(expression, query); }, [query]); - const handleCopy = async () => { - await navigator.clipboard.writeText(celExpression); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; + const handleCopy = () => copy(celExpression); // Show loading state if (isLoading) { @@ -122,7 +119,7 @@ export function CELRuleBuilder({
-
-
{ if (!disabled && !editMode) setEditMode(true); }} className={!disabled && !editMode ? "cursor-text" : ""}> +
{ if (!disabled && !editMode && !(e.target as HTMLElement).closest("button, a, [role='button']")) setEditMode(true); }} className={!disabled && !editMode ? "cursor-text" : ""}> {editMode ? ( { - if (!disabled) setEditMode(true); + onClick={(e) => { + if (disabled || editMode) return; + if ((e.target as HTMLElement).closest("button, a, [role='button']")) return; + setEditMode(true); }} > diff --git a/ui/components/prompts/components/messagesView/userMessageView.tsx b/ui/components/prompts/components/messagesView/userMessageView.tsx index fc8da8965f..1513d9b6f1 100644 --- a/ui/components/prompts/components/messagesView/userMessageView.tsx +++ b/ui/components/prompts/components/messagesView/userMessageView.tsx @@ -286,8 +286,10 @@ export function UserMessageView({ ) : (
{ - if (!disabled) setEditMode(true); + onClick={(e) => { + if (disabled || editMode) return; + if ((e.target as HTMLElement).closest("button, a, [role='button']")) return; + setEditMode(true); }} > diff --git a/ui/components/ui/input.tsx b/ui/components/ui/input.tsx index 40d5b0946a..37cba85dd9 100644 --- a/ui/components/ui/input.tsx +++ b/ui/components/ui/input.tsx @@ -2,9 +2,9 @@ import * as React from "react"; +import { useCopyToClipboard } from "@/hooks/useCopyToClipboard"; import { cn } from "@/lib/utils"; import { CopyIcon } from "lucide-react"; -import { toast } from "sonner"; import { Button } from "./button"; export interface InputProps extends React.InputHTMLAttributes { @@ -14,6 +14,8 @@ export interface InputProps extends React.InputHTMLAttributes export const Input = React.forwardRef( ({ className, type, showCopyButton = false, inputClassName, ...props }, ref) => { + const { copy } = useCopyToClipboard(); + if (showCopyButton) { return (
( variant="ghost" size="icon" onClick={() => { - if (typeof props.value === "string") { - navigator.clipboard.writeText(props.value as string); - } else { - navigator.clipboard.writeText(JSON.stringify(props.value)); - } - toast.success("Copied to clipboard"); + const text = typeof props.value === "string" ? props.value : JSON.stringify(props.value); + copy(text); }} > diff --git a/ui/hooks/useCopyToClipboard.ts b/ui/hooks/useCopyToClipboard.ts new file mode 100644 index 0000000000..ee22532f3f --- /dev/null +++ b/ui/hooks/useCopyToClipboard.ts @@ -0,0 +1,34 @@ +"use client"; + +import { useCallback, useRef, useState } from "react"; +import { toast } from "sonner"; + +interface UseCopyToClipboardOptions { + successMessage?: string; + errorMessage?: string; + resetDelay?: number; +} + +export function useCopyToClipboard(options: UseCopyToClipboardOptions = {}) { + const { successMessage = "Copied to clipboard", errorMessage = "Failed to copy", resetDelay = 2000 } = options; + const [copied, setCopied] = useState(false); + const timeoutRef = useRef>(undefined); + + const copy = useCallback( + async (text: string) => { + try { + await navigator.clipboard.writeText(text); + setCopied(true); + toast.success(successMessage); + + if (timeoutRef.current) clearTimeout(timeoutRef.current); + timeoutRef.current = setTimeout(() => setCopied(false), resetDelay); + } catch { + toast.error(errorMessage); + } + }, + [successMessage, errorMessage, resetDelay], + ); + + return { copy, copied }; +} diff --git a/ui/lib/store/apis/logsApi.ts b/ui/lib/store/apis/logsApi.ts index aed46a32f3..64343555b3 100644 --- a/ui/lib/store/apis/logsApi.ts +++ b/ui/lib/store/apis/logsApi.ts @@ -394,4 +394,5 @@ export const { useDeleteLogsMutation, useRecalculateLogCostsMutation, useLazyGetLogByIdQuery, + useGetLogByIdQuery, } = logsApi;