feat: add Chinese UI, Responses models, and mirrored Windows setup
CI / Python 3.12 / ubuntu-latest (push) Canceled after 0s
CI / Python 3.12 / windows-latest (push) Canceled after 0s
CI / Frontend tests and production build (push) Canceled after 0s
CI / Build and smoke-test Python packages (push) Canceled after 0s

Localize the web console and persist custom model and capability settings.
Use OpenAI Responses exclusively and move runtime limits into environment configuration.
Bootstrap uv and the virtual environment on Windows, support optional China mirrors,
and package source distributions without environments, build output, or credentials.

Update regression tests and repository quality checks for the new behavior.
This commit is contained in:
chenmingxuan committed 2026-09-22 19:16:17 +08:00
1 parent 371aa6df56
commit 2ddec53954
183 files changed
+3427 -11857

No files matched your search

+11 -31
View File
@@ -46,6 +46,7 @@ from artemis.core.diagnostics.schema import ProbeResult, ProbeStatus, SystemRead
from artemis.runtime import DeviceExecutionLock, trace_store
from artemis.runtime.helper_manager import helper_manager
from artemis.utils.credentials_validator import validate_api_key
from artemis.config.settings import settings
from artemis.utils.logger import get_logger
logger = get_logger(__name__)
@@ -59,15 +60,7 @@ DIAGNOSIS_TIMEOUT_SECONDS = 40.0
#: Per-provider budget for a live API key verification request.
CREDENTIAL_CHECK_TIMEOUT_SECONDS = 12.0
#: Providers the credentials probe lists by their *endpoint URL* rather than a
#: key (``OPENAI_BASE_URL`` / ``OLLAMA_BASE_URL`` / ``VLLM_BASE_URL``). Their
#: "raw_key" is that URL: verification must call the endpoint, not treat the
#: URL as an API key.
_ENDPOINT_PROVIDERS = frozenset({"custom", "ollama", "vllm"})
#: Metadata keys that carry credential material. The console UI needs them to
#: prefill its settings form; an MCP caller is an LLM context and must never
#: see them.
#: Redact secrets from legacy probe metadata before returning diagnostics.
_SECRET_METADATA_KEYS = frozenset(
{"raw_key", "key", "api_keys", "current_key", "current_gemini_key"}
)
@@ -227,7 +220,7 @@ def _render_action_lines(action: Any, *, installed_avds: list[str]) -> list[str]
def _credential_steps(env_file: str | None) -> list[str]:
location = env_file or "the project's .env file"
return [
" Ask the user to add a provider key (for example GEMINI_API_KEY=...) to "
" Ask the user to add a provider key (for example OPENAI_API_KEY=...) to "
f"{location}, or to the MCP server's env block in the IDE's MCP config. "
"Never ask them to paste the key into the chat.",
" `artemis init` is interactive and cannot run from a tool call; edit the env file instead.",
@@ -374,30 +367,17 @@ async def _verify_credentials(cred_result: ProbeResult | None) -> list[dict[str,
"""Check every configured key against its provider; the keys never leave this function."""
if cred_result is None:
return []
metadata = cred_result.metadata
api_keys = metadata.get("api_keys") or {}
targets: list[tuple[str, str, str]] = []
for entry in metadata.get("providers") or []:
provider = str(entry.get("provider") or "").strip()
raw_key = entry.get("raw_key") or api_keys.get(provider) or ""
if not provider or not raw_key:
continue
targets.append((provider, str(entry.get("label") or provider), str(raw_key)))
if api_keys.get("ocr"):
targets.append(("ocr", "Vision OCR", str(api_keys["ocr"])))
for provider, label in (("openai", "OpenAI Responses"), ("ocr", "Vision OCR")):
key = settings.get_api_key(provider)
if key:
targets.append((provider, label, key.get_secret_value()))
if not targets:
return []
outcomes = await asyncio.gather(
*(
validate_api_key(
provider,
"EMPTY",
base_url=key,
timeout=CREDENTIAL_CHECK_TIMEOUT_SECONDS,
)
if provider in _ENDPOINT_PROVIDERS
else validate_api_key(provider, key, timeout=CREDENTIAL_CHECK_TIMEOUT_SECONDS)
validate_api_key(provider, key, timeout=settings.ARTEMIS_CREDENTIAL_TIMEOUT_SECONDS)
for provider, _label, key in targets
),
return_exceptions=True,
@@ -747,7 +727,7 @@ def _next_steps(
tag = "REQUIRED" if result.is_blocker else "OPTIONAL"
steps.append(f"[{tag}] {result.title}: {result.description}")
if result.id == "gemini_api_key" and result.status is ProbeStatus.FAIL:
if result.id == "openai_api_key" and result.status is ProbeStatus.FAIL:
steps.extend(_credential_steps(env_file))
needs_restart = True
elif result.id == "android_adb" and in_progress and emulator is not None:
@@ -769,7 +749,7 @@ def _next_steps(
if result.id in ("system_config", "integration_host", "python_runtime"):
needs_restart = True
if result.id == "gemini_api_key":
if result.id == "openai_api_key":
steps.extend(credential_lines)
needs_restart = needs_restart or credential_restart
@@ -986,7 +966,7 @@ async def _run_extras(
helper_serial = _helper_target(adb_result, requested_device)
credentials, device_probe, accessibility_helper = await asyncio.gather(
_verify_credentials(_find(results, "gemini_api_key")) if verify_credentials else _none(),
_verify_credentials(_find(results, "openai_api_key")) if verify_credentials else _none(),
_run_device_probe(adb_result, requested_device) if probe_device else _none(),
asyncio.to_thread(_helper_status, helper_serial) if helper_serial else _none(),
)