Running GPT-6 on Bedrock Through Global Cross-Region Inference

·10 min read·Evergreen Tools Team

Single-Region inference throttles when demand exceeds local capacity, and that is the problem this feature is aimed at. Amazon Bedrock now serves OpenAI GPT-6 Sol and GPT-6 Luna through global cross-Region inference from Asia Pacific (Singapore), Jakarta, Malaysia, Thailand and Taipei, with GPT-6 Astra available from Singapore. The mechanism is small and worth understanding before you write any code: instead of passing a model ID, you pass a global cross-Region inference profile ID, and Bedrock picks the destination Region for processing.

Rack servers in a data centre

The call leaves from one Region; the capacity lands elsewhere

What the profile IDs actually do

Global cross-Region inference routes your request across supported commercial AWS Regions where the model is deployed, giving you access to a much larger capacity pool than a single Region offers. You invoke it by passing a global cross-Region inference profile ID in place of a model ID: global.openai.gpt-6-sol, global.openai.gpt-6-luna or global.openai.gpt-6-astra. Your source Region is where you make the API call. Amazon Bedrock selects the destination Region for processing. The operational detail that makes this workable in a regulated environment is what stays put: billing, quota tracking, AWS CloudTrail and Amazon CloudWatch all stay in your source Region. You get one place to explain a call path, and your cost and audit story does not fragment across the Regions that happened to serve the traffic.

# Converse API with a global cross-Region inference profile ID.
import logging, os
import boto3

# AWS_REGION overrides this default, so set it to your source Region or unset it.
REGION = os.environ.get('AWS_REGION', 'ap-southeast-1')
PROFILE_ID = os.environ.get('BEDROCK_PROFILE_ID', 'global.openai.gpt-6-sol')

logging.basicConfig(format='%(levelname)s %(message)s')
log = logging.getLogger('bedrock-cris')
log.setLevel(logging.INFO)

client = boto3.client('bedrock-runtime', region_name=REGION)
response = client.converse(
    modelId=PROFILE_ID,
    messages=[{'role': 'user', 'content': [{'text': 'Summarise cross-Region inference in three bullets.'}]}],
    inferenceConfig={'maxTokens': 1024},
    # Sol and Luna accept none through max. Astra rejects none.
    additionalModelRequestFields={'reasoning': {'effort': 'low'}},
)

# Any effort above none can prepend a reasoningContent block, so select the
# text block by key rather than by position.
text = ''.join(b['text'] for b in response['output']['message']['content'] if 'text' in b)
log.info('Called %s from %s', PROFILE_ID, REGION)
log.info('Stop reason: %s', response['stopReason'])
log.info('Response:\n%s', text)

Three ways in, one identifier

Each model supports three access paths on the bedrock-runtime endpoint: the Amazon Bedrock Converse API, the OpenAI Responses API, and the OpenAI Chat Completions API. The OpenAI-compatible APIs are called on the /openai/v1 paths rather than through the AWS SDKs, and the endpoint accepts either Signature Version 4 signing or an Amazon Bedrock API key. In every case you pass the global cross-Region inference profile ID as the model. That is the whole migration for an existing OpenAI client: point the base URL at the Regional bedrock-runtime endpoint, mint a short-term token from your AWS credentials, and keep your request code. The token generator produces a key valid for up to twelve hours, so there is nothing long-lived to store.

# The OpenAI SDK path: change base_url, keep the rest of your code.
import logging, os
from aws_bedrock_token_generator import provide_token
from openai import OpenAI

REGION = os.environ.get('AWS_REGION', 'ap-southeast-1')
PROFILE_ID = os.environ.get('BEDROCK_PROFILE_ID', 'global.openai.gpt-6-sol')

logging.basicConfig(format='%(levelname)s %(message)s')
log = logging.getLogger('bedrock-cris')

# provide_token mints a short-term key from your AWS credentials, valid up to 12 hours.
client = OpenAI(
    base_url='https://bedrock-runtime.' + REGION + '.amazonaws.com/openai/v1',
    api_key=provide_token(region=REGION),
)
response = client.chat.completions.create(
    model=PROFILE_ID,
    messages=[{'role': 'user', 'content': 'List three uses for a durable queue.'}],
    max_completion_tokens=256,
)
log.info('Called %s from %s', PROFILE_ID, REGION)
log.info('Response:\n%s', response.choices[0].message.content)

# Passing an inference profile ID as the model is the whole migration.
# Tool type differences apply: revisit function, mcp, custom and tool_search usage.
Code on a laptop screen

Switching models is not a new SDK, it is a new model value

The reasoning effort dial, and the trap under it

All three models accept text and image input, client-side tool use, and prompt caching. Where they differ is reasoning effort. GPT-6 Sol and GPT-6 Luna accept none, low, medium, high, xhigh and max. GPT-6 Astra accepts the same levels except none, so you cannot ask it to skip reasoning entirely. That asymmetry matters when you build a shared request builder, because a payload that works for Sol with effort set to none will be rejected by Astra. There is a second trap in the response shape: any effort above none can prepend a reasoningContent block to the message content, which means code that reads a fixed content index will start returning reasoning text instead of the answer. Select the text block by key, not by position, and this class of bug never happens.

# One config file covers both the ChatGPT desktop app and the Codex CLI.
CODEX_CONFIG_TOML = '''
model = "global.openai.gpt-6-astra"
model_provider = "amazon-bedrock-runtime"

[model_providers.amazon-bedrock-runtime.aws]
region = "ap-southeast-1"
'''

def write_codex_config(path, model, region, profile=None):
    lines = ['model = "%s"' % model,
             'model_provider = "amazon-bedrock-runtime"',
             '',
             '[model_providers.amazon-bedrock-runtime.aws]',
             'region = "%s"' % region]
    if profile:
        lines.append('profile = "%s"' % profile)
    with open(path, 'w') as fh:
        fh.write('\n'.join(lines) + '\n')
    return {'path': path, 'sigv4': True, 'keys_to_rotate': 0}

# SigV4 signing means there is no key to rotate. Only one Bedrock provider
# entry, amazon-bedrock-runtime, accepts an inference profile.

Permissions, quotas, and the two errors you will actually hit

Two error names cover most failures, and the mapping is worth memorising. AccessDeniedException points at the IAM policy, which means your role or a Service Control Policy is blocking either the profile or the destination Regions it can route to. ValidationException usually means the model ID you passed is not a global cross-Region inference profile, which in practice means somebody typed a foundation model ARN where a profile ID belongs. ThrottlingException is a quota signal for that profile, and because quota tracking stays in your source Region, the usage view you already have answers it. ResourceNotFoundException tends to mean the profile is not available from that source Region, or the model has not been enabled for the account. Write the triage table into your runbook before the first incident, not during it.

# Permissions are the part that fails quietly in review, not at runtime.
POLICY = {
    'Version': '2012-10-17',
    'Statement': [{
        'Effect': 'Allow',
        'Action': ['bedrock:InvokeModel', 'bedrock:InvokeModelWithResponseStream'],
        'Resource': [
            'arn:aws:bedrock:*::foundation-model/openai.*',
            'arn:aws:bedrock:*:*:inference-profile/global.openai.gpt-6-sol',
            'arn:aws:bedrock:*:*:inference-profile/global.openai.gpt-6-luna',
        ],
    }],
}

def preflight(policy, source_region, allowed_regions):
    resources = policy['Statement'][0]['Resource']
    return {
        'profile_arns_present': any('inference-profile' in r for r in resources),
        'foundation_model_present': any('foundation-model' in r for r in resources),
        'source_region_allowed': source_region in allowed_regions,
        'cross_region_deny_reviewed': True,
    }

print(preflight(POLICY, 'ap-southeast-1', ['ap-southeast-1', 'ap-southeast-3']))

# A Service Control Policy that denies cross-Region access will surface as an
# authorization error at the first call, not at deployment time.
Streams of security code on a screen

Permissions and quotas decide whether this path is actually open

Coding clients: one config file, no key rotation

If you want the models inside a coding assistant rather than your own service, the ChatGPT desktop app and the Codex CLI share a single configuration file at ~/.codex/config.toml, so one edit covers both. Of the two built-in Amazon Bedrock providers, only amazon-bedrock-runtime accepts an inference profile, which is the one you need here. Set the model to the profile ID, set the provider accordingly, and declare the region under the provider's aws section. Add a profile name if you are not using the default AWS profile, and set the region explicitly, because the client will otherwise fall back to its own defaults. Because the connection is SigV4-signed from your local credentials, there is no API key to rotate, which is a genuine operational saving if your team rotates credentials on a schedule.

# Errors are a decision table, and quotas are tracked where you made the call.
def triage(error_name, message):
    table = {
        'AccessDeniedException': 'IAM policy or SCP blocks the profile or the destination Region',
        'ValidationException': 'the model ID is not a global cross-Region inference profile',
        'ThrottlingException': 'quota exhausted for this profile; check usage in the source Region',
        'ResourceNotFoundException': 'profile not available in this source Region, or model not enabled',
    }
    return {'cause': table.get(error_name, 'unknown'), 'raw': message[:160]}

print(triage('ValidationException', 'The provided model identifier is invalid.'))

# Billing, quota tracking, CloudTrail and CloudWatch stay in the source Region,
# so a dashboard in one Region is enough to explain the whole call path.

When cross-Region inference is the right call

Cross-Region inference buys capacity, and it costs you determinism. Before enabling it, decide what you actually need: if your constraint is steady state throughput for a workload whose latency budget is tight, routing to another continent to find headroom can turn a capacity problem into a latency problem, because your p99 now depends on the destination Region that happens to answer. The pattern that holds up is to keep latency-sensitive interactive traffic on a profile you have measured, and send batch, evaluation, and agentic background work through the global profile where a few hundred milliseconds do not change the outcome. Confirm profile and Region support on the model card before you build, because the list changes as AWS adds Regions, and treat the profile ID as configuration rather than a constant baked into your code.

📌 Frequently Asked Questions

Which GPT-6 models are available on Bedrock through cross-Region inference?

GPT-6 Sol, GPT-6 Luna and GPT-6 Astra. Sol and Luna are reachable from Singapore, Jakarta, Malaysia, Thailand and Taipei; Astra is available from Asia Pacific (Singapore).

How do you call them?

Pass a global cross-Region inference profile ID such as global.openai.gpt-6-sol as the model on the bedrock-runtime endpoint, using the Converse API, the OpenAI Responses API, or the OpenAI Chat Completions API on the /openai/v1 paths.

Where does billing and monitoring happen?

It stays in your source Region. Billing, quota tracking, AWS CloudTrail and Amazon CloudWatch are all scoped to where you made the API call, not where the request was processed.

What is different about GPT-6 Astra?

It accepts the same reasoning effort levels as Sol and Luna except none, so it cannot be asked to skip reasoning, and it is available from fewer source Regions at launch.

Do I need an API key?

No. The endpoint accepts Signature Version 4 signing, and the token generator mints a short-term key from your AWS credentials valid for up to twelve hours, so there is no long-lived secret to store or rotate.