mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-06 08:06:19 +00:00
docs(backend): unify provider comments to Chinese
Convert the remaining English doc comments in the openai and xai provider crates to Chinese, function-oriented wording so the whole backend follows one convention. Explanatory narration is compressed into what the item does and what its errors mean; code identifiers, route paths, rustdoc links and `# Errors` headings stay as-is. Also fixes two half-translated comments left in the websocket protocol module and merges a duplicated doc block on the OIDC verifier constructor. No code, signature or `#[error(...)]` string changes. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -60,7 +60,7 @@ end
|
||||
return 1
|
||||
"#;
|
||||
|
||||
/// Redis-backed, best-effort native response continuation affinity.
|
||||
/// 基于 Redis 的 best-effort 原生响应 continuation 亲和。
|
||||
#[derive(Clone)]
|
||||
pub struct RedisNativeContinuationRepository {
|
||||
connection: ConnectionManager,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Codex credential domain exports。
|
||||
//! Codex credential 领域导出。
|
||||
|
||||
mod admin;
|
||||
mod affinity;
|
||||
|
||||
@@ -35,7 +35,7 @@ use super::websocket::{
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
// 常量
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const MAX_UPSTREAM_ERROR_BODY_BYTES: usize = 1024 * 1024;
|
||||
@@ -82,7 +82,7 @@ pub fn build_reqwest_client() -> Result<Client, CustomCaError> {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Error types
|
||||
// 错误类型
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Codex 上游 HTTP 客户端错误。
|
||||
@@ -245,7 +245,7 @@ pub type CodexBackendSseStream =
|
||||
Pin<Box<dyn Stream<Item = CodexClientResult<Bytes>> + Send + 'static>>;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Request context & response types
|
||||
// 请求上下文与响应类型
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// 单次 Codex 上游请求的上下文。
|
||||
@@ -253,7 +253,7 @@ pub type CodexBackendSseStream =
|
||||
pub struct CodexRequestContext<'a> {
|
||||
/// Provider 已构造并脱敏持有的完整 Authorization 值。
|
||||
pub authorization: &'a str,
|
||||
/// ChatGPT account id。
|
||||
/// ChatGPT 账号 ID。
|
||||
pub account_id: Option<&'a str>,
|
||||
/// 代理请求 ID。
|
||||
pub request_id: &'a str,
|
||||
@@ -261,11 +261,11 @@ pub struct CodexRequestContext<'a> {
|
||||
pub turn_state: Option<&'a str>,
|
||||
/// 客户端 turn metadata;其中 installation ID 已按当前账号处理。
|
||||
pub turn_metadata: Option<&'a str>,
|
||||
/// x-codex-beta-features。
|
||||
/// x-codex-beta-features 头值。
|
||||
pub beta_features: Option<&'a str>,
|
||||
/// x-responsesapi-include-timing-metrics。
|
||||
/// x-responsesapi-include-timing-metrics 头值。
|
||||
pub include_timing_metrics: Option<&'a str>,
|
||||
/// version。
|
||||
/// 客户端版本号。
|
||||
pub version: Option<&'a str>,
|
||||
/// 客户端 window ID。
|
||||
pub codex_window_id: Option<&'a str>,
|
||||
@@ -335,9 +335,9 @@ impl fmt::Debug for CodexRequestContext<'_> {
|
||||
/// Codex Responses 实际使用的上游传输。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CodexBackendTransport {
|
||||
/// HTTP SSE transport.
|
||||
/// HTTP SSE 传输。
|
||||
HttpSse,
|
||||
/// WebSocket transport.
|
||||
/// WebSocket 传输。
|
||||
WebSocket,
|
||||
}
|
||||
|
||||
@@ -382,10 +382,10 @@ pub struct CodexTransportMetrics {
|
||||
pub http_version: Option<String>,
|
||||
}
|
||||
|
||||
/// Live stream rate-limit updates captured after the response headers.
|
||||
/// 响应头之后在 live 流中采集的限流更新。
|
||||
pub type CodexRateLimitHeaderUpdates = CodexWebSocketRateLimitHeaderUpdates;
|
||||
|
||||
/// Live stream turn-state updates captured after the response headers.
|
||||
/// 响应头之后在 live 流中采集的 turn state 更新。
|
||||
pub type CodexTurnStateUpdate = CodexWebSocketTurnStateUpdate;
|
||||
|
||||
/// Codex Responses 上游 live SSE 响应。
|
||||
@@ -449,7 +449,7 @@ pub(super) struct PreparedWebSocketRoute {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Response helpers
|
||||
// 响应辅助函数
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub(super) fn log_websocket_pool_decision(
|
||||
@@ -587,7 +587,7 @@ pub(super) async fn read_capped_error_body(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Request helpers
|
||||
// 请求辅助函数
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub(super) fn websocket_upstream_request(request: &CodexResponsesRequest) -> CodexResponsesRequest {
|
||||
@@ -649,7 +649,7 @@ pub(super) fn openai_subagent_from_metadata(client_metadata: Option<&Value>) ->
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Error conversion helpers
|
||||
// 错误转换辅助函数
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
pub(super) fn websocket_exchange_error_to_client_error(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Upstream response diagnostics captured at the transport boundary.
|
||||
//! 在 transport 边界采集的上游响应诊断信息。
|
||||
|
||||
use super::protocol::responses::ResponsesSseFailure;
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
||||
|
||||
@@ -60,7 +60,7 @@ pub struct CodexResponsesRequest {
|
||||
pub responses_lite: Option<String>,
|
||||
/// Memory consolidation 请求语义;HTTP 与 WebSocket opening 均使用 header。
|
||||
pub memgen_request: Option<String>,
|
||||
/// codex window id。
|
||||
/// Codex window ID。
|
||||
pub codex_window_id: Option<String>,
|
||||
/// 父线程 ID。
|
||||
pub parent_thread_id: Option<String>,
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct OpeningAuditSnapshot {
|
||||
pub request_line: String,
|
||||
/// 请求头顺序。
|
||||
pub header_order: Vec<String>,
|
||||
/// 红action后的请求头。
|
||||
/// 脱敏后的请求头。
|
||||
pub headers: Vec<OpeningAuditHeader>,
|
||||
}
|
||||
|
||||
@@ -31,11 +31,11 @@ pub struct OpeningAuditHeader {
|
||||
pub struct PayloadAuditSnapshot {
|
||||
/// 按构造顺序记录的顶层字段。
|
||||
pub top_level_keys: Vec<String>,
|
||||
/// 红action后的 JSON payload。
|
||||
/// 脱敏后的 JSON payload。
|
||||
pub body: Value,
|
||||
}
|
||||
|
||||
/// WebSocket audit artifact.
|
||||
/// WebSocket 审计产物。
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
|
||||
pub struct WebSocketAuditArtifact {
|
||||
/// 实际选择的传输模式。
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::transport::diagnostics::CodexUpstreamSendPhase;
|
||||
|
||||
use super::PreviousResponseUnavailableReason;
|
||||
|
||||
/// Responses WebSocket exchange error.
|
||||
/// Responses WebSocket 交互错误。
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CodexWebSocketExchangeError {
|
||||
/// opening request 无法构造。
|
||||
@@ -98,7 +98,7 @@ pub enum CodexWebSocketExchangeError {
|
||||
/// WebSocket 上游错误帧载荷。
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CodexWebSocketUpstreamError {
|
||||
/// HTTP-style upstream status code.
|
||||
/// 上游返回的 HTTP 风格状态码。
|
||||
pub status_code: u16,
|
||||
/// 推导出的重试秒数。
|
||||
pub retry_after_seconds: Option<u64>,
|
||||
|
||||
@@ -24,11 +24,11 @@ const WEBSOCKET_STREAM_BUFFER: usize = 16;
|
||||
|
||||
pub(super) use self::stream::{WebSocketStreamPoolReturn, stream_websocket_response};
|
||||
|
||||
/// Responses WebSocket live SSE exchange result.
|
||||
/// Responses WebSocket live SSE 交互结果。
|
||||
pub struct CodexWebSocketStreamingExchange {
|
||||
/// 关联请求日志与底层 pump 生命周期日志的连接标识。
|
||||
pub(crate) websocket_connection_id: Uuid,
|
||||
/// Live SSE bytes converted from WebSocket events.
|
||||
/// 由 WebSocket 事件转换出的 live SSE 字节流。
|
||||
pub body: CodexWebSocketSseStream,
|
||||
/// 上游 metadata 帧中的最新 turn state。
|
||||
pub turn_state: Option<String>,
|
||||
@@ -50,12 +50,12 @@ pub struct CodexWebSocketStreamingExchange {
|
||||
pub response_metadata: CodexResponseMetadata,
|
||||
}
|
||||
|
||||
/// Responses WebSocket live SSE byte stream.
|
||||
/// Responses WebSocket live SSE 字节流。
|
||||
pub type CodexWebSocketSseStream =
|
||||
Pin<Box<dyn Stream<Item = Result<Bytes, CodexWebSocketExchangeError>> + Send + 'static>>;
|
||||
/// WebSocket live stream rate-limit header updates.
|
||||
/// live 流中的限流头动态更新。
|
||||
pub type CodexWebSocketRateLimitHeaderUpdates = Arc<Mutex<Vec<(String, String)>>>;
|
||||
/// WebSocket live stream turn-state update.
|
||||
/// live 流中的 turn state 动态更新。
|
||||
pub type CodexWebSocketTurnStateUpdate = Arc<Mutex<Option<String>>>;
|
||||
|
||||
pub(super) fn reusable_websocket_metadata(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Codex HTTP Responses WebSocket transport。
|
||||
//! Codex Responses WebSocket 传输。
|
||||
|
||||
mod audit;
|
||||
mod breaker;
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct CodexWebSocketConnection {
|
||||
pub(super) headers: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
/// Prepared Responses WebSocket request descriptor.
|
||||
/// 已构造完成的 Responses WebSocket 请求描述。
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CodexWebSocketRequest {
|
||||
pub(super) connection: CodexWebSocketConnection,
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::{
|
||||
|
||||
const MAX_CALLBACK_VALUE_BYTES: usize = 64 * 1024;
|
||||
|
||||
/// Parsed callback whose code and state remain redacted in debug output.
|
||||
/// 已解析的回调;code 与 state 在 debug 输出中保持脱敏。
|
||||
pub struct AuthorizationCallback {
|
||||
code: Option<SecretValue>,
|
||||
state: Option<SecretValue>,
|
||||
@@ -22,13 +22,13 @@ pub struct AuthorizationCallback {
|
||||
}
|
||||
|
||||
impl AuthorizationCallback {
|
||||
/// Parses an OAuth callback query while rejecting duplicate security fields.
|
||||
/// Raw server descriptions are intentionally discarded.
|
||||
/// 解析 OAuth 回调 query,并拒绝重复的安全敏感参数。
|
||||
/// 服务端原始描述文本一律丢弃。
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`CallbackRejection::DuplicateParameter`] for repeated `code`,
|
||||
/// `state`, or `error` keys.
|
||||
/// `code`、`state` 或 `error` 键重复出现时返回
|
||||
/// [`CallbackRejection::DuplicateParameter`]。
|
||||
pub fn parse(query: &str) -> Result<Self, CallbackRejection> {
|
||||
let mut code = None;
|
||||
let mut state = None;
|
||||
@@ -72,7 +72,7 @@ impl fmt::Debug for AuthorizationCallback {
|
||||
}
|
||||
}
|
||||
|
||||
/// Authorization Code + PKCE state awaiting one callback.
|
||||
/// 等待一次回调的 Authorization Code + PKCE 状态。
|
||||
pub struct PendingAuthorization {
|
||||
authorization_url: Url,
|
||||
redirect_uri: AllowedRedirectUri,
|
||||
@@ -122,7 +122,7 @@ impl PendingAuthorization {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the URL that an administrator opens at the official issuer.
|
||||
/// 返回管理员需在官方 issuer 打开的 URL。
|
||||
#[must_use]
|
||||
pub fn authorization_url(&self) -> &Url {
|
||||
&self.authorization_url
|
||||
@@ -180,12 +180,11 @@ impl PendingAuthorization {
|
||||
})
|
||||
}
|
||||
|
||||
/// Consumes the one-time flow and validates mandatory callback state.
|
||||
/// 消费一次性流程并校验必需的回调 state。
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a callback rejection for missing/mismatched state, provider
|
||||
/// denial, or a missing code.
|
||||
/// state 缺失或不匹配、provider 拒绝、code 缺失时返回回调拒绝错误。
|
||||
pub fn accept_callback(
|
||||
self,
|
||||
callback: AuthorizationCallback,
|
||||
@@ -247,7 +246,7 @@ impl fmt::Debug for PendingAuthorization {
|
||||
}
|
||||
}
|
||||
|
||||
/// State-validated authorization grant ready for one token exchange.
|
||||
/// 已通过 state 校验、可用于一次 token 交换的授权 grant。
|
||||
pub struct AuthorizationCodeGrant {
|
||||
code: SecretValue,
|
||||
redirect_uri: AllowedRedirectUri,
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
VerificationMethod, VerifiedTokenSet,
|
||||
};
|
||||
|
||||
/// Transport-agnostic official Grok Build OAuth protocol client.
|
||||
/// 与 transport 无关的官方 Grok Build OAuth 协议客户端。
|
||||
#[derive(Clone)]
|
||||
pub struct GrokOAuthClient {
|
||||
config: GrokOAuthConfig,
|
||||
@@ -19,7 +19,7 @@ pub struct GrokOAuthClient {
|
||||
}
|
||||
|
||||
impl GrokOAuthClient {
|
||||
/// Creates a client with explicit HTTP and token verification trust ports.
|
||||
/// 创建客户端,显式注入 HTTP 与 token 验证两个信任端口。
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
config: GrokOAuthConfig,
|
||||
@@ -35,18 +35,18 @@ impl GrokOAuthClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the immutable official provider configuration.
|
||||
/// 返回不可变的官方 provider 配置。
|
||||
#[must_use]
|
||||
pub const fn config(&self) -> &GrokOAuthConfig {
|
||||
&self.config
|
||||
}
|
||||
|
||||
/// Fetches and validates official same-origin OIDC discovery.
|
||||
/// 拉取并校验官方同源 OIDC 发现文档。
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Fails on transport errors, non-success status, malformed JSON, issuer
|
||||
/// mismatch, cross-origin endpoints, missing JWKS, or insecure algorithms.
|
||||
/// transport 失败、非成功状态、JSON 非法、issuer 不匹配、端点跨 origin、
|
||||
/// 缺少 JWKS 或算法不安全时返回错误。
|
||||
pub async fn discover(&self) -> Result<DiscoveryDocument, OAuthError> {
|
||||
let response = self
|
||||
.execute(
|
||||
@@ -63,12 +63,11 @@ impl GrokOAuthClient {
|
||||
DiscoveryDocument::parse(&self.config, response.body())
|
||||
}
|
||||
|
||||
/// Starts Authorization Code + PKCE without performing network I/O.
|
||||
/// 启动 Authorization Code + PKCE,不产生网络 I/O。
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an entropy error if secure random state, nonce, or verifier
|
||||
/// generation fails.
|
||||
/// 安全随机 state、nonce 或 verifier 生成失败时返回熵不可用错误。
|
||||
pub fn start_authorization_code(
|
||||
&self,
|
||||
discovery: &DiscoveryDocument,
|
||||
@@ -78,13 +77,13 @@ impl GrokOAuthClient {
|
||||
PendingAuthorization::start(&self.config, discovery, redirect_uri, principal)
|
||||
}
|
||||
|
||||
/// Exchanges a state-validated authorization grant exactly once, then
|
||||
/// requires nonce-bound ID-token verification before returning credentials.
|
||||
/// 对已校验 state 的授权 grant 只做一次交换,返回凭据前强制通过
|
||||
/// nonce 绑定的 ID token 验证。
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Fails on transport/protocol errors, missing ID token, failed verification,
|
||||
/// or any non-success OAuth response. No automatic retry is performed.
|
||||
/// transport/协议错误、缺少 ID token、验证失败或任何非成功 OAuth 响应时
|
||||
/// 返回错误。不做自动重试。
|
||||
pub async fn exchange_authorization_code(
|
||||
&self,
|
||||
discovery: &DiscoveryDocument,
|
||||
@@ -216,13 +215,13 @@ impl GrokOAuthClient {
|
||||
Ok(VerifiedTokenSet::new(tokens, evidence, scope))
|
||||
}
|
||||
|
||||
/// Exchanges a refresh token once. The caller must serialize refreshes and
|
||||
/// persist rotated tokens through credential-revision CAS.
|
||||
/// 执行一次 refresh token 交换。调用方须串行化刷新,并通过 credential
|
||||
/// revision CAS 持久化轮换后的 token。
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a classified OAuth error. Ambiguous transport failures must not
|
||||
/// be retried automatically because the refresh token may have rotated.
|
||||
/// 返回已分类的 OAuth 错误。refresh token 可能已轮换,Ambiguous 类
|
||||
/// transport 失败不得自动重试。
|
||||
pub async fn refresh(
|
||||
&self,
|
||||
discovery: &DiscoveryDocument,
|
||||
|
||||
@@ -4,16 +4,16 @@ use url::{Host, Url};
|
||||
|
||||
use crate::ConfigError;
|
||||
|
||||
/// Official first-party OIDC issuer used by Grok Build.
|
||||
/// Grok Build 使用的官方第一方 OIDC issuer。
|
||||
pub const OFFICIAL_ISSUER: &str = "https://auth.x.ai";
|
||||
|
||||
/// Official public OAuth client identifier embedded in Grok Build.
|
||||
/// Grok Build 内置的官方 public OAuth client 标识。
|
||||
pub const OFFICIAL_CLIENT_ID: &str = "b1a00492-073a-47ea-816f-4c329264a828";
|
||||
|
||||
/// 官方桌面客户端使用的固定 loopback OAuth 回调地址。
|
||||
pub const OFFICIAL_REDIRECT_URI: &str = "http://127.0.0.1:56121/callback";
|
||||
|
||||
/// Official first-party OAuth scope set used by current Grok Build clients.
|
||||
/// 当前 Grok Build 客户端使用的官方第一方 OAuth scope 集合。
|
||||
pub const OFFICIAL_SCOPES: &[&str] = &[
|
||||
"openid",
|
||||
"profile",
|
||||
@@ -23,12 +23,12 @@ pub const OFFICIAL_SCOPES: &[&str] = &[
|
||||
"api:access",
|
||||
];
|
||||
|
||||
/// Exact redirect URI that has passed syntax and local allowlist checks.
|
||||
/// 已通过语法与本地 allowlist 校验的精确回调 URI。
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AllowedRedirectUri(Url);
|
||||
|
||||
impl AllowedRedirectUri {
|
||||
/// Returns the exact redirect URI sent during authorization and exchange.
|
||||
/// 返回授权与 token 交换时发送的精确回调 URI。
|
||||
#[must_use]
|
||||
pub fn as_url(&self) -> &Url {
|
||||
&self.0
|
||||
@@ -39,20 +39,19 @@ impl AllowedRedirectUri {
|
||||
}
|
||||
}
|
||||
|
||||
/// Explicit callback allowlist. Public HTTPS callbacks and dynamic loopback
|
||||
/// callbacks must be inserted before a flow can start.
|
||||
/// 显式回调 allowlist。公网 HTTPS 回调与动态 loopback 回调必须先登记才能启动流程。
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RedirectUriAllowlist {
|
||||
entries: Vec<Url>,
|
||||
}
|
||||
|
||||
impl RedirectUriAllowlist {
|
||||
/// Validates and constructs an exact redirect allowlist.
|
||||
/// 校验并构造精确回调 allowlist。
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`ConfigError::InvalidRedirectUri`] for non-HTTPS public URIs,
|
||||
/// user-info, query strings, fragments, or malformed values.
|
||||
/// 公网 URI 非 HTTPS、含 user-info、query、fragment 或格式非法时返回
|
||||
/// [`ConfigError::InvalidRedirectUri`]。
|
||||
pub fn new<I, S>(uris: I) -> Result<Self, ConfigError>
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
@@ -66,11 +65,11 @@ impl RedirectUriAllowlist {
|
||||
Ok(Self { entries })
|
||||
}
|
||||
|
||||
/// Validates a candidate and requires an exact local allowlist match.
|
||||
/// 校验候选 URI,并要求与本地 allowlist 精确匹配。
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a configuration error when the URI is invalid or absent.
|
||||
/// URI 非法或未登记时返回配置错误。
|
||||
pub fn authorize(&self, candidate: &str) -> Result<AllowedRedirectUri, ConfigError> {
|
||||
let candidate = validate_redirect_uri(candidate)?;
|
||||
if self.entries.iter().any(|entry| entry == &candidate) {
|
||||
@@ -81,36 +80,36 @@ impl RedirectUriAllowlist {
|
||||
}
|
||||
}
|
||||
|
||||
/// Immutable official Grok Build OAuth configuration.
|
||||
/// 不可变的官方 Grok Build OAuth 配置。
|
||||
#[derive(Clone)]
|
||||
pub struct GrokOAuthConfig {
|
||||
issuer: Url,
|
||||
}
|
||||
|
||||
impl GrokOAuthConfig {
|
||||
/// Builds the fixed official issuer, client, and scope configuration.
|
||||
/// 构造固定的官方 issuer、client 与 scope 配置。
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error when the fixed official issuer cannot be constructed.
|
||||
/// 固定官方 issuer 无法构造时返回错误。
|
||||
pub fn official() -> Result<Self, ConfigError> {
|
||||
let issuer = Url::parse(OFFICIAL_ISSUER).map_err(|_| ConfigError::UntrustedIssuer)?;
|
||||
Ok(Self { issuer })
|
||||
}
|
||||
|
||||
/// Returns the fixed issuer URL.
|
||||
/// 返回固定的 issuer URL。
|
||||
#[must_use]
|
||||
pub fn issuer(&self) -> &Url {
|
||||
&self.issuer
|
||||
}
|
||||
|
||||
/// Returns the official public client identifier.
|
||||
/// 返回官方 public client 标识。
|
||||
#[must_use]
|
||||
pub const fn client_id(&self) -> &'static str {
|
||||
OFFICIAL_CLIENT_ID
|
||||
}
|
||||
|
||||
/// Returns the official scope set.
|
||||
/// 返回官方 scope 集合。
|
||||
#[must_use]
|
||||
pub const fn scopes(&self) -> &'static [&'static str] {
|
||||
OFFICIAL_SCOPES
|
||||
|
||||
@@ -5,8 +5,7 @@ use crate::{GrokOAuthConfig, OAuthError, OAuthOperation, ProtocolViolation};
|
||||
|
||||
pub(crate) const MAX_OAUTH_RESPONSE_BYTES: usize = 64 * 1024;
|
||||
|
||||
/// A discovery document whose issuer and endpoints have passed the official
|
||||
/// Grok Build origin policy.
|
||||
/// issuer 与端点均已通过官方 Grok Build origin 策略校验的发现文档。
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DiscoveryDocument {
|
||||
issuer: Url,
|
||||
@@ -70,38 +69,37 @@ impl DiscoveryDocument {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the validated issuer from the discovery document.
|
||||
/// 返回发现文档中已校验的 issuer。
|
||||
#[must_use]
|
||||
pub fn issuer(&self) -> &Url {
|
||||
&self.issuer
|
||||
}
|
||||
|
||||
/// Returns the validated authorization endpoint.
|
||||
/// 返回已校验的 authorization 端点。
|
||||
#[must_use]
|
||||
pub fn authorization_endpoint(&self) -> &Url {
|
||||
&self.authorization_endpoint
|
||||
}
|
||||
|
||||
/// Returns the validated token endpoint.
|
||||
/// 返回已校验的 token 端点。
|
||||
#[must_use]
|
||||
pub fn token_endpoint(&self) -> &Url {
|
||||
&self.token_endpoint
|
||||
}
|
||||
|
||||
/// Returns the validated JWKS endpoint required by an ID-token verifier.
|
||||
/// 返回 ID token 校验器所需的已校验 JWKS 端点。
|
||||
#[must_use]
|
||||
pub fn jwks_uri(&self) -> &Url {
|
||||
&self.jwks_uri
|
||||
}
|
||||
|
||||
/// Returns the validated authoritative user-info endpoint.
|
||||
/// 返回已校验的权威 user-info 端点。
|
||||
#[must_use]
|
||||
pub fn userinfo_endpoint(&self) -> &Url {
|
||||
&self.userinfo_endpoint
|
||||
}
|
||||
|
||||
/// Returns algorithms advertised by discovery. The verifier must still
|
||||
/// enforce its own cryptographic allowlist.
|
||||
/// 返回发现文档声明的算法;校验器仍须强制自身的密码学 allowlist。
|
||||
#[must_use]
|
||||
pub fn signing_algorithms(&self) -> &[String] {
|
||||
&self.signing_algorithms
|
||||
|
||||
@@ -2,188 +2,188 @@ use thiserror::Error;
|
||||
|
||||
use crate::TransportFailureKind;
|
||||
|
||||
/// Static OAuth operation labels used for safe telemetry and classification.
|
||||
/// OAuth 操作标签,用于脱敏遥测与错误分类。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum OAuthOperation {
|
||||
/// OpenID Provider discovery.
|
||||
/// OpenID Provider 发现。
|
||||
Discovery,
|
||||
/// Authorization-code token exchange.
|
||||
/// Authorization Code 换取 token。
|
||||
AuthorizationCodeToken,
|
||||
/// Refresh-token exchange.
|
||||
/// Refresh token 换取 token。
|
||||
RefreshToken,
|
||||
/// 已有 OAuth token 的受控导入验证。
|
||||
CredentialImport,
|
||||
}
|
||||
|
||||
/// Stable OAuth error codes that are safe to expose to control-plane logic.
|
||||
/// 可安全暴露给控制面的稳定 OAuth 错误码。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum OAuthErrorCode {
|
||||
/// The human rejected authorization.
|
||||
/// 用户拒绝授权。
|
||||
AccessDenied,
|
||||
/// A code or refresh token is invalid or already consumed.
|
||||
/// code 或 refresh token 无效或已被消费。
|
||||
InvalidGrant,
|
||||
/// The configured public client is rejected.
|
||||
/// 配置的 public client 被拒绝。
|
||||
InvalidClient,
|
||||
/// The requested official scope set is rejected.
|
||||
/// 请求的官方 scope 集合被拒绝。
|
||||
InvalidScope,
|
||||
/// The authorization server is temporarily unavailable.
|
||||
/// 授权服务器暂时不可用。
|
||||
TemporarilyUnavailable,
|
||||
/// An unrecognized code whose raw server text is intentionally discarded.
|
||||
/// 无法识别的错误码;不保留服务端原文。
|
||||
Other,
|
||||
}
|
||||
|
||||
/// High-level action a coordinator may take after a failure.
|
||||
/// 失败后协调方可采取的处置类别。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum FailureClass {
|
||||
/// Safe transient failure; a later, separately coordinated attempt may run.
|
||||
/// 瞬时失败,允许后续独立协调的重试。
|
||||
Transient,
|
||||
/// Send state is ambiguous and the one-time artifact must not be replayed.
|
||||
/// 发送状态不确定,一次性凭据不得重放。
|
||||
Ambiguous,
|
||||
/// Credential material is permanently rejected at its current revision.
|
||||
/// 当前 revision 的凭据被永久拒绝。
|
||||
CredentialPermanent,
|
||||
/// Provider/client configuration is permanently rejected.
|
||||
/// Provider 或 client 配置被永久拒绝。
|
||||
ConfigurationPermanent,
|
||||
/// The human denied or must restart the interactive flow.
|
||||
/// 用户已拒绝,或需要重新发起交互流程。
|
||||
UserActionRequired,
|
||||
/// Protocol or trust-boundary validation failed closed.
|
||||
/// 协议或信任边界校验失败并 fail closed。
|
||||
Security,
|
||||
/// The official deployment does not offer the requested flow.
|
||||
/// 官方部署不提供该流程。
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
/// Configuration failures detected before any network request.
|
||||
/// 发起网络请求前检出的配置错误。
|
||||
#[derive(Debug, Error, Clone, PartialEq, Eq)]
|
||||
pub enum ConfigError {
|
||||
/// Redirect URI is malformed, insecure, or contains forbidden components.
|
||||
/// 回调 URI 格式非法、不安全或包含禁止成分。
|
||||
#[error("invalid OAuth redirect URI")]
|
||||
InvalidRedirectUri,
|
||||
/// Redirect URI was not explicitly registered in the local allowlist.
|
||||
/// 回调 URI 未登记在本地 allowlist。
|
||||
#[error("OAuth redirect URI is not allowlisted")]
|
||||
RedirectUriNotAllowlisted,
|
||||
/// Issuer differs from the official Grok Build issuer.
|
||||
/// issuer 不是官方 Grok Build issuer。
|
||||
#[error("OIDC issuer is not the official Grok Build issuer")]
|
||||
UntrustedIssuer,
|
||||
/// A discovered endpoint leaves the official issuer origin.
|
||||
/// 发现文档中的端点离开了官方 issuer origin。
|
||||
#[error("OIDC discovery returned an untrusted endpoint")]
|
||||
UntrustedEndpoint,
|
||||
/// Optional team principal metadata is empty or contains unsafe text.
|
||||
/// 可选的 team principal 元数据为空或含不安全文本。
|
||||
#[error("invalid OAuth principal metadata")]
|
||||
InvalidPrincipal,
|
||||
}
|
||||
|
||||
/// Safe, field-oriented protocol validation errors.
|
||||
/// 按字段定位的脱敏协议校验错误。
|
||||
#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ProtocolViolation {
|
||||
/// JSON could not be decoded into the expected wire schema.
|
||||
/// JSON 无法解析为预期 wire 结构。
|
||||
#[error("OAuth response is not valid JSON")]
|
||||
InvalidJson,
|
||||
/// A required wire field is absent.
|
||||
/// 缺少必需的 wire 字段。
|
||||
#[error("OAuth response is missing field `{0}`")]
|
||||
MissingField(&'static str),
|
||||
/// A wire field violates its format or size contract.
|
||||
/// wire 字段违反格式或长度约束。
|
||||
#[error("OAuth response contains invalid field `{0}`")]
|
||||
InvalidField(&'static str),
|
||||
/// A response exceeded the bounded parser limit.
|
||||
/// 响应超出解析器的体积上限。
|
||||
#[error("OAuth response exceeds the maximum body size")]
|
||||
ResponseTooLarge,
|
||||
/// The discovery document permits the insecure `none` signing algorithm.
|
||||
/// 发现文档允许不安全的 `none` 签名算法。
|
||||
#[error("OIDC discovery advertises an insecure signing algorithm")]
|
||||
InsecureSigningAlgorithm,
|
||||
}
|
||||
|
||||
/// Callback failures that never retain the authorization code or state value.
|
||||
/// 回调失败原因;不保留 code 与 state 值。
|
||||
#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CallbackRejection {
|
||||
/// Query contains repeated security-sensitive keys.
|
||||
/// query 中出现重复的安全敏感参数。
|
||||
#[error("OAuth callback contains a duplicate parameter")]
|
||||
DuplicateParameter,
|
||||
/// Provider callback omitted the one-time state.
|
||||
/// 回调缺少一次性 state。
|
||||
#[error("OAuth callback is missing state")]
|
||||
MissingState,
|
||||
/// Provider callback omitted the authorization code.
|
||||
/// 回调缺少 authorization code。
|
||||
#[error("OAuth callback is missing code")]
|
||||
MissingCode,
|
||||
/// One-time state does not match the pending flow.
|
||||
/// 一次性 state 与 pending flow 不匹配。
|
||||
#[error("OAuth callback state mismatch")]
|
||||
StateMismatch,
|
||||
/// Authorization server returned `access_denied`.
|
||||
/// 授权服务器返回 `access_denied`。
|
||||
#[error("OAuth authorization was denied")]
|
||||
AccessDenied,
|
||||
/// Authorization server returned another callback error.
|
||||
/// 授权服务器返回其他回调错误。
|
||||
#[error("OAuth authorization callback was rejected")]
|
||||
ProviderRejected,
|
||||
}
|
||||
|
||||
/// Reasons an unverified token set cannot cross the credential boundary.
|
||||
/// 未验证 token 集合无法通过凭据边界的原因。
|
||||
#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum VerificationFailure {
|
||||
/// No JWT/JWKS or authoritative user-info verifier was wired.
|
||||
/// 未注入 JWT/JWKS 或权威 user-info 校验器。
|
||||
#[error("token verification is unavailable; refusing unverified credentials")]
|
||||
Unavailable,
|
||||
/// Authorization-code response omitted its required ID token.
|
||||
/// Authorization Code 响应缺少必需的 ID token。
|
||||
#[error("authorization-code response is missing an ID token")]
|
||||
MissingIdToken,
|
||||
/// The verifier rejected signature, issuer, audience, nonce, expiry, or identity.
|
||||
/// 校验器拒绝了签名、issuer、audience、nonce、过期时间或身份。
|
||||
#[error("token verification failed")]
|
||||
Rejected,
|
||||
/// Authorization-code flow was not verified through an ID token.
|
||||
/// Authorization Code 流程未经 ID token 验证。
|
||||
#[error("authorization-code flow requires verified ID-token evidence")]
|
||||
WrongEvidence,
|
||||
}
|
||||
|
||||
/// Secret-free OAuth protocol error.
|
||||
/// 不含密钥的 OAuth 协议错误。
|
||||
#[derive(Debug, Error)]
|
||||
pub enum OAuthError {
|
||||
/// Local trust or redirect configuration is invalid.
|
||||
/// 本地信任或回调配置非法。
|
||||
#[error(transparent)]
|
||||
Configuration(#[from] ConfigError),
|
||||
/// Replaceable transport failed without retaining its raw error message.
|
||||
/// 可替换 transport 失败;不保留原始错误信息。
|
||||
#[error("OAuth transport failed during {operation:?}: {kind:?}")]
|
||||
Transport {
|
||||
/// Operation being executed.
|
||||
/// 执行中的操作。
|
||||
operation: OAuthOperation,
|
||||
/// Safe send-state classification.
|
||||
/// 脱敏的发送状态分类。
|
||||
kind: TransportFailureKind,
|
||||
},
|
||||
/// Non-success HTTP response without a recognized OAuth code.
|
||||
/// 非成功 HTTP 响应,且没有可识别的 OAuth 错误码。
|
||||
#[error("OAuth endpoint returned HTTP {status} during {operation:?}")]
|
||||
HttpStatus {
|
||||
/// Operation being executed.
|
||||
/// 执行中的操作。
|
||||
operation: OAuthOperation,
|
||||
/// HTTP status code.
|
||||
/// HTTP 状态码。
|
||||
status: u16,
|
||||
},
|
||||
/// Recognized OAuth error response.
|
||||
/// 可识别的 OAuth 错误响应。
|
||||
#[error("OAuth endpoint returned {code:?} during {operation:?}")]
|
||||
Server {
|
||||
/// Operation being executed.
|
||||
/// 执行中的操作。
|
||||
operation: OAuthOperation,
|
||||
/// HTTP status code.
|
||||
/// HTTP 状态码。
|
||||
status: u16,
|
||||
/// Stable OAuth error code.
|
||||
/// 稳定 OAuth 错误码。
|
||||
code: OAuthErrorCode,
|
||||
},
|
||||
/// Callback was rejected before token exchange.
|
||||
/// token 交换前回调即被拒绝。
|
||||
#[error(transparent)]
|
||||
Callback(#[from] CallbackRejection),
|
||||
/// Wire response violated the strict parser contract.
|
||||
/// wire 响应违反严格解析契约。
|
||||
#[error("{operation:?} protocol violation: {violation}")]
|
||||
Protocol {
|
||||
/// Operation being parsed.
|
||||
/// 解析中的操作。
|
||||
operation: OAuthOperation,
|
||||
/// Safe field-oriented reason.
|
||||
/// 按字段定位的脱敏原因。
|
||||
violation: ProtocolViolation,
|
||||
},
|
||||
/// Token set failed the mandatory verification boundary.
|
||||
/// token 集合未通过强制验证边界。
|
||||
#[error(transparent)]
|
||||
Verification(#[from] VerificationFailure),
|
||||
/// Cryptographically secure entropy was unavailable before a flow started.
|
||||
/// 流程启动前无法获得安全随机数。
|
||||
#[error("secure OAuth entropy is unavailable")]
|
||||
EntropyUnavailable,
|
||||
}
|
||||
|
||||
impl OAuthError {
|
||||
/// Returns a low-cardinality failure class for orchestration decisions.
|
||||
/// 返回供编排决策使用的低基数失败类别。
|
||||
#[must_use]
|
||||
pub fn class(&self) -> FailureClass {
|
||||
match self {
|
||||
|
||||
@@ -7,16 +7,16 @@ use zeroize::Zeroizing;
|
||||
|
||||
use crate::SecretValue;
|
||||
|
||||
/// HTTP methods needed by the OAuth protocol.
|
||||
/// OAuth 协议所需的 HTTP 方法。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum HttpMethod {
|
||||
/// Read-only discovery request.
|
||||
/// 只读的发现请求。
|
||||
Get,
|
||||
/// Form-encoded OAuth request.
|
||||
/// form 编码的 OAuth 请求。
|
||||
Post,
|
||||
}
|
||||
|
||||
/// A request header whose value is public protocol metadata.
|
||||
/// 值为公开协议元数据的请求头。
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct HttpHeader {
|
||||
name: &'static str,
|
||||
@@ -24,7 +24,7 @@ pub struct HttpHeader {
|
||||
}
|
||||
|
||||
impl HttpHeader {
|
||||
/// Creates a public request header.
|
||||
/// 创建公开请求头。
|
||||
#[must_use]
|
||||
pub fn new(name: &'static str, value: impl Into<String>) -> Self {
|
||||
Self {
|
||||
@@ -33,30 +33,30 @@ impl HttpHeader {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the header name.
|
||||
/// 返回请求头名称。
|
||||
#[must_use]
|
||||
pub fn name(&self) -> &'static str {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// Returns the public header value.
|
||||
/// 返回公开的请求头值。
|
||||
#[must_use]
|
||||
pub fn value(&self) -> &str {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
/// A public or sensitive form field value.
|
||||
/// 公开或敏感的 form 字段值。
|
||||
#[derive(Clone)]
|
||||
pub enum FormValue {
|
||||
/// Non-sensitive protocol metadata.
|
||||
/// 非敏感的协议元数据。
|
||||
Public(String),
|
||||
/// Authorization code, verifier, or token.
|
||||
/// authorization code、verifier 或 token。
|
||||
Secret(SecretValue),
|
||||
}
|
||||
|
||||
impl FormValue {
|
||||
/// Exposes a value at the HTTP encoder boundary.
|
||||
/// 在 HTTP 编码边界暴露值。
|
||||
#[must_use]
|
||||
pub fn expose(&self) -> &str {
|
||||
match self {
|
||||
@@ -75,7 +75,7 @@ impl fmt::Debug for FormValue {
|
||||
}
|
||||
}
|
||||
|
||||
/// One `application/x-www-form-urlencoded` field.
|
||||
/// 单个 `application/x-www-form-urlencoded` 字段。
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FormField {
|
||||
name: &'static str,
|
||||
@@ -83,7 +83,7 @@ pub struct FormField {
|
||||
}
|
||||
|
||||
impl FormField {
|
||||
/// Creates a public field.
|
||||
/// 创建公开字段。
|
||||
#[must_use]
|
||||
pub fn public(name: &'static str, value: impl Into<String>) -> Self {
|
||||
Self {
|
||||
@@ -92,7 +92,7 @@ impl FormField {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a sensitive field.
|
||||
/// 创建敏感字段。
|
||||
#[must_use]
|
||||
pub fn secret(name: &'static str, value: SecretValue) -> Self {
|
||||
Self {
|
||||
@@ -101,20 +101,20 @@ impl FormField {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the form field name.
|
||||
/// 返回 form 字段名。
|
||||
#[must_use]
|
||||
pub fn name(&self) -> &'static str {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// Returns the form field value.
|
||||
/// 返回 form 字段值。
|
||||
#[must_use]
|
||||
pub fn value(&self) -> &FormValue {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
/// A transport-neutral OAuth HTTP request.
|
||||
/// 与 transport 无关的 OAuth HTTP 请求。
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OAuthHttpRequest {
|
||||
method: HttpMethod,
|
||||
@@ -142,39 +142,39 @@ impl OAuthHttpRequest {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the request method.
|
||||
/// 返回请求方法。
|
||||
#[must_use]
|
||||
pub const fn method(&self) -> HttpMethod {
|
||||
self.method
|
||||
}
|
||||
|
||||
/// Returns the validated destination URL.
|
||||
/// 返回已校验的目标 URL。
|
||||
#[must_use]
|
||||
pub fn url(&self) -> &Url {
|
||||
&self.url
|
||||
}
|
||||
|
||||
/// Returns public request headers.
|
||||
/// 返回公开请求头。
|
||||
#[must_use]
|
||||
pub fn headers(&self) -> &[HttpHeader] {
|
||||
&self.headers
|
||||
}
|
||||
|
||||
/// Returns form fields. Transport implementations must not log them.
|
||||
/// 返回 form 字段。transport 实现不得记录这些值。
|
||||
#[must_use]
|
||||
pub fn form(&self) -> &[FormField] {
|
||||
&self.form
|
||||
}
|
||||
}
|
||||
|
||||
/// A response body that is zeroized and omitted from debug output.
|
||||
/// 响应体会被清零,且不出现在 debug 输出中。
|
||||
pub struct OAuthHttpResponse {
|
||||
status: u16,
|
||||
body: Zeroizing<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl OAuthHttpResponse {
|
||||
/// Constructs a response from a status code and raw body.
|
||||
/// 由状态码与原始响应体构造响应。
|
||||
#[must_use]
|
||||
pub fn new(status: u16, body: impl Into<Vec<u8>>) -> Self {
|
||||
Self {
|
||||
@@ -183,7 +183,7 @@ impl OAuthHttpResponse {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the HTTP status code.
|
||||
/// 返回 HTTP 状态码。
|
||||
#[must_use]
|
||||
pub const fn status(&self) -> u16 {
|
||||
self.status
|
||||
@@ -205,51 +205,48 @@ impl fmt::Debug for OAuthHttpResponse {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether an HTTP failure definitely occurred before sending or may have
|
||||
/// consumed a one-time authorization artifact.
|
||||
/// 区分 HTTP 失败确定发生在发送前,还是可能已消费一次性授权凭据。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TransportFailureKind {
|
||||
/// No request bytes were sent, so a coordinator may safely retry.
|
||||
/// 未发送任何请求字节,协调方可安全重试。
|
||||
NotSent,
|
||||
/// The server may have consumed a code or rotating refresh token.
|
||||
/// 服务端可能已消费 code 或轮换的 refresh token。
|
||||
Ambiguous,
|
||||
/// A timeout occurred and send state is unknown.
|
||||
/// 发生超时,发送状态未知。
|
||||
Timeout,
|
||||
/// TLS establishment or validation failed before an accepted response.
|
||||
/// 在收到有效响应前 TLS 建立或校验失败。
|
||||
Tls,
|
||||
}
|
||||
|
||||
/// A deliberately low-cardinality, secret-free transport error.
|
||||
/// 有意保持低基数且不含密钥的 transport 错误。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct TransportFailure {
|
||||
kind: TransportFailureKind,
|
||||
}
|
||||
|
||||
impl TransportFailure {
|
||||
/// Creates a transport failure without retaining a potentially sensitive
|
||||
/// third-party error message.
|
||||
/// 创建 transport 失败,不保留可能敏感的第三方错误信息。
|
||||
#[must_use]
|
||||
pub const fn new(kind: TransportFailureKind) -> Self {
|
||||
Self { kind }
|
||||
}
|
||||
|
||||
/// Returns the send-state classification.
|
||||
/// 返回发送状态分类。
|
||||
#[must_use]
|
||||
pub const fn kind(self) -> TransportFailureKind {
|
||||
self.kind
|
||||
}
|
||||
}
|
||||
|
||||
/// Future returned by an OAuth HTTP transport.
|
||||
/// OAuth HTTP transport 返回的 Future。
|
||||
pub type TransportFuture<'a> =
|
||||
Pin<Box<dyn Future<Output = Result<OAuthHttpResponse, TransportFailure>> + Send + 'a>>;
|
||||
|
||||
/// Replaceable HTTP boundary for OAuth protocol requests.
|
||||
/// OAuth 协议请求的可替换 HTTP 边界。
|
||||
///
|
||||
/// Implementations must enforce TLS, bounded response bodies, and proxy/IP
|
||||
/// affinity outside this crate. They must never log form values or response
|
||||
/// bodies.
|
||||
/// 实现方需在本 crate 之外保证 TLS、有界响应体与 proxy/IP 亲和性,
|
||||
/// 且不得记录 form 值或响应体。
|
||||
pub trait OAuthHttpTransport: Send + Sync {
|
||||
/// Executes one request without hidden retries.
|
||||
/// 执行一次请求,不做隐式重试。
|
||||
fn execute(&self, request: OAuthHttpRequest) -> TransportFuture<'_>;
|
||||
}
|
||||
|
||||
@@ -43,12 +43,8 @@ pub struct ReqwestOidcTokenVerifier {
|
||||
}
|
||||
|
||||
impl ReqwestOidcTokenVerifier {
|
||||
/// 构建只允许访问 `auth.x.ai` 官方验证端点的 verifier。
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// TLS client 初始化失败时返回 transport build error。
|
||||
/// 构建使用显式 endpoint policy 与 JWKS cache TTL 的 verifier。
|
||||
/// 构建 verifier;使用显式 endpoint policy 与 JWKS cache TTL,只允许访问
|
||||
/// `auth.x.ai` 官方验证端点。
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
|
||||
@@ -2,19 +2,18 @@ use std::fmt;
|
||||
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
/// An in-memory secret whose debug output is always redacted and whose buffer
|
||||
/// is zeroized on drop.
|
||||
/// 内存中的 secret;debug 输出恒为脱敏,缓冲区在 drop 时清零。
|
||||
#[derive(Clone)]
|
||||
pub struct SecretValue(Zeroizing<String>);
|
||||
|
||||
impl SecretValue {
|
||||
/// Wraps an owned secret value.
|
||||
/// 包装持有所有权的 secret 值。
|
||||
#[must_use]
|
||||
pub fn new(value: impl Into<String>) -> Self {
|
||||
Self(Zeroizing::new(value.into()))
|
||||
}
|
||||
|
||||
/// Exposes the secret only at an explicit protocol or transport boundary.
|
||||
/// 仅在明确的协议或 transport 边界暴露 secret。
|
||||
#[must_use]
|
||||
pub fn expose(&self) -> &str {
|
||||
self.0.as_str()
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
const MAX_TOKEN_BYTES: usize = 64 * 1024;
|
||||
const MAX_TOKEN_LIFETIME_SECONDS: u64 = 366 * 24 * 60 * 60;
|
||||
|
||||
/// Optional team principal fields sent during refresh.
|
||||
/// refresh 请求可选携带的 team principal 字段。
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct OAuthPrincipal {
|
||||
principal_type: String,
|
||||
@@ -30,12 +30,12 @@ impl fmt::Debug for OAuthPrincipal {
|
||||
}
|
||||
|
||||
impl OAuthPrincipal {
|
||||
/// Creates validated principal metadata.
|
||||
/// 创建校验过的 principal 元数据。
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`ConfigError::InvalidPrincipal`] for empty, oversized, or
|
||||
/// control-character-bearing values.
|
||||
/// 值为空、超长或含控制字符时返回
|
||||
/// [`ConfigError::InvalidPrincipal`]。
|
||||
pub fn new(
|
||||
principal_type: impl Into<String>,
|
||||
principal_id: impl Into<String>,
|
||||
@@ -52,27 +52,27 @@ impl OAuthPrincipal {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the official principal type value.
|
||||
/// 返回官方 principal 类型值。
|
||||
#[must_use]
|
||||
pub fn principal_type(&self) -> &str {
|
||||
&self.principal_type
|
||||
}
|
||||
|
||||
/// Returns the official principal identifier.
|
||||
/// 返回官方 principal 标识。
|
||||
#[must_use]
|
||||
pub fn principal_id(&self) -> &str {
|
||||
&self.principal_id
|
||||
}
|
||||
}
|
||||
|
||||
/// Input for one refresh-token exchange.
|
||||
/// 单次 refresh token 交换的输入。
|
||||
#[derive(Clone)]
|
||||
pub struct RefreshTokenGrant {
|
||||
refresh_token: SecretValue,
|
||||
}
|
||||
|
||||
impl RefreshTokenGrant {
|
||||
/// Creates a refresh grant for an existing verified credential.
|
||||
/// 为已验证的现有凭据创建 refresh grant。
|
||||
#[must_use]
|
||||
pub fn new(refresh_token: SecretValue) -> Self {
|
||||
Self { refresh_token }
|
||||
@@ -92,7 +92,7 @@ impl fmt::Debug for RefreshTokenGrant {
|
||||
}
|
||||
}
|
||||
|
||||
/// Initial token set that crossed the mandatory verification boundary.
|
||||
/// 已通过强制验证边界的初始 token set。
|
||||
#[derive(Clone)]
|
||||
pub struct VerifiedTokenSet {
|
||||
access_token: SecretValue,
|
||||
@@ -119,13 +119,13 @@ impl VerifiedTokenSet {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the verified access token.
|
||||
/// 返回已验证的 access token。
|
||||
#[must_use]
|
||||
pub fn access_token(&self) -> &SecretValue {
|
||||
&self.access_token
|
||||
}
|
||||
|
||||
/// Returns the optional refresh token.
|
||||
/// 返回可选的 refresh token。
|
||||
#[must_use]
|
||||
pub fn refresh_token(&self) -> Option<&SecretValue> {
|
||||
self.refresh_token.as_ref()
|
||||
@@ -143,13 +143,13 @@ impl VerifiedTokenSet {
|
||||
&self.scope
|
||||
}
|
||||
|
||||
/// Returns the server-provided lifetime.
|
||||
/// 返回服务端下发的有效期。
|
||||
#[must_use]
|
||||
pub const fn expires_in(&self) -> Option<Duration> {
|
||||
self.expires_in
|
||||
}
|
||||
|
||||
/// Returns trusted identity verification evidence.
|
||||
/// 返回可信身份验证证据。
|
||||
#[must_use]
|
||||
pub const fn evidence(&self) -> &VerificationEvidence {
|
||||
&self.evidence
|
||||
@@ -173,8 +173,8 @@ impl fmt::Debug for VerifiedTokenSet {
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of refreshing an already verified credential. An omitted refresh
|
||||
/// token means the caller must retain the existing one under revision CAS.
|
||||
/// 刷新已验证凭据的结果。未返回 refresh token 时,调用方须在 revision CAS 下
|
||||
/// 保留原有 refresh token。
|
||||
#[derive(Clone)]
|
||||
pub struct RefreshedTokenSet {
|
||||
access_token: SecretValue,
|
||||
@@ -183,19 +183,19 @@ pub struct RefreshedTokenSet {
|
||||
}
|
||||
|
||||
impl RefreshedTokenSet {
|
||||
/// Returns the replacement access token.
|
||||
/// 返回替换后的 access token。
|
||||
#[must_use]
|
||||
pub fn access_token(&self) -> &SecretValue {
|
||||
&self.access_token
|
||||
}
|
||||
|
||||
/// Returns a rotated refresh token when the server supplied one.
|
||||
/// 服务端下发轮换 refresh token 时返回它。
|
||||
#[must_use]
|
||||
pub fn rotated_refresh_token(&self) -> Option<&SecretValue> {
|
||||
self.rotated_refresh_token.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the replacement access-token lifetime.
|
||||
/// 返回替换后 access token 的有效期。
|
||||
#[must_use]
|
||||
pub const fn expires_in(&self) -> Option<Duration> {
|
||||
self.expires_in
|
||||
|
||||
@@ -7,10 +7,10 @@ use url::Url;
|
||||
|
||||
use crate::{SecretValue, VerificationFailure};
|
||||
|
||||
/// OAuth flow whose initial access token is being verified.
|
||||
/// 正在验证首个 access token 的 OAuth 流程。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum VerificationFlow {
|
||||
/// Authorization Code + PKCE with a mandatory nonce-bound ID token.
|
||||
/// Authorization Code + PKCE;必须携带与 nonce 绑定的 ID token。
|
||||
AuthorizationCode,
|
||||
/// 已有 token 的导入;必须通过官方 user-info 验证当前 access token。
|
||||
CredentialImport,
|
||||
@@ -18,17 +18,17 @@ pub enum VerificationFlow {
|
||||
CredentialImportRefreshed,
|
||||
}
|
||||
|
||||
/// Trusted verification mechanism reported by the injected verifier.
|
||||
/// 注入的校验器报告的可信验证机制。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum VerificationMethod {
|
||||
/// Full JWT/JWKS validation including issuer, audience, expiry, and nonce.
|
||||
/// 完整 JWT/JWKS 校验,含 issuer、audience、过期时间与 nonce。
|
||||
IdToken,
|
||||
/// Authoritative official user-info lookup using the access token.
|
||||
/// 用 access token 查询官方权威 user-info。
|
||||
UserInfo,
|
||||
}
|
||||
|
||||
/// Evidence produced by a trusted verifier. Identity is redacted from debug
|
||||
/// output and is not derived by this crate through unverified JWT decoding.
|
||||
/// 可信校验器产出的验证证据。身份在 debug 输出中脱敏,且本 crate 不会通过
|
||||
/// 未经验证的 JWT 解码推导身份。
|
||||
#[derive(Clone)]
|
||||
pub struct VerificationEvidence {
|
||||
method: VerificationMethod,
|
||||
@@ -36,7 +36,7 @@ pub struct VerificationEvidence {
|
||||
}
|
||||
|
||||
impl VerificationEvidence {
|
||||
/// Creates evidence after full ID-token verification.
|
||||
/// 完整 ID token 校验通过后创建证据。
|
||||
#[must_use]
|
||||
pub fn id_token(subject: String) -> Self {
|
||||
Self {
|
||||
@@ -45,7 +45,7 @@ impl VerificationEvidence {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates evidence after an authoritative official user-info lookup.
|
||||
/// 官方权威 user-info 查询通过后创建证据。
|
||||
#[must_use]
|
||||
pub fn user_info(subject: String) -> Self {
|
||||
Self {
|
||||
@@ -54,13 +54,13 @@ impl VerificationEvidence {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the verification method.
|
||||
/// 返回验证机制。
|
||||
#[must_use]
|
||||
pub const fn method(&self) -> VerificationMethod {
|
||||
self.method
|
||||
}
|
||||
|
||||
/// Exposes the verified subject at the credential construction boundary.
|
||||
/// 在凭据构造边界暴露已验证的 subject。
|
||||
#[must_use]
|
||||
pub fn subject(&self) -> &str {
|
||||
self.subject.expose()
|
||||
@@ -77,7 +77,7 @@ impl fmt::Debug for VerificationEvidence {
|
||||
}
|
||||
}
|
||||
|
||||
/// Immutable trust context supplied to a token verifier.
|
||||
/// 传给 token 校验器的不可变信任上下文。
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct TokenVerificationContext<'a> {
|
||||
flow: VerificationFlow,
|
||||
@@ -110,51 +110,51 @@ impl<'a> TokenVerificationContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the flow being verified.
|
||||
/// 返回正在验证的流程。
|
||||
#[must_use]
|
||||
pub const fn flow(&self) -> VerificationFlow {
|
||||
self.flow
|
||||
}
|
||||
|
||||
/// Returns the exact expected issuer.
|
||||
/// 返回精确匹配的预期 issuer。
|
||||
#[must_use]
|
||||
pub fn issuer(&self) -> &Url {
|
||||
self.issuer
|
||||
}
|
||||
|
||||
/// Returns the exact expected audience/client identifier.
|
||||
/// 返回精确匹配的预期 audience/client 标识。
|
||||
#[must_use]
|
||||
pub const fn client_id(&self) -> &str {
|
||||
self.client_id
|
||||
}
|
||||
|
||||
/// Returns the same-origin JWKS URL from validated discovery.
|
||||
/// 返回已校验发现文档中的同源 JWKS URL。
|
||||
#[must_use]
|
||||
pub fn jwks_uri(&self) -> &Url {
|
||||
self.jwks_uri
|
||||
}
|
||||
|
||||
/// Returns the same-origin authoritative user-info URL from discovery.
|
||||
/// 返回发现文档中的同源权威 user-info URL。
|
||||
#[must_use]
|
||||
pub fn userinfo_endpoint(&self) -> &Url {
|
||||
self.userinfo_endpoint
|
||||
}
|
||||
|
||||
/// Returns discovery-advertised algorithms. Implementations must intersect
|
||||
/// them with a local cryptographic allowlist and reject `none`.
|
||||
/// 返回发现文档声明的算法。实现必须与本地密码学 allowlist 取交集,并拒绝
|
||||
/// `none`。
|
||||
#[must_use]
|
||||
pub const fn signing_algorithms(&self) -> &[String] {
|
||||
self.signing_algorithms
|
||||
}
|
||||
|
||||
/// Returns the expected nonce for authorization-code flows.
|
||||
/// 返回 Authorization Code 流程的预期 nonce。
|
||||
#[must_use]
|
||||
pub const fn expected_nonce(&self) -> Option<&SecretValue> {
|
||||
self.expected_nonce
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrowed token material available only to the trusted verification port.
|
||||
/// 仅向可信校验端口开放的借用 token 材料。
|
||||
pub struct TokenCandidate<'a> {
|
||||
access_token: &'a SecretValue,
|
||||
id_token: Option<&'a SecretValue>,
|
||||
@@ -174,19 +174,19 @@ impl<'a> TokenCandidate<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the access token for authoritative user-info verification.
|
||||
/// 返回用于权威 user-info 验证的 access token。
|
||||
#[must_use]
|
||||
pub const fn access_token(&self) -> &SecretValue {
|
||||
self.access_token
|
||||
}
|
||||
|
||||
/// Returns the ID token for full JWT/JWKS validation, when provided.
|
||||
/// 返回用于完整 JWT/JWKS 校验的 ID token(若存在)。
|
||||
#[must_use]
|
||||
pub const fn id_token(&self) -> Option<&SecretValue> {
|
||||
self.id_token
|
||||
}
|
||||
|
||||
/// Returns the server-provided lifetime.
|
||||
/// 返回服务端给出的有效期。
|
||||
#[must_use]
|
||||
pub const fn expires_in(&self) -> Option<Duration> {
|
||||
self.expires_in
|
||||
@@ -204,14 +204,14 @@ impl fmt::Debug for TokenCandidate<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Future returned by a token verification port.
|
||||
/// token 校验端口返回的 future。
|
||||
pub type VerificationFuture<'a> =
|
||||
Pin<Box<dyn Future<Output = Result<VerificationEvidence, VerificationFailure>> + Send + 'a>>;
|
||||
|
||||
/// Trust boundary for full JWT/JWKS or authoritative user-info validation.
|
||||
/// 完整 JWT/JWKS 或权威 user-info 校验的信任边界。
|
||||
pub trait TokenVerifier: Send + Sync {
|
||||
/// Verifies an initial token set. Implementations must never base64-decode
|
||||
/// JWT claims without signature and claim validation.
|
||||
/// 验证初始 token 集合。实现不得在未做签名与 claim 校验的情况下 base64 解码
|
||||
/// JWT claims。
|
||||
fn verify<'a>(
|
||||
&'a self,
|
||||
context: TokenVerificationContext<'a>,
|
||||
@@ -219,7 +219,7 @@ pub trait TokenVerifier: Send + Sync {
|
||||
) -> VerificationFuture<'a>;
|
||||
}
|
||||
|
||||
/// Default verifier that makes incomplete wiring fail closed.
|
||||
/// 默认校验器,未完成接线时 fail closed。
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FailClosedTokenVerifier;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Official Grok Build OAuth Provider boundary.
|
||||
//! 官方 Grok Build OAuth Provider 边界。
|
||||
|
||||
mod admin;
|
||||
pub mod config;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `gateway-core` Provider adapter for official Grok Build sessions.
|
||||
//! 官方 Grok Build 会话的 `gateway-core` Provider adapter。
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -70,12 +70,10 @@ const MIN_REASONING_CIPHERTEXT_ENTROPY: f64 = 0.85;
|
||||
const REASONING_DECODE_FAILED_CODE: &str = "reasoning_decode_failed";
|
||||
const RESPONSE_NOT_FOUND_CODE: &str = "not_found";
|
||||
|
||||
/// Official Grok Build provider with injected session selection and HTTP SSE
|
||||
/// transport ports.
|
||||
/// 官方 Grok Build Provider;会话选择与 HTTP SSE transport 均由外部注入。
|
||||
///
|
||||
/// Each call selects exactly one OAuth session and prepares exactly one visible
|
||||
/// upstream POST. Retries, credential rotation, endpoint fallback, and public
|
||||
/// xAI API-key inference are deliberately outside this adapter.
|
||||
/// 每次调用只选择一个 OAuth 会话,只准备一次可见的上游 POST。重试、凭据轮换、
|
||||
/// endpoint fallback 以及公开 xAI API key 推理都不在该 adapter 内。
|
||||
pub struct GrokBuildProvider {
|
||||
selector: Arc<dyn GrokSessionSelector>,
|
||||
transport: Arc<dyn GrokInferenceTransport>,
|
||||
@@ -88,7 +86,7 @@ pub struct GrokBuildProvider {
|
||||
}
|
||||
|
||||
impl GrokBuildProvider {
|
||||
/// Creates a provider over explicit session and transport boundaries.
|
||||
/// 在显式的会话与 transport 边界上创建 Provider。
|
||||
pub fn new(
|
||||
selector: Arc<dyn GrokSessionSelector>,
|
||||
transport: Arc<dyn GrokInferenceTransport>,
|
||||
@@ -380,9 +378,8 @@ fn tool_support(evidence: GrokCatalogCapabilityEvidence) -> SupportLevel {
|
||||
match evidence {
|
||||
GrokCatalogCapabilityEvidence::DeclaredNative => SupportLevel::Native,
|
||||
GrokCatalogCapabilityEvidence::DeclaredUnsupported => SupportLevel::Unsupported,
|
||||
// Grok Build's Responses tool protocol remains available when the
|
||||
// optional catalog field is omitted; the request adapter normalizes
|
||||
// the client-only tool shapes before sending.
|
||||
// catalog 省略该可选字段时,Grok Build 的 Responses 工具协议仍可用;
|
||||
// 请求 adapter 会在发送前规范化仅客户端侧的工具结构。
|
||||
GrokCatalogCapabilityEvidence::Unknown => SupportLevel::Emulated,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Official Grok Responses SSE to gateway canonical events.
|
||||
//! 官方 Grok Responses SSE 到网关 canonical 事件的转换。
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
@@ -128,7 +128,7 @@ fn usd_price_per_million(per_token_ticks: u128) -> Option<Money> {
|
||||
usd_money(per_token_ticks.checked_mul(1_000_000)?)
|
||||
}
|
||||
|
||||
/// Incremental decoder for one official Grok Responses attempt.
|
||||
/// 单次官方 Grok Responses 尝试的增量解码器。
|
||||
///
|
||||
/// 每个上游 event 同时保留 OpenAI wire,并在可识别时附加 canonical facts。
|
||||
pub struct GrokCanonicalDecoder {
|
||||
@@ -158,8 +158,8 @@ impl GrokCanonicalDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a decoder that restores request-scoped tool aliases before the
|
||||
/// canonical and wire projections observe each upstream event.
|
||||
/// 创建在 canonical 与 wire 投影处理每个上游 event 前先还原请求级 tool 别名的
|
||||
/// 解码器。
|
||||
#[must_use]
|
||||
pub fn for_request(fallback_model: impl Into<String>, request: &GrokResponsesRequest) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -356,7 +356,7 @@ impl GrokBillingTransportError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Billing transport future。
|
||||
/// 计费 transport 的 future。
|
||||
pub type GrokBillingTransportFuture<'a> = Pin<
|
||||
Box<
|
||||
dyn Future<Output = Result<GrokBillingTransportResponse, GrokBillingTransportError>>
|
||||
|
||||
@@ -35,17 +35,17 @@ impl fmt::Debug for GrokClientIdentity {
|
||||
}
|
||||
}
|
||||
|
||||
/// Public or sensitive official Grok request header value.
|
||||
/// 官方 Grok 请求头的公开或敏感取值。
|
||||
#[derive(Clone)]
|
||||
pub enum GrokHeaderValue {
|
||||
/// Non-sensitive protocol metadata.
|
||||
/// 非敏感的协议元数据。
|
||||
Public(String),
|
||||
/// OAuth, identity, or session value that must be redacted.
|
||||
/// 必须脱敏的 OAuth、身份或会话值。
|
||||
Sensitive(SecretValue),
|
||||
}
|
||||
|
||||
impl GrokHeaderValue {
|
||||
/// Exposes a value only at the injected HTTP transport boundary.
|
||||
/// 仅在注入的 HTTP transport 边界处暴露取值。
|
||||
#[must_use]
|
||||
pub fn expose(&self) -> &str {
|
||||
match self {
|
||||
@@ -64,7 +64,7 @@ impl fmt::Debug for GrokHeaderValue {
|
||||
}
|
||||
}
|
||||
|
||||
/// One official Grok CLI proxy request header.
|
||||
/// 一个官方 Grok CLI 代理请求头。
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GrokHeader {
|
||||
name: &'static str,
|
||||
@@ -72,13 +72,13 @@ pub struct GrokHeader {
|
||||
}
|
||||
|
||||
impl GrokHeader {
|
||||
/// Returns the static header name.
|
||||
/// 返回静态 header 名。
|
||||
#[must_use]
|
||||
pub const fn name(&self) -> &'static str {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// Returns the typed header value.
|
||||
/// 返回类型化的 header 值。
|
||||
#[must_use]
|
||||
pub const fn value(&self) -> &GrokHeaderValue {
|
||||
&self.value
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Grok Build CLI Responses provider boundary.
|
||||
//! Grok Build CLI Responses provider 边界。
|
||||
|
||||
pub(crate) mod canonical;
|
||||
pub(crate) mod catalog;
|
||||
|
||||
@@ -159,7 +159,7 @@ impl GrokDnsResolutionPolicy {
|
||||
#[error("Grok official DNS resolution was rejected")]
|
||||
pub struct GrokDnsResolutionError;
|
||||
|
||||
/// HTTP client construction and endpoint validation are one injected security boundary.
|
||||
/// HTTP client 构造与端点校验合为一个可注入的安全边界。
|
||||
pub trait GrokEndpointPolicy: fmt::Debug + Send + Sync {
|
||||
fn build_oauth_client(
|
||||
&self,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Grok CLI runtime wire profile.
|
||||
//! Grok CLI 运行时 wire profile。
|
||||
|
||||
use std::{
|
||||
sync::{Arc, RwLock},
|
||||
|
||||
@@ -270,17 +270,16 @@ impl fmt::Debug for GrokResponsesRequest {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate-to-Responses encoding error that never retains option or prompt
|
||||
/// values.
|
||||
/// Generate 到 Responses 的编码错误,不保留 option 与 prompt 值。
|
||||
#[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GrokRequestEncodeError {
|
||||
/// 数据面只接受 OpenAI adapter 保留的原始 Responses object。
|
||||
#[error("Grok Build request is missing its OpenAI protocol payload")]
|
||||
InvalidProtocolPayload,
|
||||
/// JSON serialization unexpectedly failed.
|
||||
/// JSON 序列化意外失败。
|
||||
#[error("Grok Build request serialization failed")]
|
||||
Serialization,
|
||||
/// Responses compatibility fields could not be normalized safely.
|
||||
/// Responses 兼容字段无法安全归一化。
|
||||
#[error("Grok Build request normalization failed")]
|
||||
InvalidRequestNormalization,
|
||||
/// 请求中的具体字段无法安全转换为 Grok Build 接受的形态。
|
||||
|
||||
@@ -13,17 +13,16 @@ use sha2::{Digest as _, Sha256};
|
||||
|
||||
use crate::SecretValue;
|
||||
|
||||
/// Opaque, pseudonymous egress/session key understood by the injected
|
||||
/// inference transport.
|
||||
/// 注入的推理 transport 可识别的不透明假名化 egress/session 键。
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct GrokSessionBinding(String);
|
||||
|
||||
impl GrokSessionBinding {
|
||||
/// Creates a bounded, non-secret binding reference.
|
||||
/// 创建有界的非敏感绑定引用。
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Rejects empty, oversized, control-character, or reserved values.
|
||||
/// 值为空、超长、含控制字符或使用保留前缀时返回错误。
|
||||
pub fn new(value: impl Into<String>) -> Result<Self, GrokSessionDataError> {
|
||||
let value = value.into();
|
||||
if value.is_empty()
|
||||
@@ -36,7 +35,7 @@ impl GrokSessionBinding {
|
||||
Ok(Self(value))
|
||||
}
|
||||
|
||||
/// Returns the pseudonymous transport lookup key.
|
||||
/// 返回假名化的 transport 查找键。
|
||||
#[must_use]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
@@ -75,12 +74,12 @@ impl fmt::Debug for GrokSessionAffinityKey {
|
||||
}
|
||||
}
|
||||
|
||||
/// Selector-owned guard for credential, capacity, and egress affinity.
|
||||
/// 由 selector 持有的凭据、容量与 egress 亲和守卫。
|
||||
pub trait GrokSessionLeaseGuard: Send + Sync + 'static {}
|
||||
|
||||
impl<T> GrokSessionLeaseGuard for T where T: Send + Sync + 'static {}
|
||||
|
||||
/// One selected OAuth session and its owned live lease.
|
||||
/// 一个选中的 OAuth 会话及其持有的活跃 lease。
|
||||
pub struct SelectedGrokSession {
|
||||
account_id: ProviderAccountId,
|
||||
credential_revision: CredentialRevision,
|
||||
@@ -92,11 +91,11 @@ pub struct SelectedGrokSession {
|
||||
}
|
||||
|
||||
impl SelectedGrokSession {
|
||||
/// Constructs a selected session from Provider-owned plaintext OAuth material.
|
||||
/// 从 Provider 持有的明文 OAuth 材料构造选中会话。
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Rejects a credential-less snapshot or malformed auth/header values.
|
||||
/// 快照缺少凭据,或 auth/header 值格式非法时返回错误。
|
||||
pub fn new(
|
||||
account_id: ProviderAccountId,
|
||||
credential_revision: CredentialRevision,
|
||||
@@ -125,19 +124,19 @@ impl SelectedGrokSession {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the selected account ID.
|
||||
/// 返回选中的账号 ID。
|
||||
#[must_use]
|
||||
pub const fn account_id(&self) -> &ProviderAccountId {
|
||||
&self.account_id
|
||||
}
|
||||
|
||||
/// Returns the selector-frozen credential revision.
|
||||
/// 返回 selector 冻结的凭据 revision。
|
||||
#[must_use]
|
||||
pub const fn credential_revision(&self) -> CredentialRevision {
|
||||
self.credential_revision
|
||||
}
|
||||
|
||||
/// Returns metadata recorded by Core for this upstream call.
|
||||
/// 返回 Core 为本次上游调用记录的元数据。
|
||||
#[must_use]
|
||||
pub fn resource(&self) -> ProviderResource {
|
||||
ProviderResource::Account {
|
||||
@@ -146,25 +145,25 @@ impl SelectedGrokSession {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the OAuth access token for explicit header construction.
|
||||
/// 返回用于显式构造 header 的 OAuth access token。
|
||||
#[must_use]
|
||||
pub const fn access_token(&self) -> &SecretValue {
|
||||
&self.access_token
|
||||
}
|
||||
|
||||
/// Returns the verified user ID for official proxy headers.
|
||||
/// 返回用于官方代理 header 的已验证 user ID。
|
||||
#[must_use]
|
||||
pub const fn user_id(&self) -> &SecretValue {
|
||||
&self.user_id
|
||||
}
|
||||
|
||||
/// Returns the optional verified email for official proxy headers.
|
||||
/// 返回用于官方代理 header 的可选已验证 email。
|
||||
#[must_use]
|
||||
pub const fn email(&self) -> Option<&SecretValue> {
|
||||
self.email.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the pseudonymous egress/session transport binding.
|
||||
/// 返回假名化的 egress/session transport 绑定。
|
||||
#[must_use]
|
||||
pub const fn binding(&self) -> &GrokSessionBinding {
|
||||
&self.binding
|
||||
@@ -186,7 +185,7 @@ impl fmt::Debug for SelectedGrokSession {
|
||||
}
|
||||
}
|
||||
|
||||
/// Input to one selector call. It owns a frozen, secret-free attempt view.
|
||||
/// 单次 selector 调用的入参,持有冻结且不含密钥的尝试视图。
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GrokSessionSelection {
|
||||
upstream_model: UpstreamModelId,
|
||||
@@ -198,7 +197,7 @@ pub struct GrokSessionSelection {
|
||||
}
|
||||
|
||||
impl GrokSessionSelection {
|
||||
/// Creates the immutable selection request.
|
||||
/// 创建不可变的选择请求。
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
upstream_model: UpstreamModelId,
|
||||
@@ -224,25 +223,25 @@ impl GrokSessionSelection {
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the frozen upstream model.
|
||||
/// 返回冻结的上游模型。
|
||||
#[must_use]
|
||||
pub const fn upstream_model(&self) -> &UpstreamModelId {
|
||||
&self.upstream_model
|
||||
}
|
||||
|
||||
/// Returns accounts already attempted by the coordinator.
|
||||
/// 返回协调方已尝试过的账号。
|
||||
#[must_use]
|
||||
pub const fn excluded_accounts(&self) -> &BTreeSet<ProviderAccountId> {
|
||||
&self.excluded_accounts
|
||||
}
|
||||
|
||||
/// Returns the sole account permitted for this call, when constrained by Core.
|
||||
/// 返回 Core 约束下本次调用唯一允许的账号。
|
||||
#[must_use]
|
||||
pub const fn required_account(&self) -> Option<&ProviderAccountId> {
|
||||
self.required_account.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the frozen global account scheduling policy.
|
||||
/// 返回冻结的全局账号调度策略。
|
||||
#[must_use]
|
||||
pub const fn account_selection_policy(&self) -> AccountSelectionPolicy {
|
||||
self.account_selection_policy
|
||||
@@ -254,44 +253,44 @@ impl GrokSessionSelection {
|
||||
self.affinity.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the absolute deadline that bounds the scheduling lease.
|
||||
/// 返回限定调度租约的绝对截止时间。
|
||||
#[must_use]
|
||||
pub const fn deadline(&self) -> SystemTime {
|
||||
self.deadline
|
||||
}
|
||||
}
|
||||
|
||||
/// Future returned by a Grok session selector.
|
||||
/// Grok session selector 返回的 future。
|
||||
pub type GrokSessionSelectorFuture<'a> = Pin<
|
||||
Box<dyn Future<Output = Result<SelectedGrokSession, GrokSessionSelectorError>> + Send + 'a>,
|
||||
>;
|
||||
|
||||
/// Credential-scoped upstream failure safe to persist as availability feedback.
|
||||
/// 凭据维度的上游失败,可安全落库为可用性反馈。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GrokCredentialFailure {
|
||||
/// The selected OAuth token was rejected.
|
||||
/// 选中的 OAuth token 被拒绝。
|
||||
Unauthorized,
|
||||
/// The selected OAuth account was rate limited.
|
||||
/// 选中的 OAuth 账号被限流。
|
||||
RateLimited {
|
||||
/// Already parsed and redacted delay; no raw upstream header is retained.
|
||||
/// 已解析并脱敏的延迟;不保留上游原始 header。
|
||||
retry_after: Option<Duration>,
|
||||
},
|
||||
/// The selected OAuth account exhausted its quota.
|
||||
/// 选中的 OAuth 账号配额耗尽。
|
||||
QuotaExhausted,
|
||||
/// A successful SSE response ended before a terminal Responses event.
|
||||
/// 成功的 SSE 响应在终止 Responses 事件前结束。
|
||||
StreamInterrupted,
|
||||
}
|
||||
|
||||
/// Future returned after one best-effort credential feedback write.
|
||||
/// 一次 best-effort 凭据反馈写入返回的 future。
|
||||
pub type GrokCredentialFeedbackFuture<'a> = Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
|
||||
|
||||
/// Runtime port that selects exactly one eligible OAuth session and acquires
|
||||
/// all credential/capacity/egress leases required for the stream lifetime.
|
||||
/// 运行时端口:选出恰好一个可用 OAuth 会话,并获取流生命周期所需的全部
|
||||
/// 凭据、容量与出口租约。
|
||||
pub trait GrokSessionSelector: Send + Sync {
|
||||
/// Performs one selection without internal provider fallback.
|
||||
/// 执行一次选择,不做 Provider 内部回退。
|
||||
fn select(&self, request: GrokSessionSelection) -> GrokSessionSelectorFuture<'_>;
|
||||
|
||||
/// Persists one classified failure without retrying or replacing the error.
|
||||
/// 持久化一条已分类的失败,不重试也不替换原错误。
|
||||
fn record_failure<'a>(
|
||||
&'a self,
|
||||
session: &'a SelectedGrokSession,
|
||||
@@ -299,39 +298,39 @@ pub trait GrokSessionSelector: Send + Sync {
|
||||
) -> GrokCredentialFeedbackFuture<'a>;
|
||||
}
|
||||
|
||||
/// Secret-free selector failure.
|
||||
/// 不含密钥的选择器失败。
|
||||
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||
pub enum GrokSessionSelectorError {
|
||||
/// No session satisfies model, state, and exclusion constraints.
|
||||
/// 没有会话同时满足模型、状态与排除约束。
|
||||
#[error("no eligible Grok Build session is available")]
|
||||
NoEligibleSession,
|
||||
/// All eligible sessions are currently leased or rate-spaced.
|
||||
/// 所有可用会话当前均被租出或处于限速间隔。
|
||||
#[error("Grok Build session capacity is unavailable")]
|
||||
CapacityUnavailable {
|
||||
/// Optional selector-derived delay.
|
||||
/// 选择器推导出的可选延迟。
|
||||
retry_after: Option<Duration>,
|
||||
},
|
||||
/// Every eligible session is inside its runtime cooldown window.
|
||||
/// 每个可用会话都处于运行时冷却窗口内。
|
||||
#[error("Grok Build account is cooling down")]
|
||||
AccountCoolingDown {
|
||||
/// Remaining cooldown of the earliest recovering session.
|
||||
/// 最早恢复的会话剩余冷却时长。
|
||||
retry_after: Option<Duration>,
|
||||
},
|
||||
/// Session metadata or Provider-owned plaintext secret is invalid.
|
||||
/// 会话元数据或 Provider 持有的明文密钥非法。
|
||||
#[error("Grok Build session data is invalid")]
|
||||
InvalidSession,
|
||||
/// Selector backing service is unavailable.
|
||||
/// 选择器依赖的后端服务不可用。
|
||||
#[error("Grok Build session selector is unavailable")]
|
||||
Unavailable,
|
||||
}
|
||||
|
||||
/// Selected-session construction failure.
|
||||
/// 构造选中会话时的失败。
|
||||
#[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GrokSessionDataError {
|
||||
/// Access token or verified identity header is malformed.
|
||||
/// access token 或已验证身份头格式非法。
|
||||
#[error("selected session contains an invalid secret value")]
|
||||
InvalidSecretValue,
|
||||
/// Egress/session binding is invalid.
|
||||
/// 出口/会话绑定非法。
|
||||
#[error("selected session binding is invalid")]
|
||||
InvalidBinding,
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use zeroize::Zeroizing;
|
||||
|
||||
use super::{GrokHeader, GrokSessionBinding};
|
||||
|
||||
/// Owned request handed to the injected HTTP SSE transport.
|
||||
/// 交给注入的 HTTP SSE transport 的自持有请求。
|
||||
pub struct GrokInferenceRequest {
|
||||
endpoint: Url,
|
||||
headers: Vec<GrokHeader>,
|
||||
@@ -35,25 +35,25 @@ impl GrokInferenceRequest {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the strict official Responses endpoint.
|
||||
/// 返回严格限定的官方 Responses 端点。
|
||||
#[must_use]
|
||||
pub const fn endpoint(&self) -> &Url {
|
||||
&self.endpoint
|
||||
}
|
||||
|
||||
/// Returns typed headers; adapters must not log sensitive values.
|
||||
/// 返回类型化 header;适配层不得记录敏感值。
|
||||
#[must_use]
|
||||
pub fn headers(&self) -> &[GrokHeader] {
|
||||
&self.headers
|
||||
}
|
||||
|
||||
/// Returns the serialized typed Responses body.
|
||||
/// 返回序列化后的类型化 Responses body。
|
||||
#[must_use]
|
||||
pub fn body(&self) -> &[u8] {
|
||||
&self.body
|
||||
}
|
||||
|
||||
/// Returns the pseudonymous proxy/egress lookup binding.
|
||||
/// 返回假名化的 proxy/egress 查找绑定。
|
||||
#[must_use]
|
||||
pub const fn binding(&self) -> &GrokSessionBinding {
|
||||
&self.binding
|
||||
@@ -75,16 +75,16 @@ impl fmt::Debug for GrokInferenceRequest {
|
||||
}
|
||||
}
|
||||
|
||||
/// Stream of raw SSE byte chunks returned after the POST is accepted.
|
||||
/// POST 被接受后返回的原始 SSE 字节块流。
|
||||
pub type GrokInferenceChunkStream =
|
||||
Pin<Box<dyn Stream<Item = Result<bytes::Bytes, GrokInferenceTransportError>> + Send + 'static>>;
|
||||
|
||||
/// Whether the account-isolated inference client was already cached.
|
||||
/// 账号隔离的推理 client 是否已在缓存中。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GrokInferenceClientCacheStatus {
|
||||
/// The binding reused its existing account-isolated client.
|
||||
/// 绑定复用了已有的账号隔离 client。
|
||||
Hit,
|
||||
/// The binding was absent when the request first checked the cache.
|
||||
/// 请求首次查缓存时该绑定不存在。
|
||||
Miss,
|
||||
}
|
||||
|
||||
@@ -98,12 +98,12 @@ impl GrokInferenceClientCacheStatus {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolver that supplied the addresses used by the request.
|
||||
/// 提供本次请求所用地址的解析器。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GrokInferenceDnsSource {
|
||||
/// The system resolver returned an entirely public address set.
|
||||
/// 系统解析器返回了完全公网的地址集合。
|
||||
System,
|
||||
/// The system result was unusable and the trusted DoH fallback supplied addresses.
|
||||
/// 系统结果不可用,由受信 DoH 回退提供地址。
|
||||
TrustedDoh,
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ impl GrokInferenceDnsSource {
|
||||
}
|
||||
}
|
||||
|
||||
/// DNS work observed while opening an upstream connection.
|
||||
/// 建立上游连接期间观测到的 DNS 工作量。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct GrokInferenceDnsObservation {
|
||||
source: GrokInferenceDnsSource,
|
||||
@@ -144,7 +144,7 @@ impl GrokInferenceDnsObservation {
|
||||
}
|
||||
}
|
||||
|
||||
/// Low-cardinality transport timings and pool facts for one inference request.
|
||||
/// 单次推理请求的低基数 transport 耗时与连接池事实。
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub struct GrokInferenceTransportMetrics {
|
||||
headers_ms: Option<u64>,
|
||||
@@ -190,8 +190,8 @@ impl GrokInferenceTransportMetrics {
|
||||
}
|
||||
}
|
||||
|
||||
/// Accepted inference response. Non-success HTTP responses must be returned as
|
||||
/// [`GrokInferenceTransportError`] instead.
|
||||
/// 已被接受的推理响应。非成功 HTTP 响应必须改为返回
|
||||
/// [`GrokInferenceTransportError`]。
|
||||
pub struct GrokInferenceResponse {
|
||||
body: GrokInferenceChunkStream,
|
||||
http_version: UpstreamHttpVersion,
|
||||
@@ -201,7 +201,7 @@ pub struct GrokInferenceResponse {
|
||||
}
|
||||
|
||||
impl GrokInferenceResponse {
|
||||
/// Wraps one accepted SSE response body.
|
||||
/// 包装一个已被接受的 SSE 响应体。
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
body: GrokInferenceChunkStream,
|
||||
@@ -265,34 +265,34 @@ impl fmt::Debug for GrokInferenceResponse {
|
||||
}
|
||||
}
|
||||
|
||||
/// Secret-free transport failure category.
|
||||
/// 不含密钥的 transport 失败分类。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GrokInferenceTransportErrorKind {
|
||||
/// Canonical request semantics were rejected by the official proxy.
|
||||
/// 官方代理拒绝了 canonical 请求语义。
|
||||
InvalidRequest,
|
||||
/// The official proxy does not support a requested capability.
|
||||
/// 官方代理不支持请求的某项能力。
|
||||
Unsupported,
|
||||
/// Access token was rejected.
|
||||
/// access token 被拒绝。
|
||||
Unauthorized,
|
||||
/// Session lacks model or feature entitlement.
|
||||
/// 会话缺少模型或特性授权。
|
||||
PermissionDenied,
|
||||
/// Per-session or provider rate limit.
|
||||
/// 会话级或 Provider 级限流。
|
||||
RateLimited,
|
||||
/// Account credits or quota are exhausted.
|
||||
/// 账号额度或配额耗尽。
|
||||
QuotaExhausted,
|
||||
/// Deadline or transport timeout.
|
||||
/// 截止时间或 transport 超时。
|
||||
Timeout,
|
||||
/// Network/TLS/connection failure.
|
||||
/// 网络/TLS/连接失败。
|
||||
Transport,
|
||||
/// HTTP/SSE response violates the expected contract.
|
||||
/// HTTP/SSE 响应违反预期契约。
|
||||
Protocol,
|
||||
/// Official CLI proxy is unavailable.
|
||||
/// 官方 CLI 代理不可用。
|
||||
Unavailable,
|
||||
/// Caller cancellation observed by the transport.
|
||||
/// transport 观测到调用方取消。
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
/// Classified transport error that never contains an upstream response body.
|
||||
/// 已分类的 transport 错误,绝不包含上游响应体。
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct GrokInferenceTransportError {
|
||||
kind: GrokInferenceTransportErrorKind,
|
||||
@@ -308,7 +308,7 @@ pub struct GrokInferenceTransportError {
|
||||
}
|
||||
|
||||
impl GrokInferenceTransportError {
|
||||
/// Creates a classified error with the transport's conservative send state.
|
||||
/// 以 transport 保守判定的发送状态创建已分类错误。
|
||||
#[must_use]
|
||||
pub const fn new(kind: GrokInferenceTransportErrorKind, send_state: UpstreamSendState) -> Self {
|
||||
Self {
|
||||
@@ -329,7 +329,7 @@ impl GrokInferenceTransportError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Attaches a valid HTTP status.
|
||||
/// 附着合法的 HTTP 状态码。
|
||||
#[must_use]
|
||||
pub fn with_status(mut self, status: u16) -> Self {
|
||||
if (100..=599).contains(&status) {
|
||||
@@ -338,7 +338,7 @@ impl GrokInferenceTransportError {
|
||||
self
|
||||
}
|
||||
|
||||
/// Attaches a bounded retry delay parsed by the transport.
|
||||
/// 附着 transport 解析出的有界重试延迟。
|
||||
#[must_use]
|
||||
pub const fn with_retry_after(mut self, retry_after: Duration) -> Self {
|
||||
self.retry_after = Some(retry_after);
|
||||
@@ -378,32 +378,32 @@ impl GrokInferenceTransportError {
|
||||
self
|
||||
}
|
||||
|
||||
/// Discards a possibly sensitive upstream body while retaining that fact.
|
||||
/// 丢弃可能敏感的上游响应体,仅保留「已丢弃」这一事实。
|
||||
#[must_use]
|
||||
pub fn redact_sensitive_context(mut self, _value: impl AsRef<str>) -> Self {
|
||||
self.sensitive_context_redacted = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the stable transport category.
|
||||
/// 返回稳定的 transport 分类。
|
||||
#[must_use]
|
||||
pub const fn kind(&self) -> GrokInferenceTransportErrorKind {
|
||||
self.kind
|
||||
}
|
||||
|
||||
/// Returns the conservative payload send state.
|
||||
/// 返回保守判定的 payload 发送状态。
|
||||
#[must_use]
|
||||
pub const fn send_state(&self) -> UpstreamSendState {
|
||||
self.send_state
|
||||
}
|
||||
|
||||
/// Returns the sanitized HTTP status.
|
||||
/// 返回清洗后的 HTTP 状态码。
|
||||
#[must_use]
|
||||
pub const fn status(&self) -> Option<u16> {
|
||||
self.status
|
||||
}
|
||||
|
||||
/// Returns the optional retry delay.
|
||||
/// 返回可选的重试延迟。
|
||||
#[must_use]
|
||||
pub const fn retry_after(&self) -> Option<Duration> {
|
||||
self.retry_after
|
||||
@@ -434,7 +434,7 @@ impl GrokInferenceTransportError {
|
||||
self.credential_recovery_required
|
||||
}
|
||||
|
||||
/// Reports whether a sensitive body was discarded.
|
||||
/// 报告是否丢弃过敏感响应体。
|
||||
#[must_use]
|
||||
pub const fn sensitive_context_was_redacted(&self) -> bool {
|
||||
self.sensitive_context_redacted
|
||||
@@ -477,17 +477,16 @@ impl fmt::Display for GrokInferenceTransportError {
|
||||
|
||||
impl std::error::Error for GrokInferenceTransportError {}
|
||||
|
||||
/// Future returned by the inference transport.
|
||||
/// 推理 transport 返回的 future。
|
||||
pub type GrokInferenceTransportFuture<'a> = Pin<
|
||||
Box<
|
||||
dyn Future<Output = Result<GrokInferenceResponse, GrokInferenceTransportError>> + Send + 'a,
|
||||
>,
|
||||
>;
|
||||
|
||||
/// Runtime HTTP SSE port. Implementations must issue exactly one POST, use the
|
||||
/// supplied session binding for egress affinity, and never retry, switch
|
||||
/// credentials, or fall back to another endpoint.
|
||||
/// 运行时 HTTP SSE 端口。实现必须只发出一次 POST,用传入的 session binding
|
||||
/// 维持 egress 亲和,且不得重试、切换凭据或回退到其他端点。
|
||||
pub trait GrokInferenceTransport: Send + Sync {
|
||||
/// Starts one official CLI proxy request.
|
||||
/// 发起一次官方 CLI 代理请求。
|
||||
fn execute(&self, request: GrokInferenceRequest) -> GrokInferenceTransportFuture<'_>;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! External fixture-driven contracts for the transport and verification ports.
|
||||
//! transport 与验证端口的外置 fixture 驱动契约。
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::future::ready;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! External OIDC verifier contracts.
|
||||
//! 外置的 OIDC verifier 契约。
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! External reqwest network policy contracts.
|
||||
//! 外置的 reqwest 网络策略契约。
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::net::IpAddr;
|
||||
|
||||
Reference in New Issue
Block a user