Skip to content
Merged
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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions codex-rs/app-server-protocol/schema/typescript/index.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion codex-rs/app-server-protocol/schema/typescript/v2/Account.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions codex-rs/app-server-protocol/src/protocol/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1668,6 +1668,7 @@ mod tests {
use super::*;
use anyhow::Result;
use codex_protocol::ThreadId;
use codex_protocol::account::AmazonBedrockCredentialSource;
use codex_protocol::account::PlanType;
use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_READ_ONLY;
use codex_protocol::parse_command::ParsedCommand;
Expand Down Expand Up @@ -2771,6 +2772,41 @@ mod tests {
serde_json::to_value(&chatgpt)?,
);

let codex_managed_bedrock = v2::Account::AmazonBedrock {
credential_source: AmazonBedrockCredentialSource::CodexManaged,
};
assert_eq!(
json!({
"type": "amazonBedrock",
"credentialSource": "codexManaged",
}),
serde_json::to_value(&codex_managed_bedrock)?,
);

let aws_managed_bedrock = v2::Account::AmazonBedrock {
credential_source: AmazonBedrockCredentialSource::AwsManaged,
};
assert_eq!(
json!({
"type": "amazonBedrock",
"credentialSource": "awsManaged",
}),
serde_json::to_value(&aws_managed_bedrock)?,
);

Ok(())
}

#[test]
fn account_defaults_legacy_bedrock_credential_source() -> Result<()> {
assert_eq!(
v2::Account::AmazonBedrock {
credential_source: AmazonBedrockCredentialSource::AwsManaged,
},
serde_json::from_value(json!({
"type": "amazonBedrock",
}))?,
);
Ok(())
}

Expand Down
14 changes: 12 additions & 2 deletions codex-rs/app-server-protocol/src/protocol/v2/account.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::protocol::common::AuthMode;
use codex_experimental_api_macros::ExperimentalApi;
use codex_protocol::account::AmazonBedrockCredentialSource;
use codex_protocol::account::PlanType;
use codex_protocol::account::ProviderAccount;
use codex_protocol::protocol::CreditsSnapshot as CoreCreditsSnapshot;
Expand Down Expand Up @@ -28,15 +29,24 @@ pub enum Account {

#[serde(rename = "amazonBedrock", rename_all = "camelCase")]
#[ts(rename = "amazonBedrock", rename_all = "camelCase")]
AmazonBedrock {},
AmazonBedrock {
#[serde(default = "default_bedrock_credential_source")]
credential_source: AmazonBedrockCredentialSource,
Comment thread
celia-oai marked this conversation as resolved.
},
}

fn default_bedrock_credential_source() -> AmazonBedrockCredentialSource {
AmazonBedrockCredentialSource::AwsManaged
}

impl From<ProviderAccount> for Account {
fn from(account: ProviderAccount) -> Self {
match account {
ProviderAccount::ApiKey => Self::ApiKey {},
ProviderAccount::Chatgpt { email, plan_type } => Self::Chatgpt { email, plan_type },
ProviderAccount::AmazonBedrock => Self::AmazonBedrock {},
ProviderAccount::AmazonBedrock { credential_source } => {
Self::AmazonBedrock { credential_source }
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions codex-rs/app-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1902,12 +1902,15 @@ Response examples:
{ "id": 1, "result": { "account": null, "requiresOpenaiAuth": true } } // OpenAI auth required (typical for OpenAI-hosted models)
{ "id": 1, "result": { "account": { "type": "apiKey" }, "requiresOpenaiAuth": true } }
{ "id": 1, "result": { "account": { "type": "chatgpt", "email": "user@example.com", "planType": "pro" }, "requiresOpenaiAuth": true } }
{ "id": 1, "result": { "account": { "type": "amazonBedrock", "credentialSource": "codexManaged" }, "requiresOpenaiAuth": false } }
{ "id": 1, "result": { "account": { "type": "amazonBedrock", "credentialSource": "awsManaged" }, "requiresOpenaiAuth": false } }
```

Field notes:

- `refreshToken` (bool): set `true` to force a token refresh.
- `requiresOpenaiAuth` reflects the active provider; when `false`, Codex can run without OpenAI credentials.
- Amazon Bedrock reports `credentialSource: "codexManaged"` when it uses a Bedrock API key managed by Codex. Otherwise it reports `credentialSource: "awsManaged"` for the external AWS credential path. This identifies the selected credential source; it does not validate that the AWS credential chain can resolve credentials.

### 2) Log in with an API key

Expand Down
51 changes: 50 additions & 1 deletion codex-rs/app-server/tests/suite/v2/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ use codex_login::AuthKeyringBackendKind;
use codex_login::CLIENT_ID_OVERRIDE_ENV_VAR;
use codex_login::REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR;
use codex_login::login_with_api_key;
use codex_login::login_with_bedrock_api_key;
use codex_protocol::account::AmazonBedrockCredentialSource;
use codex_protocol::account::PlanType as AccountPlanType;
use core_test_support::responses;
use pretty_assertions::assert_eq;
Expand Down Expand Up @@ -1725,13 +1727,60 @@ region = "us-west-2"
let received: GetAccountResponse = to_response(resp)?;

let expected = GetAccountResponse {
account: Some(Account::AmazonBedrock {}),
account: Some(Account::AmazonBedrock {
credential_source: AmazonBedrockCredentialSource::AwsManaged,
}),
requires_openai_auth: false,
};
assert_eq!(received, expected);
Ok(())
}

#[tokio::test]
async fn get_account_with_managed_bedrock_provider() -> Result<()> {
let codex_home = TempDir::new()?;
create_config_toml(
codex_home.path(),
CreateConfigTomlParams {
model_provider_id: Some("amazon-bedrock".to_string()),
..Default::default()
},
)?;
login_with_bedrock_api_key(
codex_home.path(),
"managed-bedrock-api-key",
"us-west-2",
AuthCredentialsStoreMode::File,
AuthKeyringBackendKind::default(),
)?;

let mut mcp = TestAppServer::new(codex_home.path()).await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;

let request_id = mcp
.send_get_account_request(GetAccountParams {
refresh_token: false,
})
.await?;
let resp: JSONRPCResponse = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_response_message(RequestId::Integer(request_id)),
)
.await??;
let received: GetAccountResponse = to_response(resp)?;

assert_eq!(
received,
GetAccountResponse {
account: Some(Account::AmazonBedrock {
credential_source: AmazonBedrockCredentialSource::CodexManaged,
}),
requires_openai_auth: false,
}
);
Ok(())
}

#[tokio::test]
async fn get_account_with_chatgpt() -> Result<()> {
let codex_home = TempDir::new()?;
Expand Down
26 changes: 25 additions & 1 deletion codex-rs/model-provider/src/amazon_bedrock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use codex_model_provider_info::ModelProviderAwsAuthInfo;
use codex_model_provider_info::ModelProviderInfo;
use codex_models_manager::manager::SharedModelsManager;
use codex_models_manager::manager::StaticModelsManager;
use codex_protocol::account::AmazonBedrockCredentialSource;
use codex_protocol::account::ProviderAccount;
use codex_protocol::error::Result;
use codex_protocol::openai_models::ModelsResponse;
Expand Down Expand Up @@ -130,8 +131,13 @@ impl ModelProvider for AmazonBedrockModelProvider {
}

fn account_state(&self) -> ProviderAccountResult {
let credential_source = if self.managed_auth().is_some() {
AmazonBedrockCredentialSource::CodexManaged
} else {
AmazonBedrockCredentialSource::AwsManaged
};
Ok(ProviderAccountState {
account: Some(ProviderAccount::AmazonBedrock),
account: Some(ProviderAccount::AmazonBedrock { credential_source }),
requires_openai_auth: false,
})
}
Expand Down Expand Up @@ -209,6 +215,15 @@ mod tests {
provider.auth().await,
Some(CodexAuth::BedrockApiKey(managed_auth))
);
assert_eq!(
provider.account_state(),
Ok(ProviderAccountState {
account: Some(ProviderAccount::AmazonBedrock {
credential_source: AmazonBedrockCredentialSource::CodexManaged,
}),
requires_openai_auth: false,
})
);
assert_eq!(
provider
.runtime_base_url()
Expand Down Expand Up @@ -238,6 +253,15 @@ mod tests {

assert!(provider.auth_manager().is_none());
assert_eq!(provider.auth().await, None);
assert_eq!(
provider.account_state(),
Ok(ProviderAccountState {
account: Some(ProviderAccount::AmazonBedrock {
credential_source: AmazonBedrockCredentialSource::AwsManaged,
}),
requires_openai_auth: false,
})
);
}

#[test]
Expand Down
5 changes: 4 additions & 1 deletion codex-rs/model-provider/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,10 @@ mod tests {
assert_eq!(
provider.account_state(),
Ok(ProviderAccountState {
account: Some(ProviderAccount::AmazonBedrock),
account: Some(ProviderAccount::AmazonBedrock {
credential_source:
codex_protocol::account::AmazonBedrockCredentialSource::AwsManaged,
}),
requires_openai_auth: false,
})
);
Expand Down
17 changes: 15 additions & 2 deletions codex-rs/protocol/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,21 @@ pub enum PlanType {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProviderAccount {
ApiKey,
Chatgpt { email: String, plan_type: PlanType },
AmazonBedrock,
Chatgpt {
email: String,
plan_type: PlanType,
},
AmazonBedrock {
credential_source: AmazonBedrockCredentialSource,
},
}

#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase")]
pub enum AmazonBedrockCredentialSource {
CodexManaged,
AwsManaged,
}

impl PlanType {
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/tui/src/app_server_session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ impl AppServerSession {
true,
)
}
Some(Account::AmazonBedrock {}) => {
Some(Account::AmazonBedrock { .. }) => {
(None, None, None, None, FeedbackAudience::External, false)
}
None => (None, None, None, None, FeedbackAudience::External, false),
Expand Down
2 changes: 1 addition & 1 deletion codex-rs/tui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1874,7 +1874,7 @@ async fn get_login_status(
Ok(match account.account {
Some(AppServerAccount::ApiKey {}) => LoginStatus::AuthMode(AppServerAuthMode::ApiKey),
Some(AppServerAccount::Chatgpt { .. }) => LoginStatus::AuthMode(AppServerAuthMode::Chatgpt),
Some(AppServerAccount::AmazonBedrock {}) => LoginStatus::NotAuthenticated,
Some(AppServerAccount::AmazonBedrock { .. }) => LoginStatus::NotAuthenticated,
None => LoginStatus::NotAuthenticated,
})
}
Expand Down
Loading