|
12 | 12 | if TYPE_CHECKING: |
13 | 13 | from tablestore import AsyncOTSClient # type: ignore[import-untyped] |
14 | 14 | from tablestore import OTSClient |
| 15 | + from tablestore.credentials import CredentialsProvider |
| 16 | + |
| 17 | + from agentrun.utils.config import Config |
15 | 18 |
|
16 | 19 | # OTS 单个属性列值上限为 2MB,留 0.5MB 余量(按字符数计) |
17 | 20 | MAX_COLUMN_SIZE: int = 1_500_000 # 1.5M 字符 |
@@ -103,38 +106,73 @@ def from_chunks(chunks: list[str]) -> str: |
103 | 106 | return "".join(chunks) |
104 | 107 |
|
105 | 108 |
|
| 109 | +def build_ots_credentials_provider(config: "Config") -> "CredentialsProvider": |
| 110 | + """构建 TableStore CredentialsProvider,每次请求从 Config 实时取最新 STS。 |
| 111 | +
|
| 112 | + TableStore client 在**每个请求**调用 ``credentials_provider.get_credentials()`` |
| 113 | + (见 tablestore client 的 ``_request_helper``),因此长生命周期的 OTSClient |
| 114 | + 也能在每次操作时拿到请求级 overlay 注入的最新 STS(再回退环境变量)。 |
| 115 | +
|
| 116 | + Args: |
| 117 | + config: agentrun Config 对象,凭证经其 getter 解析(overlay 优先)。 |
| 118 | +
|
| 119 | + Returns: |
| 120 | + TableStore ``CredentialsProvider`` 实例。 |
| 121 | + """ |
| 122 | + from tablestore.credentials import Credentials, CredentialsProvider |
| 123 | + |
| 124 | + class _AgentrunOtsCredentialsProvider(CredentialsProvider): |
| 125 | + def __init__(self, cfg: "Config") -> None: |
| 126 | + self._cfg = cfg |
| 127 | + |
| 128 | + def get_credentials(self) -> Credentials: |
| 129 | + cfg = self._cfg |
| 130 | + return Credentials( |
| 131 | + access_key_id=cfg.get_access_key_id(), |
| 132 | + access_key_secret=cfg.get_access_key_secret(), |
| 133 | + security_token=cfg.get_security_token() or None, |
| 134 | + ) |
| 135 | + |
| 136 | + return _AgentrunOtsCredentialsProvider(config) |
| 137 | + |
| 138 | + |
106 | 139 | def build_ots_clients( |
107 | 140 | endpoint: str, |
108 | | - access_key_id: str, |
109 | | - access_key_secret: str, |
110 | 141 | instance_name: str, |
111 | 142 | *, |
112 | | - sts_token: str | None = None, |
| 143 | + config: "Config", |
113 | 144 | ) -> tuple[OTSClient, AsyncOTSClient]: |
114 | 145 | """构建 OTSClient 和 AsyncOTSClient 实例。 |
115 | 146 |
|
116 | 147 | 独立于 codegen 模板,避免 AsyncOTSClient 被替换为 OTSClient。 |
117 | 148 |
|
| 149 | + 凭证统一通过 :func:`build_ots_credentials_provider` 注入:client 每次请求 |
| 150 | + 动态从 ``config`` 取最新 STS(请求级 overlay 优先),STS 过期可静默刷新。 |
| 151 | + 遵循 AGENTS.md 约定——不接受静态 ak/sk/sts。 |
| 152 | +
|
| 153 | + Args: |
| 154 | + endpoint: OTS endpoint。 |
| 155 | + instance_name: OTS 实例名。 |
| 156 | + config: agentrun Config 对象,凭证经其 getter 动态解析。 |
| 157 | +
|
118 | 158 | Returns: |
119 | 159 | (ots_client, async_ots_client) 二元组。 |
120 | 160 | """ |
121 | 161 | from tablestore import AsyncOTSClient # type: ignore[import-untyped] |
122 | 162 | from tablestore import OTSClient, WriteRetryPolicy |
123 | 163 |
|
| 164 | + # 同一个 provider 可被 sync / async client 共享:无状态,按请求读 overlay。 |
| 165 | + provider = build_ots_credentials_provider(config) |
124 | 166 | ots_client = OTSClient( |
125 | 167 | endpoint, |
126 | | - access_key_id, |
127 | | - access_key_secret, |
128 | | - instance_name, |
129 | | - sts_token=sts_token, |
| 168 | + instance_name=instance_name, |
| 169 | + credentials_provider=provider, |
130 | 170 | retry_policy=WriteRetryPolicy(), |
131 | 171 | ) |
132 | 172 | async_ots_client = AsyncOTSClient( |
133 | 173 | endpoint, |
134 | | - access_key_id, |
135 | | - access_key_secret, |
136 | | - instance_name, |
137 | | - sts_token=sts_token, |
| 174 | + instance_name=instance_name, |
| 175 | + credentials_provider=provider, |
138 | 176 | retry_policy=WriteRetryPolicy(), |
139 | 177 | ) |
140 | 178 | return ots_client, async_ots_client |
0 commit comments