已开启
feat(web): 沙箱不可用时界面区分「计算就绪」状态并显著提示(Issue #91) #160
feat(web): 沙箱不可用时界面区分「计算就绪」状态并显著提示(Issue #91) #160
已开启
hugoxk创建于 9月20日
共 9 个文件变更+73-3
@@ -1212,6 +1212,8 @@ export function App({ initialToken }: { initialToken?: string } = {}) {
1212 const providerSettingsRef = useRef<ProviderModelSettingsHandle>(null);1212 const providerSettingsRef = useRef<ProviderModelSettingsHandle>(null);
1213 const thinkingNormalizationInFlight = useRef<string | undefined>(undefined);1213 const thinkingNormalizationInFlight = useRef<string | undefined>(undefined);
1214 const [error, setErrorState] = useState<string>();1214 const [error, setErrorState] = useState<string>();
1215+ const [computeUnavailable, setComputeUnavailable] = useState(false);
1216+ const [computeUnavailableDetail, setComputeUnavailableDetail] = useState<string>("");
1215 const [systemSettingsErrors, setSystemSettingsErrors] = useState<string[]>([]);1217 const [systemSettingsErrors, setSystemSettingsErrors] = useState<string[]>([]);
1216 const [scopedSettingsErrors, setScopedSettingsErrors] = useState<string[]>([]);1218 const [scopedSettingsErrors, setScopedSettingsErrors] = useState<string[]>([]);
1217 const [dragActive, setDragActive] = useState(false);1219 const [dragActive, setDragActive] = useState(false);
@@ -1425,6 +1427,33 @@ export function App({ initialToken }: { initialToken?: string } = {}) {
1425 }));1427 }));
1426 }, [client]);1428 }, [client]);
1427 1429 
1430+ // Poll the aggregated health endpoint so the compute-readiness state (the
1431+ // sandbox the runner executes code under) follows the real runner, not just
1432+ // whether the local API is reachable. The green connection dot means only
1433+ // "API reachable"; a sandbox that cannot start must surface separately so
1434+ // the user does not invest in a run that will fail at first tool call.
1435+ useEffect(() => {
1436+ let active = true;
1437+ const poll = async (): Promise<void> => {
1438+ try {
1439+ const health = await client.getApiHealth();
1440+ if (!active) return;
1441+ const runner = health.runner;
1442+ const sandboxUsable = runner && "sandboxUsable" in runner ? runner.sandboxUsable : undefined;
1443+ setComputeUnavailable(sandboxUsable === false);
1444+ setComputeUnavailableDetail(sandboxUsable === false && runner && "sandboxDetail" in runner ? runner.sandboxDetail ?? "" : "");
1445+ } catch {
1446+ if (active) setComputeUnavailable(false);
1447+ }
1448+ };
1449+ void poll();
1450+ const timer = window.setInterval(() => void poll(), 30_000);
1451+ return () => {
1452+ active = false;
1453+ window.clearInterval(timer);
1454+ };
1455+ }, [client]);
1456+ 
1428 // Opening System settings starts a dialog-owned refresh. Failures from this1457 // Opening System settings starts a dialog-owned refresh. Failures from this
1429 // operation stay in the dialog instead of being inferred from whichever1458 // operation stay in the dialog instead of being inferred from whichever
1430 // overlay happens to be open when an unrelated background request fails.1459 // overlay happens to be open when an unrelated background request fails.
@@ -4404,9 +4433,10 @@ export function App({ initialToken }: { initialToken?: string } = {}) {
4404 sessionTokens={sessionUsage?.totals.totalTokens}4433 sessionTokens={sessionUsage?.totals.totalTokens}
4405 />4434 />
4406 ) : null}4435 ) : null}
4407- <span className={error ? "connection bad" : "connection"} title={error ? t("app.connectionErrorTooltip", { error }) : t("app.connectionOkTooltip")}><i />{error ? t("app.connectionErrorChip") : null}</span>4436+ <span className={error ? "connection bad" : computeUnavailable ? "connection degraded" : "connection"} title={error ? t("app.connectionErrorTooltip", { error }) : computeUnavailable ? t("app.connectionDegradedTooltip", { detail: computeUnavailableDetail || t("app.computeUnavailableBanner") }) : t("app.connectionOkTooltip")}><i />{error ? t("app.connectionErrorChip") : computeUnavailable ? t("app.connectionDegradedChip") : null}</span>
4408 </div>4437 </div>
4409 </div> : null}4438 </div> : null}
4439+ {computeUnavailable ? <div className="compute-unavailable-notice" role="alert">{t("app.computeUnavailableBanner")}{computeUnavailableDetail ? ` · ${computeUnavailableDetail}` : null}</div> : null}
4410 {sessionPending ? (4440 {sessionPending ? (
4411 <div className="messages"><SkeletonRows className="message-skeleton" count={3} /></div>4441 <div className="messages"><SkeletonRows className="message-skeleton" count={3} /></div>
4412 ) : !session ? (4442 ) : !session ? (
Mapps/web/src/api/artifacts.ts+14-0文件内容审核中,请稍后刷新重试
@@ -2325,7 +2325,10 @@ export const en = {
2325 "app.runnerSelectionChip": "Runners · {count}",2325 "app.runnerSelectionChip": "Runners · {count}",
2326 "app.connectionErrorTooltip": "Needs attention: {error}",2326 "app.connectionErrorTooltip": "Needs attention: {error}",
2327 "app.connectionOkTooltip": "Local API connected",2327 "app.connectionOkTooltip": "Local API connected",
2328+ "app.connectionDegradedTooltip": "Connected, but analysis cannot run: {detail}",
2328 "app.connectionErrorChip": "Needs attention",2329 "app.connectionErrorChip": "Needs attention",
2330+ "app.connectionDegradedChip": "Compute unavailable",
2331+ "app.computeUnavailableBanner": "This environment cannot run analyses. The sandbox could not be prepared. You can still browse history and artifacts.",
2329 "app.sessionIntroPrompt": "Try “Research human TP53 and create a cited evidence brief.”",2332 "app.sessionIntroPrompt": "Try “Research human TP53 and create a cited evidence brief.”",
2330 "app.roleYou": "You",2333 "app.roleYou": "You",
2331 "app.roleResearcher": "Researcher",2334 "app.roleResearcher": "Researcher",
@@ -4913,7 +4916,10 @@ export const zhCN: Partial<Record<MessageKey, string>> = {
4913 "app.runnerSelectionChip": "Runner · {count}",4916 "app.runnerSelectionChip": "Runner · {count}",
4914 "app.connectionErrorTooltip": "需要处理:{error}",4917 "app.connectionErrorTooltip": "需要处理:{error}",
4915 "app.connectionOkTooltip": "本地 API 已连接",4918 "app.connectionOkTooltip": "本地 API 已连接",
4919+ "app.connectionDegradedTooltip": "已连接,但无法执行分析:{detail}",
4916 "app.connectionErrorChip": "需要处理",4920 "app.connectionErrorChip": "需要处理",
4921+ "app.connectionDegradedChip": "计算不可用",
4922+ "app.computeUnavailableBanner": "当前环境无法执行分析任务。沙箱环境未就绪。你仍可浏览历史会话与产物。",
4917 "app.sessionIntroPrompt": "试试「研究人类 TP53,并生成一份带引用的证据简报。」",4923 "app.sessionIntroPrompt": "试试「研究人类 TP53,并生成一份带引用的证据简报。」",
4918 "app.roleYou": "你",4924 "app.roleYou": "你",
4919 "app.roleResearcher": "研究者",4925 "app.roleResearcher": "研究者",
Mapps/web/src/styles/shell.css+2-0文件内容审核中,请稍后刷新重试
Mpackages/schema/src/environment.ts+4-0文件内容审核中,请稍后刷新重试
Mservices/api/src/model-usage-api.test.ts+1-0文件内容审核中,请稍后刷新重试
Mservices/api/src/run-cancel.test.ts+1-0文件内容审核中,请稍后刷新重试
Mservices/api/src/server.test.ts+2-2文件内容审核中,请稍后刷新重试
Mservices/runner/src/server.ts+12-0文件内容审核中,请稍后刷新重试