feat: route minimax m3 through agentrun
This commit is contained in:
@@ -117,8 +117,9 @@ G14 Code Agent 通过同一个 repo-owned Codex app-server stdio runner 承载
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `deepseek` | 是 | `HWLAB_CODE_AGENT_DEEPSEEK_MODEL`,默认 `deepseek-chat` | `HWLAB_CODE_AGENT_DEEPSEEK_BASE_URL`,默认 `http://hwlab-deepseek-proxy.<namespace>.svc.cluster.local:4000/v1/responses` | G14 集群内 DeepSeek Responses bridge;Service 4000 先进入 `hwlab-deepseek-responses-bridge`,再转发到同 Pod 内 Moon Bridge 4001。 |
|
||||
| `codex-api` | 否 | `HWLAB_CODE_AGENT_CODEX_API_MODEL`,默认 `gpt-5.5` | `HWLAB_CODE_AGENT_CODEX_API_BASE_URL`,G14 默认应指向同 Pod `127.0.0.1` loopback forwarder,forwarder upstream 为 hyueapi | 独立 Codex/OpenAI-compatible Responses API 通道;不得依赖 DeepSeek bridge。 |
|
||||
| `minimax-m3` | 否 | `HWLAB_CODE_AGENT_MINIMAX_M3_MODEL`,默认 `MiniMax-M3` | 不由 HWLAB 配置;由 AgentRun `backendProfile=minimax-m3` 的 Secret/profile 承接 | HWLAB 只把该 profile 委托给 AgentRun v0.1,不在 HWLAB 新增 MiniMax API key、base URL、proxy 或 provider 实现。 |
|
||||
|
||||
`hwlab-cloud-api` 仍以 `HWLAB_CODE_AGENT_PROVIDER=codex-stdio` 运行。`HWLAB_CODE_AGENT_MODEL` 与 `HWLAB_CODE_AGENT_OPENAI_BASE_URL` 可作为 runtime-default 兜底,但前端默认 profile 是 `deepseek`,用户可以切到 `codex-api`。Codex app-server 启动参数必须同时设置 provider base URL、provider `name`、`model` 和 `review_model`,否则 Codex/DeepSeek 链路可能在模型目录或 `/v1/responses` 阶段退化成 `model=None`。G14 cloud-api 的 `NO_PROXY/no_proxy` 必须包含 `hyueapi.com` 和 `.hyueapi.com`;`codex-api` 的 hyueapi upstream 由同 Pod loopback forwarder 直连,不能被 proxy 注入污染。
|
||||
`hwlab-cloud-api` 仍以 `HWLAB_CODE_AGENT_PROVIDER=codex-stdio` 运行。`HWLAB_CODE_AGENT_MODEL` 与 `HWLAB_CODE_AGENT_OPENAI_BASE_URL` 可作为 runtime-default 兜底,但前端默认 profile 是 `deepseek`,用户可以切到 `codex-api` 或 `minimax-m3`。Codex app-server 启动参数必须同时设置 provider base URL、provider `name`、`model` 和 `review_model`,否则 Codex/DeepSeek 链路可能在模型目录或 `/v1/responses` 阶段退化成 `model=None`。`minimax-m3` 是 AgentRun 委托 profile,HWLAB 只负责把用户选择透传为 AgentRun `backendProfile=minimax-m3`,不得为了该 profile 在 HWLAB 里重新读取 MiniMax Secret 或维护第二套 provider bridge。G14 cloud-api 的 `NO_PROXY/no_proxy` 必须包含 `hyueapi.com` 和 `.hyueapi.com`;`codex-api` 的 hyueapi upstream 由同 Pod loopback forwarder 直连,不能被 proxy 注入污染。
|
||||
|
||||
G14 `codex-api` 不得把 `http://172.26.26.227:17680/v1/responses` 作为默认 base URL;该地址只保留为 D601 legacy Code Queue runner 或历史 egress 对照线索。G14 上的 `codex-api` 应先进入同 Pod `127.0.0.1` loopback forwarder,再由 forwarder 直连 `hyueapi.com` / `.hyueapi.com`;这两个域名必须同时进入 `NO_PROXY` 和 `no_proxy`。DeepSeek profile 可以通过集群内 bridge/Moon Bridge 转换,`codex-api` 不能用 DeepSeek bridge 伪装通过,也不能因为默认 profile 切到 DeepSeek 而删除或覆盖 `codex-api` 的独立模型、base URL、auth 和最小闭环验证。
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ const DEFAULT_PROVIDER_ID = "G14";
|
||||
const DEFAULT_REPO_URL = "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git";
|
||||
const DEFAULT_RUNNER_NAMESPACE = "agentrun-v01";
|
||||
const DEFAULT_TIMEOUT_MS = 1_200_000;
|
||||
const AGENTRUN_BACKENDS = Object.freeze(["codex", "deepseek"]);
|
||||
const AGENTRUN_BACKENDS = Object.freeze(["codex", "deepseek", "minimax-m3"]);
|
||||
const THREAD_CONTINUITY_POLICY = "hwlab-agentrun-v01-reuse-runner-thread";
|
||||
const SESSION_POLICY_RUN_LOCAL = "hwlab-agentrun-v01-session-runner-reuse";
|
||||
const TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "blocked", "cancelled", "canceled"]);
|
||||
@@ -603,11 +603,12 @@ function runLeaseExpired(leaseExpiresAt) {
|
||||
}
|
||||
|
||||
function providerCredentialSecretRef(profile, env) {
|
||||
const profileEnvKey = String(profile ?? "").toUpperCase().replace(/[^A-Z0-9]+/gu, "_");
|
||||
return {
|
||||
profile,
|
||||
secretRef: {
|
||||
namespace: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_SECRET_NAMESPACE, DEFAULT_RUNNER_NAMESPACE),
|
||||
name: firstNonEmpty(env[`HWLAB_CODE_AGENT_AGENTRUN_${profile.toUpperCase()}_SECRET_NAME`], env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_SECRET_NAME, `agentrun-v01-provider-${profile}`),
|
||||
name: firstNonEmpty(env[`HWLAB_CODE_AGENT_AGENTRUN_${profileEnvKey}_SECRET_NAME`], env[`HWLAB_CODE_AGENT_AGENTRUN_${String(profile ?? "").toUpperCase()}_SECRET_NAME`], env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_SECRET_NAME, `agentrun-v01-provider-${profile}`),
|
||||
keys: ["auth.json", "config.toml"],
|
||||
mountPath: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_CODEX_HOME_MOUNT, "~/.codex")
|
||||
}
|
||||
@@ -1120,6 +1121,7 @@ function fullSourceCommit(env) {
|
||||
|
||||
function modelForBackendProfile(profile, env = process.env) {
|
||||
if (profile === "codex") return firstNonEmpty(env.HWLAB_CODE_AGENT_CODEX_API_MODEL, env.HWLAB_CODE_AGENT_MODEL, "gpt-5.5");
|
||||
if (profile === "minimax-m3") return firstNonEmpty(env.HWLAB_CODE_AGENT_MINIMAX_M3_MODEL, "MiniMax-M3");
|
||||
return firstNonEmpty(env.HWLAB_CODE_AGENT_DEEPSEEK_MODEL, "deepseek-chat");
|
||||
}
|
||||
|
||||
|
||||
@@ -410,6 +410,127 @@ test("cloud api /v1/agent/chat delegates v0.2 turns to AgentRun v0.1 over adapte
|
||||
}
|
||||
});
|
||||
|
||||
test("cloud api AgentRun adapter maps minimax-m3 provider profile to AgentRun backend", async () => {
|
||||
const calls = [];
|
||||
const agentRunServer = createHttpServer(async (request, response) => {
|
||||
const url = new URL(request.url || "/", "http://127.0.0.1");
|
||||
const chunks = [];
|
||||
for await (const chunk of request) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
||||
const body = chunks.length ? JSON.parse(Buffer.concat(chunks).toString("utf8")) : null;
|
||||
calls.push({ method: request.method, path: url.pathname, body });
|
||||
const send = (data) => {
|
||||
response.writeHead(200, { "content-type": "application/json" });
|
||||
response.end(`${JSON.stringify({ ok: true, data, traceId: "trc_fake_agentrun_minimax_m3" })}\n`);
|
||||
};
|
||||
if (request.method === "POST" && url.pathname === "/api/v1/runs") {
|
||||
assert.equal(body.backendProfile, "minimax-m3");
|
||||
assert.equal(body.executionPolicy.secretScope.providerCredentials[0].profile, "minimax-m3");
|
||||
assert.equal(body.executionPolicy.secretScope.providerCredentials[0].secretRef.name, "agentrun-v01-provider-minimax-m3");
|
||||
assert.equal(body.executionPolicy.secretScope.providerCredentials[0].secretRef.namespace, "agentrun-v01");
|
||||
return send({ id: "run_hwlab_minimax_m3", status: "pending", backendProfile: "minimax-m3", sessionRef: body.sessionRef, resourceBundleRef: body.resourceBundleRef });
|
||||
}
|
||||
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_hwlab_minimax_m3/commands") {
|
||||
assert.equal(body.payload.providerProfile, "minimax-m3");
|
||||
assert.match(body.payload.prompt, /MiniMax-M3/u);
|
||||
return send({ id: "cmd_hwlab_minimax_m3", runId: "run_hwlab_minimax_m3", state: "pending", type: "turn", seq: 1 });
|
||||
}
|
||||
if (request.method === "POST" && url.pathname === "/api/v1/runs/run_hwlab_minimax_m3/runner-jobs") {
|
||||
assert.equal(body.commandId, "cmd_hwlab_minimax_m3");
|
||||
return send({
|
||||
action: "create-kubernetes-job",
|
||||
runId: "run_hwlab_minimax_m3",
|
||||
commandId: "cmd_hwlab_minimax_m3",
|
||||
attemptId: "attempt_hwlab_minimax_m3",
|
||||
runnerId: "runner_hwlab_minimax_m3",
|
||||
namespace: "agentrun-v01",
|
||||
jobName: "agentrun-v01-runner-hwlab-minimax-m3",
|
||||
jobIdentity: { namespace: "agentrun-v01", name: "agentrun-v01-runner-hwlab-minimax-m3" },
|
||||
runner: { attemptId: "attempt_hwlab_minimax_m3", runnerId: "runner_hwlab_minimax_m3" }
|
||||
});
|
||||
}
|
||||
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_minimax_m3/events") {
|
||||
return send({ items: [
|
||||
{ id: "evt_minimax_1", runId: "run_hwlab_minimax_m3", seq: 1, type: "backend_status", payload: { phase: "runner-job-created", commandId: "cmd_hwlab_minimax_m3", attemptId: "attempt_hwlab_minimax_m3", jobName: "agentrun-v01-runner-hwlab-minimax-m3", namespace: "agentrun-v01" }, createdAt: "2026-06-02T00:00:00.000Z" },
|
||||
{ id: "evt_minimax_2", runId: "run_hwlab_minimax_m3", seq: 2, type: "assistant_message", payload: { commandId: "cmd_hwlab_minimax_m3", text: "AGENTRUN_MINIMAX_M3_OK" }, createdAt: "2026-06-02T00:00:01.000Z" },
|
||||
{ id: "evt_minimax_3", runId: "run_hwlab_minimax_m3", seq: 3, type: "terminal_status", payload: { commandId: "cmd_hwlab_minimax_m3", terminalStatus: "completed" }, createdAt: "2026-06-02T00:00:02.000Z" }
|
||||
] });
|
||||
}
|
||||
if (request.method === "GET" && url.pathname === "/api/v1/runs/run_hwlab_minimax_m3/commands/cmd_hwlab_minimax_m3/result") {
|
||||
return send({
|
||||
runId: "run_hwlab_minimax_m3",
|
||||
commandId: "cmd_hwlab_minimax_m3",
|
||||
attemptId: "attempt_hwlab_minimax_m3",
|
||||
runnerId: "runner_hwlab_minimax_m3",
|
||||
jobName: "agentrun-v01-runner-hwlab-minimax-m3",
|
||||
namespace: "agentrun-v01",
|
||||
status: "completed",
|
||||
runStatus: "completed",
|
||||
commandState: "completed",
|
||||
terminalStatus: "completed",
|
||||
completed: true,
|
||||
reply: "AGENTRUN_MINIMAX_M3_OK",
|
||||
lastSeq: 3,
|
||||
eventCount: 3,
|
||||
sessionRef: { sessionId: "ses_server-test-minimax-m3", conversationId: "cnv_server-test-minimax-m3", threadId: null }
|
||||
});
|
||||
}
|
||||
response.writeHead(404, { "content-type": "application/json" });
|
||||
response.end(`${JSON.stringify({ ok: false, failureKind: "schema-invalid", message: `unexpected ${request.method} ${url.pathname}`, traceId: "trc_fake_agentrun_minimax_m3" })}\n`);
|
||||
});
|
||||
await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve));
|
||||
|
||||
const agentRunPort = agentRunServer.address().port;
|
||||
const server = createCloudApiServer({
|
||||
env: {
|
||||
HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01",
|
||||
AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`,
|
||||
HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1",
|
||||
HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: "0123456789abcdef0123456789abcdef01234567",
|
||||
HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "deepseek",
|
||||
HWLAB_ENVIRONMENT: "v02",
|
||||
HWLAB_GITOPS_PROFILE: "v02"
|
||||
},
|
||||
accessController: {
|
||||
required: false,
|
||||
async authenticate() {
|
||||
return { ok: true, actor: null, session: null };
|
||||
}
|
||||
}
|
||||
});
|
||||
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
||||
|
||||
try {
|
||||
const { port } = server.address();
|
||||
const traceId = "trc_server-test-agentrun-minimax-m3";
|
||||
const submit = await fetch(`http://127.0.0.1:${port}/v1/agent/chat`, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json", "x-trace-id": traceId },
|
||||
body: JSON.stringify({
|
||||
conversationId: "cnv_server-test-minimax-m3",
|
||||
sessionId: "ses_server-test-minimax-m3",
|
||||
providerProfile: "minimax-m3",
|
||||
message: "MiniMax-M3 AgentRun adapter smoke"
|
||||
})
|
||||
});
|
||||
assert.equal(submit.status, 202);
|
||||
const accepted = await submit.json();
|
||||
assert.equal(accepted.shortConnection, true);
|
||||
|
||||
const payload = await pollAgentResult(port, traceId);
|
||||
validateCodeAgentChatSchema(payload);
|
||||
assert.equal(payload.status, "completed");
|
||||
assert.equal(payload.infrastructureBackend, "agentrun-v01/minimax-m3");
|
||||
assert.equal(payload.agentRun.backendProfile, "minimax-m3");
|
||||
assert.equal(payload.providerTrace.backendProfile, "minimax-m3");
|
||||
assert.equal(payload.agentRun.jobName, "agentrun-v01-runner-hwlab-minimax-m3");
|
||||
assert.equal(payload.reply.content, "AGENTRUN_MINIMAX_M3_OK");
|
||||
assert.equal(calls.filter((call) => call.method === "POST" && call.path === "/api/v1/runs").length, 1);
|
||||
} finally {
|
||||
await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));
|
||||
await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve())));
|
||||
}
|
||||
});
|
||||
|
||||
test("cloud api AgentRun adapter rejects non-internal manager URLs by default", async () => {
|
||||
const server = createCloudApiServer({
|
||||
env: {
|
||||
|
||||
@@ -31,15 +31,17 @@ import {
|
||||
|
||||
const DEFAULT_CODE_AGENT_RESULT_TRACE_EVENT_LIMIT = 120;
|
||||
const DEFAULT_CODE_AGENT_PROVIDER_PROFILE = "deepseek";
|
||||
const CODE_AGENT_PROVIDER_PROFILE_IDS = Object.freeze(["deepseek", "codex-api", "runtime-default"]);
|
||||
const CODE_AGENT_PROVIDER_PROFILE_IDS = Object.freeze(["deepseek", "codex-api", "minimax-m3", "runtime-default"]);
|
||||
const CODE_AGENT_PROVIDER_PROFILE_LABELS = Object.freeze({
|
||||
"deepseek": "DeepSeek",
|
||||
"codex-api": "Codex API",
|
||||
"minimax-m3": "MiniMax-M3 via AgentRun",
|
||||
"runtime-default": "运行默认"
|
||||
});
|
||||
const DEFAULT_CODE_AGENT_CODEX_API_MODEL = "gpt-5.5";
|
||||
const DEFAULT_CODE_AGENT_CODEX_API_BASE_URL = "http://127.0.0.1:49280/responses";
|
||||
const DEFAULT_CODE_AGENT_DEEPSEEK_MODEL = "deepseek-chat";
|
||||
const DEFAULT_CODE_AGENT_MINIMAX_M3_MODEL = "MiniMax-M3";
|
||||
|
||||
export async function handleCodeAgentChatHttp(request, response, options) {
|
||||
const body = await readBody(request, options.bodyLimitBytes);
|
||||
@@ -252,6 +254,8 @@ function codeAgentProviderProfileEnv(env = process.env, params = {}) {
|
||||
if (profile === "codex-api") {
|
||||
overlay.HWLAB_CODE_AGENT_MODEL = firstNonEmptyValue(env.HWLAB_CODE_AGENT_CODEX_API_MODEL, env.HWLAB_CODE_AGENT_LEGACY_CODEX_MODEL, DEFAULT_CODE_AGENT_CODEX_API_MODEL);
|
||||
overlay.HWLAB_CODE_AGENT_OPENAI_BASE_URL = firstNonEmptyValue(env.HWLAB_CODE_AGENT_CODEX_API_BASE_URL, env.HWLAB_CODE_AGENT_LEGACY_OPENAI_BASE_URL, DEFAULT_CODE_AGENT_CODEX_API_BASE_URL);
|
||||
} else if (profile === "minimax-m3") {
|
||||
overlay.HWLAB_CODE_AGENT_MODEL = firstNonEmptyValue(env.HWLAB_CODE_AGENT_MINIMAX_M3_MODEL, DEFAULT_CODE_AGENT_MINIMAX_M3_MODEL);
|
||||
} else {
|
||||
overlay.HWLAB_CODE_AGENT_MODEL = firstNonEmptyValue(env.HWLAB_CODE_AGENT_DEEPSEEK_MODEL, DEFAULT_CODE_AGENT_DEEPSEEK_MODEL);
|
||||
overlay.HWLAB_CODE_AGENT_OPENAI_BASE_URL = firstNonEmptyValue(env.HWLAB_CODE_AGENT_DEEPSEEK_BASE_URL, defaultDeepSeekBaseUrlForEnv(env));
|
||||
|
||||
@@ -123,13 +123,13 @@ function help() {
|
||||
"hwlab-cli client workbench reset --profile NAME --confirm",
|
||||
"hwlab-cli client request GET /v1/access/status [--full]",
|
||||
"hwlab-cli client rpc system.health [--full]",
|
||||
"hwlab-cli client agent send --message TEXT|--message-file PATH [--from-trace TRACE] [--conversation-id ID] [--session-id ID] [--thread-id ID] [--retry-of TRACE] --provider-profile deepseek",
|
||||
"hwlab-cli client agent send --message TEXT|--message-file PATH [--from-trace TRACE] [--conversation-id ID] [--session-id ID] [--thread-id ID] [--retry-of TRACE] --provider-profile deepseek|codex-api|minimax-m3",
|
||||
"hwlab-cli client agent send --message TEXT --wait --timeout-ms 50000",
|
||||
"hwlab-cli client agent result TRACE_ID",
|
||||
"hwlab-cli client agent trace TRACE_ID [--render web]",
|
||||
"hwlab-cli client agent inspect --trace-id TRACE_ID",
|
||||
"hwlab-cli client agent cancel TRACE_ID",
|
||||
"hwlab-cli client harness submit --message TEXT --provider-profile deepseek",
|
||||
"hwlab-cli client harness submit --message TEXT --provider-profile deepseek|codex-api|minimax-m3",
|
||||
"hwlab-cli client harness wait TRACE_ID --timeout-ms 50000",
|
||||
"hwlab-cli client harness audit TRACE_ID --require-bootsharp"
|
||||
]
|
||||
@@ -716,7 +716,7 @@ function harnessHelp() {
|
||||
aliases: ["harness", "harness-ops", "harness-opt"],
|
||||
commands: [
|
||||
"health [--base-url URL]",
|
||||
"submit --message TEXT|--message-file PATH [--conversation-id ID] [--trace-id ID] [--provider-profile deepseek|codex-api]",
|
||||
"submit --message TEXT|--message-file PATH [--conversation-id ID] [--trace-id ID] [--provider-profile deepseek|codex-api|minimax-m3]",
|
||||
"result TRACE_ID [--full]",
|
||||
"trace TRACE_ID [--limit N] [--full]",
|
||||
"wait TRACE_ID [--timeout-ms N] [--poll-interval-ms N]",
|
||||
|
||||
@@ -23,7 +23,7 @@ const CODE_AGENT_TIMEOUT_STORAGE_KEY = "hwlab.workbench.codeAgentTimeoutMs.v1";
|
||||
const GATEWAY_SHELL_TIMEOUT_STORAGE_KEY = "hwlab.workbench.gatewayShellTimeoutMs.v1";
|
||||
const CODE_AGENT_PROVIDER_PROFILE_STORAGE_KEY = "hwlab.workbench.codeAgentProviderProfile.v1";
|
||||
const DEFAULT_CODE_AGENT_PROVIDER_PROFILE = "deepseek";
|
||||
const CODE_AGENT_PROVIDER_PROFILES = Object.freeze(["deepseek", "codex-api"]);
|
||||
const CODE_AGENT_PROVIDER_PROFILES = Object.freeze(["deepseek", "codex-api", "minimax-m3"]);
|
||||
const CODE_AGENT_SESSION_STORAGE_KEY = "hwlab.workbench.codeAgentSession.v1";
|
||||
const CODE_AGENT_SESSION_STORAGE_VERSION = 1;
|
||||
const CODE_AGENT_SESSION_STORAGE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
||||
@@ -396,7 +396,9 @@ function syncCodeAgentTimeoutControl() {
|
||||
el.codeAgentProviderProfile.value = CODE_AGENT_PROVIDER_PROFILE;
|
||||
el.codeAgentProviderProfile.title = CODE_AGENT_PROVIDER_PROFILE === "codex-api"
|
||||
? "本次 Code Agent 请求使用原 Codex API / Responses 通道"
|
||||
: "本次 Code Agent 请求使用 G14 DeepSeek Responses bridge";
|
||||
: CODE_AGENT_PROVIDER_PROFILE === "minimax-m3"
|
||||
? "本次 Code Agent 请求通过 AgentRun MiniMax-M3 backend profile 执行"
|
||||
: "本次 Code Agent 请求使用 G14 DeepSeek Responses bridge";
|
||||
el.codeAgentTimeout.value = String(CODE_AGENT_TIMEOUT_MS);
|
||||
el.codeAgentTimeout.title = `Code Agent 无新事件超过 ${formatTraceDuration(CODE_AGENT_TIMEOUT_MS)} 后才显示为超时`;
|
||||
el.gatewayShellTimeout.value = String(GATEWAY_SHELL_TIMEOUT_MS);
|
||||
|
||||
@@ -195,6 +195,7 @@
|
||||
<select id="code-agent-provider-profile" aria-label="Code Agent 模型通道">
|
||||
<option value="deepseek" selected>DeepSeek</option>
|
||||
<option value="codex-api">Codex API</option>
|
||||
<option value="minimax-m3">MiniMax-M3</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="agent-timeout-control" for="code-agent-timeout">
|
||||
|
||||
Reference in New Issue
Block a user