feat: add Chinese UI, Responses models, and mirrored Windows setup
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:
1 parent
371aa6df56
commit
2ddec53954
183 files changed
+3427
-11857
No files matched your search
@@ -26,11 +26,11 @@ from artemis.core.diagnostics.schema import (
|
||||
|
||||
|
||||
class LLMCredentialsProbe(BaseProbe):
|
||||
"""Probe verifying Gemini and multi-provider multimodal LLM credentials."""
|
||||
"""Readiness depends on an OpenAI credential, never merely a base URL."""
|
||||
|
||||
@property
|
||||
def probe_id(self) -> str:
|
||||
return "gemini_api_key"
|
||||
return "openai_api_key"
|
||||
|
||||
@property
|
||||
def category(self) -> ProbeCategory:
|
||||
@@ -40,192 +40,38 @@ class LLMCredentialsProbe(BaseProbe):
|
||||
def is_blocker(self) -> bool:
|
||||
return True
|
||||
|
||||
def _mask_key(self, key_str: str) -> str:
|
||||
"""Helper to safely mask an API credential for display."""
|
||||
if len(key_str) > 10:
|
||||
return f"{key_str[:6]}...{key_str[-4:]}"
|
||||
return "***"
|
||||
|
||||
async def probe(self) -> ProbeResult:
|
||||
import os
|
||||
|
||||
from artemis.config.settings import is_placeholder_key
|
||||
|
||||
gemini_key = settings.get_api_key("google")
|
||||
openai_key = settings.get_api_key("openai")
|
||||
claude_key = settings.get_api_key("anthropic")
|
||||
openrouter_key = settings.get_api_key("openrouter")
|
||||
xai_key = settings.get_api_key("xai")
|
||||
ocr_key = settings.get_api_key("ocr")
|
||||
|
||||
configured_providers: list[dict[str, Any]] = []
|
||||
api_keys_map: dict[str, str] = {}
|
||||
if gemini_key and not is_placeholder_key(gemini_key):
|
||||
g_val = gemini_key.get_secret_value()
|
||||
configured_providers.append(
|
||||
{
|
||||
"provider": "google",
|
||||
"label": "Gemini",
|
||||
"masked": self._mask_key(g_val),
|
||||
"raw_key": g_val,
|
||||
"key": g_val,
|
||||
}
|
||||
)
|
||||
api_keys_map["google"] = g_val
|
||||
api_keys_map["gemini"] = g_val
|
||||
if openai_key and not is_placeholder_key(openai_key):
|
||||
o_val = openai_key.get_secret_value()
|
||||
configured_providers.append(
|
||||
key = settings.get_api_key("openai")
|
||||
configured = bool(key)
|
||||
providers = (
|
||||
[
|
||||
{
|
||||
"provider": "openai",
|
||||
"label": "ChatGPT",
|
||||
"masked": self._mask_key(o_val),
|
||||
"raw_key": o_val,
|
||||
"key": o_val,
|
||||
"label": "OpenAI Responses",
|
||||
"masked": "****" + key.get_secret_value()[-4:],
|
||||
}
|
||||
)
|
||||
api_keys_map["openai"] = o_val
|
||||
if claude_key and not is_placeholder_key(claude_key):
|
||||
c_val = claude_key.get_secret_value()
|
||||
configured_providers.append(
|
||||
{
|
||||
"provider": "anthropic",
|
||||
"label": "Claude",
|
||||
"masked": self._mask_key(c_val),
|
||||
"raw_key": c_val,
|
||||
"key": c_val,
|
||||
}
|
||||
)
|
||||
api_keys_map["anthropic"] = c_val
|
||||
if openrouter_key and not is_placeholder_key(openrouter_key):
|
||||
or_val = openrouter_key.get_secret_value()
|
||||
configured_providers.append(
|
||||
{
|
||||
"provider": "openrouter",
|
||||
"label": "OpenRouter",
|
||||
"masked": self._mask_key(or_val),
|
||||
"raw_key": or_val,
|
||||
"key": or_val,
|
||||
}
|
||||
)
|
||||
api_keys_map["openrouter"] = or_val
|
||||
if xai_key and not is_placeholder_key(xai_key):
|
||||
x_val = xai_key.get_secret_value()
|
||||
configured_providers.append(
|
||||
{
|
||||
"provider": "xai",
|
||||
"label": "xAI (Grok)",
|
||||
"masked": self._mask_key(x_val),
|
||||
"raw_key": x_val,
|
||||
"key": x_val,
|
||||
}
|
||||
)
|
||||
api_keys_map["xai"] = x_val
|
||||
|
||||
# Detect any custom model endpoints or environment variables defined in files
|
||||
for env_var, label, prov_id in [
|
||||
("DEEPSEEK_API_KEY", "DeepSeek", "deepseek"),
|
||||
("GROQ_API_KEY", "Groq", "groq"),
|
||||
("OPENAI_BASE_URL", "Custom OpenAI Endpoint", "custom"),
|
||||
("OLLAMA_BASE_URL", "Local Ollama", "ollama"),
|
||||
("VLLM_BASE_URL", "vLLM Endpoint", "vllm"),
|
||||
("VERTEX_AI_PROJECT", "Google Cloud Vertex AI", "vertexai"),
|
||||
]:
|
||||
val = os.environ.get(env_var)
|
||||
if val and val.strip() and not is_placeholder_key(val.strip()):
|
||||
configured_providers.append(
|
||||
{
|
||||
"provider": prov_id,
|
||||
"label": label,
|
||||
"masked": self._mask_key(val.strip()),
|
||||
"raw_key": val.strip(),
|
||||
"key": val.strip(),
|
||||
}
|
||||
)
|
||||
api_keys_map[prov_id] = val.strip()
|
||||
|
||||
if ocr_key and not is_placeholder_key(ocr_key):
|
||||
api_keys_map["ocr"] = ocr_key.get_secret_value()
|
||||
|
||||
current_active_key = (
|
||||
gemini_key.get_secret_value()
|
||||
if gemini_key
|
||||
else (configured_providers[0]["key"] if configured_providers else "")
|
||||
]
|
||||
if key
|
||||
else []
|
||||
)
|
||||
|
||||
metadata = {
|
||||
"configured_count": len(configured_providers),
|
||||
"providers": configured_providers,
|
||||
"has_ocr_key": ocr_key is not None,
|
||||
"current_key": current_active_key,
|
||||
"current_gemini_key": gemini_key.get_secret_value() if gemini_key else "",
|
||||
"api_keys": api_keys_map,
|
||||
}
|
||||
|
||||
# Case 1: Gemini API Key configured (Standard / Recommended)
|
||||
if gemini_key:
|
||||
masked = self._mask_key(gemini_key.get_secret_value())
|
||||
return ProbeResult(
|
||||
id=self.probe_id,
|
||||
category=self.category,
|
||||
title="Multimodal LLM API Key",
|
||||
status=ProbeStatus.PASS,
|
||||
is_blocker=self.is_blocker,
|
||||
summary="Active (Gemini)",
|
||||
description=f"Gemini multimodal API credential is active ({masked}) and ready for vision and reasoning.",
|
||||
metadata=metadata,
|
||||
actions=[
|
||||
ProbeAction(
|
||||
action_type="hint",
|
||||
label="Provider Active",
|
||||
payload="Gemini 2.5 Flash / Pro Multimodal Engine is enabled.",
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
# Case 2: Other LLM provider configured
|
||||
if configured_providers:
|
||||
first_p = configured_providers[0]
|
||||
return ProbeResult(
|
||||
id=self.probe_id,
|
||||
category=self.category,
|
||||
title="Multimodal LLM API Key",
|
||||
status=ProbeStatus.PASS,
|
||||
is_blocker=self.is_blocker,
|
||||
summary=f"Active ({first_p['label']})",
|
||||
description=f"{first_p['label']} multimodal API credential is active ({first_p['masked']}).",
|
||||
metadata=metadata,
|
||||
actions=[
|
||||
ProbeAction(
|
||||
action_type="hint",
|
||||
label="Provider Active",
|
||||
payload=f"{first_p['label']} multimodal vision endpoint is active.",
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
# Case 3: No LLM key configured
|
||||
return ProbeResult(
|
||||
id=self.probe_id,
|
||||
category=self.category,
|
||||
title="Multimodal LLM API Key",
|
||||
status=ProbeStatus.FAIL,
|
||||
is_blocker=self.is_blocker,
|
||||
summary="Key Missing",
|
||||
description="No Multimodal LLM credential (e.g. GEMINI_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY) found in environment or .env file.",
|
||||
metadata=metadata,
|
||||
actions=[
|
||||
ProbeAction(
|
||||
action_type="command",
|
||||
label="Run Artemis Init",
|
||||
payload="artemis init",
|
||||
),
|
||||
ProbeAction(
|
||||
action_type="link",
|
||||
label="Get Google AI Studio Key",
|
||||
payload="https://aistudio.google.com/app/apikey",
|
||||
),
|
||||
],
|
||||
title="OpenAI API 密钥",
|
||||
status=ProbeStatus.PASS if configured else ProbeStatus.FAIL,
|
||||
is_blocker=True,
|
||||
summary="已配置 OpenAI Responses" if configured else "缺少 API 密钥",
|
||||
description="模型统一通过 OpenAI Responses 接口调用。"
|
||||
if configured
|
||||
else "请在环境变量或 .env 中设置 OPENAI_API_KEY。",
|
||||
metadata={
|
||||
"configured_count": len(providers),
|
||||
"providers": providers,
|
||||
"has_ocr_key": bool(settings.get_api_key("ocr")),
|
||||
},
|
||||
actions=[]
|
||||
if configured
|
||||
else [ProbeAction(action_type="command", label="初始化配置", payload="artemis init")],
|
||||
)
|
||||
|
||||
|
||||
@@ -267,7 +113,7 @@ class VisionOCRProbe(BaseProbe):
|
||||
is_blocker=False,
|
||||
summary="Active & Configured",
|
||||
description=f"Google Cloud Vision OCR ({masked}) is active for image text recognition.",
|
||||
metadata={"configured": True, "masked_key": masked, "key": val, "raw_key": val},
|
||||
metadata={"configured": True, "masked_key": masked},
|
||||
actions=[
|
||||
ProbeAction(
|
||||
action_type="hint",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import platform
|
||||
import sys
|
||||
|
||||
from artemis.config import parse_llm_config
|
||||
from artemis.config import parse_llm_config, settings
|
||||
from artemis.core.diagnostics.probes.base import BaseProbe
|
||||
from artemis.core.diagnostics.schema import (
|
||||
ProbeAction,
|
||||
@@ -67,7 +67,7 @@ class PythonRuntimeProbe(BaseProbe):
|
||||
title="Python Runtime Environment",
|
||||
status=ProbeStatus.PASS,
|
||||
is_blocker=self.is_blocker,
|
||||
summary=f"Python {py_ver_str} Ready",
|
||||
summary=f"Python {py_ver_str} 已就绪",
|
||||
description=f"Python {py_ver_str} ({arch}, 64-bit) detected at {executable}. Satisfies requirement (>= 3.12).",
|
||||
metadata=metadata,
|
||||
actions=[
|
||||
@@ -121,7 +121,7 @@ class SystemConfigProbe(BaseProbe):
|
||||
async def probe(self) -> ProbeResult:
|
||||
try:
|
||||
cfg = parse_llm_config()
|
||||
planner_model = getattr(getattr(cfg, "planner", None), "model", "Gemini 2.5 Flash")
|
||||
planner_model = getattr(getattr(cfg, "planner", None), "model", settings.OPENAI_MODEL)
|
||||
metadata = {
|
||||
"valid": True,
|
||||
"config_file": "config/artemis.jsonc",
|
||||
@@ -133,7 +133,7 @@ class SystemConfigProbe(BaseProbe):
|
||||
title="Artemis System Configuration",
|
||||
status=ProbeStatus.PASS,
|
||||
is_blocker=self.is_blocker,
|
||||
summary="Config Valid",
|
||||
summary="配置有效",
|
||||
description=f"System configuration loaded and validated (Planner model: {planner_model}).",
|
||||
metadata=metadata,
|
||||
actions=[
|
||||
|
||||
@@ -72,7 +72,7 @@ class ToolchainProbe(BaseProbe):
|
||||
title="Video Recording Toolchain",
|
||||
status=ProbeStatus.PASS,
|
||||
is_blocker=self.is_blocker,
|
||||
summary="Ready (FFmpeg + scrcpy)",
|
||||
summary="录屏工具已就绪(FFmpeg + scrcpy)",
|
||||
description=f"FFmpeg ({ffmpeg_path}) and scrcpy ({scrcpy_path}) are available. Live stream and high-speed video replays are active.",
|
||||
metadata=metadata,
|
||||
actions=[
|
||||
@@ -132,7 +132,7 @@ class ToolchainProbe(BaseProbe):
|
||||
title="Video Recording Toolchain",
|
||||
status=ProbeStatus.FAIL,
|
||||
is_blocker=self.is_blocker,
|
||||
summary=f"Missing {missing_str}",
|
||||
summary=f"缺少 {missing_str}",
|
||||
description=f"Video toolchain is required for screen streaming and test replay recording. Missing: {missing_str}.",
|
||||
metadata=metadata,
|
||||
actions=actions,
|
||||
|
||||
@@ -39,7 +39,7 @@ CHECK_ORDER: dict[str, int] = {
|
||||
"python_runtime": 0,
|
||||
"system_config": 1,
|
||||
"integration_host": 2,
|
||||
"gemini_api_key": 3,
|
||||
"openai_api_key": 3,
|
||||
"android_adb": 4,
|
||||
"toolchain": 5,
|
||||
"vision_ocr_key": 6,
|
||||
|
||||
@@ -78,7 +78,7 @@ class ProbeResult(BaseModel):
|
||||
"""Outcome of a single readiness probe check."""
|
||||
|
||||
id: str = Field(
|
||||
description="Unique identifier for the probe (e.g. 'android_adb', 'gemini_api_key')"
|
||||
description="Unique identifier for the probe (e.g. 'android_adb', 'openai_api_key')"
|
||||
)
|
||||
category: ProbeCategory = Field(description="Category of the probe")
|
||||
title: str = Field(description="Human-readable title (e.g. 'Device / Emulator Connected')")
|
||||
|
||||
Reference in new issue
Block a user