mirror of
https://github.com/openai/codex.git
synced 2026-10-30 19:18:49 +00:00
[VSCE-157] Allow Link Formatting via Markdown in System Errors
This commit is contained in:
@@ -48,6 +48,7 @@ use crate::conversation_history::ConversationHistory;
|
||||
use crate::environment_context::EnvironmentContext;
|
||||
use crate::error::CodexErr;
|
||||
use crate::error::Result as CodexResult;
|
||||
use crate::error::error_event_from;
|
||||
use crate::exec::ExecToolCallOutput;
|
||||
#[cfg(test)]
|
||||
use crate::exec::StreamOutput;
|
||||
@@ -388,7 +389,10 @@ impl Session {
|
||||
error!("{message}");
|
||||
post_session_configured_error_events.push(Event {
|
||||
id: INITIAL_SUBMIT_ID.to_owned(),
|
||||
msg: EventMsg::Error(ErrorEvent { message }),
|
||||
msg: EventMsg::Error(ErrorEvent {
|
||||
message,
|
||||
markdown_message: None,
|
||||
}),
|
||||
});
|
||||
(McpConnectionManager::default(), Default::default())
|
||||
}
|
||||
@@ -401,7 +405,10 @@ impl Session {
|
||||
error!("{message}");
|
||||
post_session_configured_error_events.push(Event {
|
||||
id: INITIAL_SUBMIT_ID.to_owned(),
|
||||
msg: EventMsg::Error(ErrorEvent { message }),
|
||||
msg: EventMsg::Error(ErrorEvent {
|
||||
message,
|
||||
markdown_message: None,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1458,6 +1465,7 @@ async fn submission_loop(
|
||||
id: sub.id.clone(),
|
||||
msg: EventMsg::Error(ErrorEvent {
|
||||
message: "Failed to shutdown rollout recorder".to_string(),
|
||||
markdown_message: None,
|
||||
}),
|
||||
};
|
||||
sess.send_event(event).await;
|
||||
@@ -1841,6 +1849,7 @@ pub(crate) async fn run_task(
|
||||
message: format!(
|
||||
"Conversation is still above the token limit after automatic summarization (limit {limit_str}, current {current_tokens}). Please start a new session or trim your input."
|
||||
),
|
||||
markdown_message: None,
|
||||
}),
|
||||
};
|
||||
sess.send_event(event).await;
|
||||
@@ -1871,9 +1880,7 @@ pub(crate) async fn run_task(
|
||||
info!("Turn error: {e:#}");
|
||||
let event = Event {
|
||||
id: sub_id.clone(),
|
||||
msg: EventMsg::Error(ErrorEvent {
|
||||
message: e.to_string(),
|
||||
}),
|
||||
msg: EventMsg::Error(error_event_from(&e)),
|
||||
};
|
||||
sess.send_event(event).await;
|
||||
// let the user continue the conversation
|
||||
|
||||
@@ -7,9 +7,9 @@ use crate::Prompt;
|
||||
use crate::client_common::ResponseEvent;
|
||||
use crate::error::CodexErr;
|
||||
use crate::error::Result as CodexResult;
|
||||
use crate::error::error_event_from;
|
||||
use crate::protocol::AgentMessageEvent;
|
||||
use crate::protocol::CompactedItem;
|
||||
use crate::protocol::ErrorEvent;
|
||||
use crate::protocol::Event;
|
||||
use crate::protocol::EventMsg;
|
||||
use crate::protocol::InputItem;
|
||||
@@ -108,9 +108,7 @@ async fn run_compact_task_inner(
|
||||
.await;
|
||||
let event = Event {
|
||||
id: sub_id.clone(),
|
||||
msg: EventMsg::Error(ErrorEvent {
|
||||
message: e.to_string(),
|
||||
}),
|
||||
msg: EventMsg::Error(error_event_from(&e)),
|
||||
};
|
||||
sess.send_event(event).await;
|
||||
return;
|
||||
@@ -131,9 +129,7 @@ async fn run_compact_task_inner(
|
||||
} else {
|
||||
let event = Event {
|
||||
id: sub_id.clone(),
|
||||
msg: EventMsg::Error(ErrorEvent {
|
||||
message: e.to_string(),
|
||||
}),
|
||||
msg: EventMsg::Error(error_event_from(&e)),
|
||||
};
|
||||
sess.send_event(event).await;
|
||||
return;
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::exec::ExecToolCallOutput;
|
||||
use crate::token_data::KnownPlan;
|
||||
use crate::token_data::PlanType;
|
||||
use codex_protocol::ConversationId;
|
||||
use codex_protocol::protocol::ErrorEvent;
|
||||
use codex_protocol::protocol::RateLimitSnapshot;
|
||||
use reqwest::StatusCode;
|
||||
use serde_json;
|
||||
@@ -12,6 +13,8 @@ use tokio::task::JoinError;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, CodexErr>;
|
||||
|
||||
const PRICING_URL: &str = "https://openai.com/chatgpt/pricing";
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum SandboxErr {
|
||||
/// Error from sandbox execution
|
||||
@@ -197,7 +200,7 @@ impl std::fmt::Display for UsageLimitReachedError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let message = match self.plan_type.as_ref() {
|
||||
Some(PlanType::Known(KnownPlan::Plus)) => format!(
|
||||
"You've hit your usage limit. Upgrade to Pro (https://openai.com/chatgpt/pricing){}",
|
||||
"You've hit your usage limit. Upgrade to Pro ({PRICING_URL}){}",
|
||||
retry_suffix_after_or(self.resets_in_seconds)
|
||||
),
|
||||
Some(PlanType::Known(KnownPlan::Team)) | Some(PlanType::Known(KnownPlan::Business)) => {
|
||||
@@ -207,8 +210,9 @@ impl std::fmt::Display for UsageLimitReachedError {
|
||||
)
|
||||
}
|
||||
Some(PlanType::Known(KnownPlan::Free)) => {
|
||||
"You've hit your usage limit. Upgrade to Plus to continue using Codex (https://openai.com/chatgpt/pricing)."
|
||||
.to_string()
|
||||
format!(
|
||||
"You've hit your usage limit. Upgrade to Plus to continue using Codex ({PRICING_URL})."
|
||||
)
|
||||
}
|
||||
Some(PlanType::Known(KnownPlan::Pro))
|
||||
| Some(PlanType::Known(KnownPlan::Enterprise))
|
||||
@@ -226,6 +230,40 @@ impl std::fmt::Display for UsageLimitReachedError {
|
||||
}
|
||||
}
|
||||
|
||||
impl UsageLimitReachedError {
|
||||
pub(crate) fn markdown_message(&self) -> String {
|
||||
match self.plan_type.as_ref() {
|
||||
Some(PlanType::Known(KnownPlan::Plus)) => format!(
|
||||
"You've hit your usage limit. Upgrade to [Pro]({PRICING_URL}){}",
|
||||
retry_suffix_after_or(self.resets_in_seconds)
|
||||
),
|
||||
Some(PlanType::Known(KnownPlan::Free)) => format!(
|
||||
"You've hit your usage limit. [Upgrade to Plus]({PRICING_URL}) to continue using Codex."
|
||||
),
|
||||
_ => self.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CodexErr {
|
||||
pub(crate) fn markdown_message(&self) -> Option<String> {
|
||||
match self {
|
||||
CodexErr::UsageLimitReached(err) => Some(err.markdown_message()),
|
||||
CodexErr::UsageNotIncluded => Some(format!(
|
||||
"To use Codex with your ChatGPT plan, [upgrade to Plus]({PRICING_URL})."
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn error_event_from(err: &CodexErr) -> ErrorEvent {
|
||||
ErrorEvent {
|
||||
message: err.to_string(),
|
||||
markdown_message: err.markdown_message(),
|
||||
}
|
||||
}
|
||||
|
||||
fn retry_suffix(resets_in_seconds: Option<u64>) -> String {
|
||||
if let Some(secs) = resets_in_seconds {
|
||||
let reset_duration = format_reset_duration(secs);
|
||||
@@ -348,6 +386,19 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_limit_reached_error_markdown_plus_plan() {
|
||||
let err = UsageLimitReachedError {
|
||||
plan_type: Some(PlanType::Known(KnownPlan::Plus)),
|
||||
resets_in_seconds: Some(60),
|
||||
rate_limits: Some(rate_limit_snapshot()),
|
||||
};
|
||||
assert_eq!(
|
||||
err.markdown_message(),
|
||||
"You've hit your usage limit. Upgrade to [Pro](https://openai.com/chatgpt/pricing) or try again in 1 minute."
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_limit_reached_error_formats_free_plan() {
|
||||
let err = UsageLimitReachedError {
|
||||
@@ -361,6 +412,19 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_limit_reached_error_markdown_free_plan() {
|
||||
let err = UsageLimitReachedError {
|
||||
plan_type: Some(PlanType::Known(KnownPlan::Free)),
|
||||
resets_in_seconds: Some(3600),
|
||||
rate_limits: Some(rate_limit_snapshot()),
|
||||
};
|
||||
assert_eq!(
|
||||
err.markdown_message(),
|
||||
"You've hit your usage limit. [Upgrade to Plus](https://openai.com/chatgpt/pricing) to continue using Codex."
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_limit_reached_error_formats_default_when_none() {
|
||||
let err = UsageLimitReachedError {
|
||||
@@ -413,6 +477,31 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_err_usage_not_included_markdown() {
|
||||
let err = CodexErr::UsageNotIncluded;
|
||||
assert_eq!(
|
||||
err.markdown_message(),
|
||||
Some("To use Codex with your ChatGPT plan, [upgrade to Plus](https://openai.com/chatgpt/pricing).".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_err_usage_limit_reached_markdown() {
|
||||
let err = CodexErr::UsageLimitReached(UsageLimitReachedError {
|
||||
plan_type: Some(PlanType::Known(KnownPlan::Plus)),
|
||||
resets_in_seconds: Some(120),
|
||||
rate_limits: Some(rate_limit_snapshot()),
|
||||
});
|
||||
assert_eq!(
|
||||
err.markdown_message(),
|
||||
Some(
|
||||
"You've hit your usage limit. Upgrade to [Pro](https://openai.com/chatgpt/pricing) or try again in 2 minutes."
|
||||
.to_string()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usage_limit_reached_includes_minutes_when_available() {
|
||||
let err = UsageLimitReachedError {
|
||||
|
||||
@@ -583,7 +583,7 @@ async fn auto_compact_stops_after_failed_attempt() {
|
||||
.unwrap();
|
||||
|
||||
let error_event = wait_for_event(&codex, |ev| matches!(ev, EventMsg::Error(_))).await;
|
||||
let EventMsg::Error(ErrorEvent { message }) = error_event else {
|
||||
let EventMsg::Error(ErrorEvent { message, .. }) = error_event else {
|
||||
panic!("expected error event");
|
||||
};
|
||||
assert!(
|
||||
|
||||
Reference in New Issue
Block a user