Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 9 additions & 24 deletions tee_gateway/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,11 @@

from .util import dynamic_session_cost_calculator
from .definitions import (
EVM_NETWORK,
BASE_TESTNET_NETWORK,
EVM_PAYMENT_ADDRESS,
USDC_ADDRESS,
BASE_OPG_ADDRESS,
CHAT_COMPLETIONS_USDC_AMOUNT,
CHAT_COMPLETIONS_OPG_SESSION_MAX_SPEND,
COMPLETIONS_USDC_AMOUNT,
COMPLETIONS_OPG_SESSION_MAX_SPEND,
FACILITATOR_URL,
)

Expand Down Expand Up @@ -111,28 +108,12 @@ def _shutdown_heartbeat():
server = x402ResourceServerSync(facilitator)
store = SessionStore()

server.register(EVM_NETWORK, ExactEvmServerScheme())
server.register(BASE_TESTNET_NETWORK, ExactEvmServerScheme())
server.register(EVM_NETWORK, UptoEvmServerScheme())
server.register(BASE_TESTNET_NETWORK, UptoEvmServerScheme())

routes = {
"POST /v1/chat/completions": RouteConfig(
accepts=[
PaymentOption(
scheme="upto",
pay_to=EVM_PAYMENT_ADDRESS,
price=AssetAmount(
amount=CHAT_COMPLETIONS_USDC_AMOUNT,
asset=USDC_ADDRESS,
extra={
"name": "OUSDC",
"version": "2",
"assetTransferMethod": "permit2",
},
),
network=EVM_NETWORK,
),
PaymentOption(
scheme="upto",
pay_to=EVM_PAYMENT_ADDRESS,
Expand All @@ -157,11 +138,15 @@ def _shutdown_heartbeat():
scheme="upto",
pay_to=EVM_PAYMENT_ADDRESS,
price=AssetAmount(
amount=COMPLETIONS_USDC_AMOUNT,
asset=USDC_ADDRESS,
extra={"name": "USDC", "version": "2"},
amount=COMPLETIONS_OPG_SESSION_MAX_SPEND,
asset=BASE_OPG_ADDRESS,
extra={
"name": "OPG",
"version": "2",
"assetTransferMethod": "permit2",
},
),
network=EVM_NETWORK,
network=BASE_TESTNET_NETWORK,
),
],
mime_type="application/json",
Expand Down
18 changes: 6 additions & 12 deletions tee_gateway/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@
# Network IDs (EIP-155 chain identifiers)
# ---------------------------------------------------------------------------

# OG EVM — where USDC payments are accepted
EVM_NETWORK: str = "eip155:10740"

# Base Testnet — where OPG payments are accepted
BASE_TESTNET_NETWORK: str = "eip155:84532"

Expand All @@ -41,9 +38,6 @@
# ERC-20 token contract addresses
# ---------------------------------------------------------------------------

# USDC Address
USDC_ADDRESS: str = "0x094E464A23B90A71a0894D5D1e5D470FfDD074e1"

# OpenGradient token (OPG) on Base Testnet
BASE_OPG_ADDRESS: str = "0x240b09731D96979f50B2C649C9CE10FcF9C7987F"

Expand All @@ -53,7 +47,6 @@

# Maps lowercase contract address → number of decimals for unit conversion.
ASSET_DECIMALS_BY_ADDRESS: dict[str, int] = {
USDC_ADDRESS.lower(): 6, # USDC / OUSDC standard: 6 decimals
BASE_OPG_ADDRESS.lower(): 18, # OPG: 18 decimals (ERC-20 standard)
}

Expand All @@ -68,15 +61,16 @@
# by dynamic_session_cost_calculator() in util.py.
# ---------------------------------------------------------------------------

# /v1/chat/completions — 0.01 OUSDC precheck (6 decimals: 10_000 = $0.01)
CHAT_COMPLETIONS_USDC_AMOUNT: str = "10000"

# /v1/chat/completions — maximum OPG spend per session (18 decimals: 100000000000000000 = 0.1 OPG).
# This is the upper-bound amount presented to the client during the x402 pre-check handshake.
# The x402 "upto" scheme allows the actual charge to be any value up to this cap;
# the real per-request cost is settled dynamically by dynamic_session_cost_calculator() in util.py
# based on actual token usage, so clients are never overcharged beyond what they consumed.
CHAT_COMPLETIONS_OPG_SESSION_MAX_SPEND: str = "100000000000000000"

# /v1/completions — 0.01 USDC precheck (6 decimals: 10_000 = $0.01)
COMPLETIONS_USDC_AMOUNT: str = "10000"
# /v1/completions — maximum OPG spend per session (18 decimals: 100000000000000000 = 0.1 OPG).
# This is the upper-bound amount presented to the client during the x402 pre-check handshake.
# The x402 "upto" scheme allows the actual charge to be any value up to this cap;
# the real per-request cost is settled dynamically by dynamic_session_cost_calculator() in util.py
# based on actual token usage, so clients are never overcharged beyond what they consumed.
COMPLETIONS_OPG_SESSION_MAX_SPEND: str = "100000000000000000"
53 changes: 3 additions & 50 deletions tests/test_pricing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@

Tests verify that:
- Every user-facing model name resolves to the correct ModelConfig
- dynamic_session_cost_calculator produces the right amount in token
smallest-units for supported models and token currencies (OPG / USDC)
- dynamic_session_cost_calculator produces the right amount in OPG token
smallest-units for supported models
- Edge cases (no usage, unknown model, bad context) are handled correctly
"""

import unittest
from decimal import Decimal

from tee_gateway.definitions import BASE_OPG_ADDRESS, USDC_ADDRESS
from tee_gateway.definitions import BASE_OPG_ADDRESS
from tee_gateway.model_registry import (
_MODEL_LOOKUP,
get_model_config,
Expand All @@ -29,11 +29,6 @@ def _opg_requirements() -> dict:
return {"asset": BASE_OPG_ADDRESS, "amount": "50000000000000000"}


def _usdc_requirements() -> dict:
"""Fake PaymentRequirements dict for USDC (6 decimals)."""
return {"asset": USDC_ADDRESS, "amount": "10000"}


def _ctx(model: str, input_tokens: int, output_tokens: int, requirements=None) -> dict:
"""Build a minimal calculator context."""
return {
Expand Down Expand Up @@ -62,18 +57,6 @@ def _expected_cost_opg(model: str, input_tokens: int, output_tokens: int) -> int
return int((total_usd * Decimal(10**18)).to_integral_value(rounding=ROUND_CEILING))


def _expected_cost_usdc(model: str, input_tokens: int, output_tokens: int) -> int:
"""Compute expected cost in USDC smallest units (6 decimals, ROUND_CEILING)."""
from decimal import ROUND_CEILING

cfg = get_model_config(model)
total_usd = (
Decimal(input_tokens) * cfg.input_price_usd
+ Decimal(output_tokens) * cfg.output_price_usd
)
return int((total_usd * Decimal(10**6)).to_integral_value(rounding=ROUND_CEILING))


# ---------------------------------------------------------------------------
# Model registry tests
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -368,36 +351,6 @@ def test_grok_4_fast_cheaper_than_grok_4(self):
self.assertLess(fast, full)


class TestDynamicSessionCostCalculatorUSDC(unittest.TestCase):
"""dynamic_session_cost_calculator with USDC (6 decimals)."""

def _calc(self, model, input_tokens, output_tokens):
return dynamic_session_cost_calculator(
_ctx(model, input_tokens, output_tokens, _usdc_requirements())
)

def test_gpt_4_1_usdc_cost(self):
cost = self._calc("gpt-4.1", 1000, 500)
expected = _expected_cost_usdc("gpt-4.1", 1000, 500)
self.assertEqual(cost, expected)
# 0.006 USD in USDC (6 decimals) = 6000 units
self.assertEqual(cost, 6000)

def test_claude_sonnet_4_5_usdc_cost(self):
cost = self._calc("claude-sonnet-4-5", 1000, 500)
expected = _expected_cost_usdc("claude-sonnet-4-5", 1000, 500)
self.assertEqual(cost, expected)
# 0.0105 USD = 10500 units
self.assertEqual(cost, 10500)

def test_gemini_flash_lite_usdc_cost(self):
cost = self._calc("gemini-2.5-flash-lite", 1000, 500)
expected = _expected_cost_usdc("gemini-2.5-flash-lite", 1000, 500)
self.assertEqual(cost, expected)
# 0.0003 USD = 300 units
self.assertEqual(cost, 300)


class TestDynamicSessionCostCalculatorEdgeCases(unittest.TestCase):
"""Edge cases for dynamic_session_cost_calculator."""

Expand Down
Loading