Skip to content

Cloud Tasks

源码版本rust-v0.145.0

codex cloud est la sous-commande qui dépose un prompt sur le cloud OpenAI : en local, on n'appelle pas le modèle ni ne fait tourner la boucle Agent ; on ne fait que soumettre une tâche, tirer son état, récupérer le diff et appliquer le patch. cloud-tasks est l'entrée TUI + CLI ; cloud-tasks-client fournit le trait CloudBackend + une impl HTTP ; cloud-tasks-mock-client fournit un faux backend pour le mode debug ; backend-client est le HTTP sous-jacent (réutilisé par d'autres API backend). Contrairement à Model Provider pour l'inférence locale, ici « l'inférence modèle » se passe dans un container cloud.

Responsabilités

  1. Soumettre une tâche : run_exec_command emballe prompt + git ref + environment id et fait un POST /wham/tasks (ou /api/codex/tasks), qui renvoie un task id (codex-rs/cloud-tasks/src/lib.rs:161-184).
  2. Liste / détail / diff : run_list_command, run_status_command, run_diff_command sont trois sous-commandes CLI correspondant à list_tasks / get_task_summary / get_task_diff (codex-rs/cloud-tasks/src/cli.rs:16-27).
  3. Appliquer un patch : apply_task enchaîne un dry-run preflight puis un vrai apply ; diff_override laisse l'utilisateur choisir une tentative parmi un best-of-N (codex-rs/cloud-tasks-client/src/http.rs:99-121).
  4. Abstraire en trait : CloudBackend regroupe toutes les interactions backend en un seul trait ; en build debug on utilise MockClient, en release HttpClient (codex-rs/cloud-tasks-client/src/api.rs:136-176).
  5. Initialiser le backend : init_backend monte base URL, UA, auth provider, ChatGPT-Account-Id, et choisit le style de chemin WHAM / Codex API selon CODEX_CLOUD_TASKS_BASE_URL (codex-rs/cloud-tasks/src/lib.rs:43-107).

Motivations de conception

C'est un crate séparé plutôt que dans core, parce que ce chemin ne fait pas tourner la boucle principale de l'Agent. Le client de core envoie une requête stream au modèle, parse le SSE et maintient le turn state ; un cloud task consiste à « soumettre une tâche longue au cloud, attendre asynchroniquement le résultat ». Les deux modèles de concurrence sont totalement différents : l'un est stream longue connexion, l'autre est poll + pull. Le trait CloudBackend existe pour que la TUI puisse tourner en build debug — init_backend détecte CODEX_CLOUD_TASKS_MODE=mock et swap en MockClient, pour régler la machine à états de l'UI sans réelle connexion au backend ChatGPT. HttpClient réutilise en interne backend-client::Client plutôt que de repartir d'un HTTP séparé, parce que les headers d'auth de backend-api, le store de cookies Cloudflare et le style de chemin (wham vs codex-api) sont cohérents avec d'autres appels d'API backend.

Le best-of-N est une spécificité de ce chemin : create_task accepte attempts: usize (1-4) ; le cloud fait tourner plusieurs attempts ; list_sibling_attempts les récupère tous ; ApplyCommand permet à l'utilisateur de spécifier --attempt N pour choisir un résultat. Le champ diff_override traverse apply_task_preflight et apply_task, pour qu'à l'étape d'apply on ne re-tire pas le diff mais qu'on utilise celui que l'utilisateur a choisi.

Fichiers clés

codex-rs/cloud-tasks/src/lib.rs:735-744run_main, dispatch des sous-commandes + entrée du mode TUI.codex-rs/cloud-tasks/src/lib.rs:43-107BackendContext et init_backend, décide mock / http selon les variables d'environnement et monte l'auth.codex-rs/cloud-tasks/src/cli.rs:29-50ExecCommand, définit les trois paramètres centraux --env, --attempts, --branch.codex-rs/cloud-tasks-client/src/api.rs:136-176 — le trait CloudBackend, 11 méthodes couvrant list / get / apply / create.codex-rs/cloud-tasks-client/src/http.rs:25-63HttpClient, enveloppe backend-client::Client et implémente CloudBackend.codex-rs/backend-client/src/client.rs:451-482 — impl de create_task dans le base client, extrait le task id du JSON.codex-rs/cloud-tasks-mock-client/src/mock.rs:163-189MockClient implémente le trait en déléguant à des fonctions de génération de données mock.

La factory init_backend inspecte les variables d'environnement en build debug pour décider mock vs vrai HTTP :

rust
// cloud-tasks/src/lib.rs:43-60 — 后端选择
async fn init_backend(user_agent_suffix: &str) -> anyhow::Result<BackendContext> {
    #[cfg(debug_assertions)]
    let use_mock = matches!(
        std::env::var("CODEX_CLOUD_TASKS_MODE").ok().as_deref(),
        Some("mock") | Some("MOCK")
    );
    let base_url = std::env::var("CODEX_CLOUD_TASKS_BASE_URL")
        .unwrap_or_else(|_| "https://chatgpt.com/backend-api".to_string());
    #[cfg(debug_assertions)]
    if use_mock {
        return Ok(BackendContext {
            backend: Arc::new(codex_cloud_tasks_mock_client::MockClient),
            base_url,
        });
    }

Le trait CloudBackend unifie les types de retour en CloudBackendFuture (boxed future) ; mock et http partagent la même signature :

rust
// cloud-tasks-client/src/api.rs:136-176 — 后端抽象
pub trait CloudBackend: Send + Sync {
    fn list_tasks<'a>(
        &'a self,
        env: Option<&'a str>,
        limit: Option<i64>,
        cursor: Option<&'a str>,
    ) -> CloudBackendFuture<'a, TaskListPage>;
    fn get_task_summary(&self, id: TaskId) -> CloudBackendFuture<'_, TaskSummary>;
    fn get_task_diff(&self, id: TaskId) -> CloudBackendFuture<'_, Option<String>>;
    // ...messages / sibling attempts / apply preflight / apply / create
    fn create_task<'a>(
        &'a self,
        env_id: &'a str,
        prompt: &'a str,
        git_ref: &'a str,
        qa_mode: bool,
        best_of_n: usize,
    ) -> CloudBackendFuture<'a, CreatedTask>;
}

create_task POST depuis le base client, puis extrait le task id du JSON (priorité à task.id, repli sur id au sommet) :

rust
// backend-client/src/client.rs:451-482 — POST /wham/tasks 并提取 task id
pub async fn create_task(&self, request_body: serde_json::Value) -> Result<String> {
    let url = match self.path_style {
        PathStyle::CodexApi => format!("{}/api/codex/tasks", self.base_url),
        PathStyle::ChatGptApi => format!("{}/wham/tasks", self.base_url),
    };
    // ...发请求,解析 body
    match serde_json::from_str::<serde_json::Value>(&body) {
        Ok(v) => {
            if let Some(id) = v.get("task").and_then(|t| t.get("id")).and_then(|s| s.as_str()) {
                Ok(id.to_string())
            } else if let Some(id) = v.get("id").and_then(|s| s.as_str()) {
                Ok(id.to_string())
            } else {
                anyhow::bail!("POST {url} succeeded but no task id found; ...");
            }
        }
        Err(e) => anyhow::bail!("Decode error for {url}: {e}; ..."),
    }
}

Flux de données

Limites et échecs

Récapitulatif

Cloud Tasks comprime les trois étapes « soumettre, interroger, appliquer » dans le trait CloudBackend ; HTTP et mock implémentent le même trait, ce qui facilite le débogage de la TUI sans backend local. Le HTTP sous-jacent réutilise backend-client::Client et partage avec Model Provider la décision d'auth et le style de chemin. La configuration propre aux tâches cloud (environment autorisé, stratégie enterprise) vient du cloud config bundle de Système de configuration, tiré et appliqué au démarrage.