Skip to content

fix(billing): Skip billing on streamed workflows with byok#4056

Merged
TheodoreSpeaks merged 10 commits intostagingfrom
debug/byok-agent-block
Apr 8, 2026
Merged

fix(billing): Skip billing on streamed workflows with byok#4056
TheodoreSpeaks merged 10 commits intostagingfrom
debug/byok-agent-block

Conversation

@TheodoreSpeaks
Copy link
Copy Markdown
Collaborator

@TheodoreSpeaks TheodoreSpeaks commented Apr 8, 2026

Summary

BYOK users were incorrectly billed when using the streamed response. This was because the byok check is skipped for streamed responses because they're streamed live to the user. Added helper method zeroCostForBYOK to zero out cost from the execution stream, while keeping tool costs.

Also added tool cost to the execution log cost summary so that the total lines up.

Also upgraded logging byok so we can see the logic in prod.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: ___________

Testing

  • Replicated bug locally, validated that model costs are 0 after change was applied.
  • Validated non-byok users are billed both for streaming and non-streaming

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Apr 8, 2026 11:08pm

Request Review

@TheodoreSpeaks TheodoreSpeaks marked this pull request as ready for review April 8, 2026 19:59
@cursor
Copy link
Copy Markdown

cursor bot commented Apr 8, 2026

PR Summary

Medium Risk
Touches provider execution/billing paths for streaming responses by intercepting and freezing execution.output.cost, which could affect downstream cost reporting if assumptions about mutability differ. UI and tokenization changes are low risk but depend on the new cost.pricing sentinel behavior.

Overview
Fixes incorrect billing for BYOK users on streaming executions by intercepting StreamingExecution responses and forcing model cost to remain zero via zeroCostForBYOK, while still allowing tool costs to accumulate.

Updates streaming tokenization to not recalculate/overwrite cost when the billing layer has already set output.cost.pricing (used as a sentinel for explicit billing decisions), and adjusts log details UI to display aggregated tool usage cost and clarify that totals include model + tool usage.

Reviewed by Cursor Bugbot for commit 3792e01. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 8, 2026

Greptile Summary

This PR fixes a billing bug where BYOK (Bring Your Own Key) users were incorrectly charged when using streamed workflow responses. It introduces zeroCostForBYOK, which intercepts cost writes on the streaming output object via Object.defineProperty, and also upgrades several logger.debug calls to logger.info for better production visibility.

Confidence Score: 3/5

The fix addresses the core billing bug for streaming BYOK users, but two reliability gaps could allow incorrect billing to slip through in edge cases.

The ZERO_COST constant uses updatedAt: '' inconsistently with the synchronous path, and the zeroCostForBYOK guard silently exits without logging when output is absent, making it impossible to detect provider-specific gaps in production. These are correctness risks in the billing path that warrant attention before merging.

apps/sim/providers/index.ts — specifically ZERO_COST.pricing.updatedAt and the early-return path in zeroCostForBYOK

Vulnerabilities

No security concerns identified. The BYOK billing fix correctly prevents cost attribution for user-supplied keys. The new logger.info call logging workspaceId is standard practice for backend service tracing and does not expose user-facing secrets or credentials.

Important Files Changed

Filename Overview
apps/sim/providers/index.ts Adds zeroCostForBYOK to intercept streaming cost writes for BYOK users; has a minor updatedAt: '' inconsistency vs the synchronous zero-cost path, and a potential gap if providers replace output entirely during streaming rather than mutating it in-place.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[executeProviderRequest] --> B{workspaceId present?}
    B -->|Yes| C[getApiKeyWithBYOK]
    C --> D[isBYOK set]
    B -->|No| E[isBYOK = false]
    D --> F[provider.executeRequest]
    E --> F
    F --> G{Response type?}
    G -->|StreamingExecution| H{isBYOK?}
    H -->|Yes| I[zeroCostForBYOK\nObject.defineProperty cost trap]
    I --> J[Return StreamingExecution]
    H -->|No| J
    G -->|ReadableStream| K[Return stream\n⚠️ No BYOK check]
    G -->|ProviderResponse| L{shouldBillModelUsage AND not isBYOK?}
    L -->|Yes| M[calculateCost → response.cost]
    L -->|No| N[Zero cost → response.cost]
    M --> O[Return response]
    N --> O
Loading

Reviews (1): Last reviewed commit: "Simplify logic" | Re-trigger Greptile

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 762bc43. Configure here.

TheodoreSpeaks and others added 6 commits April 8, 2026 16:04
* fix(webhook): throw webhook errors as 4xxs

* Fix shadowing body var

---------

Co-authored-by: Theodore Li <theo@sim.ai>
* feat(enterprise): cloud whitelabeling for enterprise orgs

* fix(enterprise): scope enterprise plan check to target org in whitelabel PUT

* fix(enterprise): use isOrganizationOnEnterprisePlan for org-scoped enterprise check

* fix(enterprise): allow clearing whitelabel fields and guard against empty update result

* fix(enterprise): remove webp from logo accept attribute to match upload hook validation

* improvement(billing): use isBillingEnabled instead of isProd for plan gate bypasses

* fix(enterprise): show whitelabeling nav item when billing is enabled on non-hosted environments

* fix(enterprise): accept relative paths for logoUrl since upload API returns /api/files/serve/ paths

* fix(whitelabeling): prevent logo flash on refresh by hiding logo while branding loads

* fix(whitelabeling): wire hover color through CSS token on tertiary buttons

* fix(whitelabeling): show sim logo by default, only replace when org logo loads

* fix(whitelabeling): cache org logo url in localstorage to eliminate flash on repeat visits

* feat(whitelabeling): add wordmark support with drag/drop upload

* updated turbo

* fix(whitelabeling): defer localstorage read to effect to prevent hydration mismatch

* fix(whitelabeling): use layout effect for cache read to eliminate logo flash before paint

* fix(whitelabeling): cache theme css to eliminate color flash before org settings resolve

* fix(whitelabeling): deduplicate HEX_COLOR_REGEX into lib/branding and remove mutation from useCallback deps

* fix(whitelabeling): use cookie-based SSR cache to eliminate brand flash on all page loads

* fix(whitelabeling): use !orgSettings condition to fix SSR brand cache injection

React Query returns isLoading: false with data: undefined during SSR, so the
previous brandingLoading condition was always false on the server — initialCache
was never injected into brandConfig. Changing to !orgSettings correctly applies
the cookie cache both during SSR and while the client-side query loads, eliminating
the logo flash on hard refresh.
…ntext (#4055)

Auto-layout was reading from getWorkflowState() without merging subblock
store values, then persisting stale subblock data to the database. This
caused runtime-edited values (e.g. router_v2 context) to be overwritten
with their initial/empty values whenever auto-layout was triggered.
…ver-side (#4057)

* fix(whitelabeling): eliminate logo flash by fetching org settings server-side

* improvement(whitelabeling): add SVG support for logo and wordmark uploads

* skelly in workspace header

* remove dead code

* fix(whitelabeling): hydration error, SVG support, skeleton shimmer, dead code removal

* fix(whitelabeling): blob preview dep cycle and missing color fallback

* fix(whitelabeling): use brand-accent as color fallback when workspace color is undefined

* chore(whitelabeling): inline hasOrgBrand
@TheodoreSpeaks TheodoreSpeaks merged commit 5f33432 into staging Apr 8, 2026
12 checks passed
@TheodoreSpeaks TheodoreSpeaks deleted the debug/byok-agent-block branch April 8, 2026 23:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants