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

+20 -72
View File
@@ -16,7 +16,7 @@
import httpx
from artemis.llm.google.provider import is_google_family_provider
from artemis.config.settings import settings
from artemis.utils.logger import get_logger
@@ -60,7 +60,7 @@ async def validate_api_key(
provider: str,
api_key: str,
base_url: str | None = None,
timeout: float = 12.0,
timeout: float | None = None,
) -> tuple[bool, str]:
"""Tests if the provided API key is valid and usable with the corresponding provider.
@@ -90,19 +90,10 @@ async def validate_api_key(
return False, "Please enter a real API key instead of the placeholder."
try:
async with httpx.AsyncClient(timeout=timeout) as client:
if is_google_family_provider(clean_provider):
# Test Google Gemini via generativelanguage API
# The key travels in a header, never in the URL: httpx logs request
# URLs at INFO level and those logs end up in files.
url = "https://generativelanguage.googleapis.com/v1beta/models?pageSize=1"
resp = await client.get(url, headers={"x-goog-api-key": clean_key})
if resp.status_code == 200:
return True, "Google Gemini API key verified successfully."
err_msg = _extract_error_message(resp)
return False, f"Gemini API verification failed ({resp.status_code}): {err_msg}"
elif clean_provider in ("ocr", "vision", "google_vision"):
async with httpx.AsyncClient(
timeout=timeout if timeout is not None else settings.ARTEMIS_CREDENTIAL_TIMEOUT_SECONDS
) as client:
if clean_provider in ("ocr", "vision", "google_vision"):
# Test Google Cloud Vision OCR API
url = "https://vision.googleapis.com/v1/images:annotate"
payload = {
@@ -124,68 +115,25 @@ async def validate_api_key(
return False, f"Vision OCR API verification failed ({resp.status_code}): {err_msg}"
elif clean_provider == "openai":
endpoint = (
f"{base_url.rstrip('/')}/models"
if base_url
else "https://api.openai.com/v1/models"
base = (base_url or settings.OPENAI_BASE_URL or "https://api.openai.com/v1").rstrip(
"/"
)
resp = await client.get(endpoint, headers={"Authorization": f"Bearer {clean_key}"})
if resp.status_code == 200:
return True, "OpenAI API key verified successfully."
err_msg = _extract_error_message(resp)
return False, f"OpenAI API verification failed ({resp.status_code}): {err_msg}"
elif clean_provider in ("anthropic", "claude"):
url = "https://api.anthropic.com/v1/models"
resp = await client.get(
url,
headers={
"x-api-key": clean_key,
"anthropic-version": "2023-06-01",
resp = await client.post(
f"{base}/responses",
headers={"Authorization": f"Bearer {clean_key}"},
json={
"model": settings.OPENAI_MODEL,
"input": "Reply with OK.",
"max_output_tokens": 16,
"store": False,
},
)
if resp.status_code == 200:
return True, "Anthropic Claude API key verified successfully."
err_msg = _extract_error_message(resp)
return False, f"Anthropic API verification failed ({resp.status_code}): {err_msg}"
elif clean_provider == "openrouter":
url = "https://openrouter.ai/api/v1/auth/key"
resp = await client.get(url, headers={"Authorization": f"Bearer {clean_key}"})
if resp.status_code == 200:
return True, "OpenRouter API key verified successfully."
err_msg = _extract_error_message(resp)
return False, f"OpenRouter API verification failed ({resp.status_code}): {err_msg}"
elif clean_provider in ("xai", "grok"):
url = "https://api.x.ai/v1/models"
resp = await client.get(url, headers={"Authorization": f"Bearer {clean_key}"})
if resp.status_code == 200:
return True, "xAI API key verified successfully."
err_msg = _extract_error_message(resp)
return False, f"xAI API verification failed ({resp.status_code}): {err_msg}"
elif clean_provider in ("ollama", "vllm", "custom"):
if base_url:
endpoint = f"{base_url.rstrip('/')}/models"
headers = (
{"Authorization": f"Bearer {clean_key}"} if clean_key != "EMPTY" else {}
)
resp = await client.get(endpoint, headers=headers)
if resp.status_code == 200:
return True, f"Custom endpoint ({base_url}) verified successfully."
err_msg = _extract_error_message(resp)
return (
False,
f"Custom endpoint verification failed ({resp.status_code}): {err_msg}",
)
return True, "Custom endpoint configured."
if resp.status_code == 200 and resp.json().get("output"):
return True, "OpenAI Responses 连接验证成功。"
return False, f"Responses 验证失败(HTTP {resp.status_code})"
else:
# Unknown provider - fallback to basic length check
if len(clean_key) >= 8:
return True, f"Credentials for {provider} configured."
return False, f"Invalid API key format for {provider}."
return False, "仅支持 OpenAI Responses 和可选 OCR 凭据。"
except httpx.TimeoutException:
logger.warning(f"Timeout verifying API key for {provider}")