fix fresh-host acceptance and document real-host debugging learnings

This commit is contained in:
phamnazage-jpg
2026-05-21 21:19:19 +08:00
parent 7c6e18f94d
commit 3ba3244ea6
85 changed files with 1721 additions and 162 deletions

View File

@@ -41,6 +41,20 @@ if [[ -z "$upstream_key" ]]; then
exit 2
fi
upstream_base_url="$(python3 - "$PACK_PATH" "$provider_id" <<'PY'
import json, pathlib, sys
pack_path = pathlib.Path(sys.argv[1])
provider_id = sys.argv[2]
provider_file = pack_path / "providers" / f"{provider_id}.json"
provider = json.loads(provider_file.read_text(encoding='utf-8'))
print(str(provider.get("base_url", "")).strip())
PY
)"
if [[ -z "$upstream_base_url" ]]; then
echo "missing provider base_url for $provider_id in $PACK_PATH" >&2
exit 2
fi
ssh_cmd() {
local cmd="$1"
ssh -i "$KEY" -o StrictHostKeyChecking=no "$REMOTE" "$cmd"
@@ -454,6 +468,14 @@ ssh_cmd "curl -sS -D /tmp/chat_headers.txt -o /tmp/chat_body.json -H 'Authorizat
ssh_cmd "cat /tmp/chat_headers.txt" > "$ART/11-chat.headers.txt"
ssh_cmd "cat /tmp/chat_body.json" > "$ART/12-chat.body.json"
ssh_cmd "curl -sS -D /tmp/upstream_models_headers.txt -o /tmp/upstream_models_body.json -H 'Authorization: Bearer $upstream_key' ${upstream_base_url%/}/models"
ssh_cmd "cat /tmp/upstream_models_headers.txt" > "$ART/17-upstream-models.headers.txt"
ssh_cmd "cat /tmp/upstream_models_body.json" > "$ART/18-upstream-models.body.json"
ssh_cmd "curl -sS -D /tmp/upstream_chat_headers.txt -o /tmp/upstream_chat_body.txt -H 'Authorization: Bearer $upstream_key' -H 'Content-Type: application/json' ${upstream_base_url%/}/chat/completions -d $(printf %q "$probe_payload")"
ssh_cmd "cat /tmp/upstream_chat_headers.txt" > "$ART/19-upstream-chat.headers.txt"
ssh_cmd "cat /tmp/upstream_chat_body.txt" > "$ART/20-upstream-chat.body.txt"
crm_curl_json GET "/api/providers/$provider_id/status" > "$ART/13-provider-status.json"
crm_curl_json GET "/api/providers/$provider_id/access/status" > "$ART/14-access-status.json"
preview_payload="$(python3 - "$provider_id" <<'PY'
@@ -472,17 +494,67 @@ provider_id=sys.argv[2]
batch_id=int(sys.argv[3])
subscription_group_id=sys.argv[4]
expected_model=sys.argv[5]
def normalize_model_id(model_id: str) -> str:
value = str(model_id or '').strip().lower()
if not value:
return ''
if '/' in value:
value = value.split('/')[-1]
return value
def has_expected_model(models, expected: str) -> bool:
normalized_expected = normalize_model_id(expected)
if not normalized_expected:
return False
return any(normalize_model_id(model_id) == normalized_expected for model_id in models)
def status_from_headers(path: pathlib.Path) -> int:
if not path.exists():
return 0
for line in path.read_text(encoding='utf-8').splitlines():
parts = line.strip().split()
if len(parts) >= 2 and parts[0].startswith('HTTP/'):
try:
return int(parts[1])
except ValueError:
return 0
return 0
def load_json(path: pathlib.Path):
try:
return json.loads(path.read_text(encoding='utf-8'))
except Exception:
return {}
import_obj=json.loads((art/'03-import.body.json').read_text())
models_obj=json.loads((art/'10-models.body.json').read_text())
access_status=json.loads((art/'14-access-status.json').read_text())
preview=json.loads((art/'15-access-preview.json').read_text())
models_obj=load_json(art/'10-models.body.json')
access_status=load_json(art/'14-access-status.json')
preview=load_json(art/'15-access-preview.json')
models_headers=(art/'09-models.headers.txt').read_text()
chat_headers=(art/'11-chat.headers.txt').read_text()
upstream_models_obj=load_json(art/'18-upstream-models.body.json')
upstream_chat_headers=(art/'19-upstream-chat.headers.txt')
upstream_chat_body=(art/'20-upstream-chat.body.txt').read_text(encoding='utf-8')
models=[]
for item in models_obj.get('data') or []:
model_id = item.get('id')
if isinstance(model_id, str) and model_id:
models.append(model_id)
upstream_models=[]
for item in upstream_models_obj.get('data') or []:
model_id = item.get('id')
if isinstance(model_id, str) and model_id:
upstream_models.append(model_id)
host_chat_status = status_from_headers(art/'11-chat.headers.txt')
upstream_chat_status = status_from_headers(upstream_chat_headers)
classification = 'unknown'
direct_has_expected_model = has_expected_model(models, expected_model)
upstream_has_expected_model = has_expected_model(upstream_models, expected_model)
if direct_has_expected_model and host_chat_status >= 500 and upstream_chat_status == 200:
classification = 'host_compatibility_gap'
elif direct_has_expected_model and upstream_chat_status == 403 and 'insufficient_user_quota' in upstream_chat_body:
classification = 'upstream_key_quota_issue'
summary={
'artifact_dir': str(art),
'provider_id': provider_id,
@@ -491,14 +563,21 @@ summary={
'access_status_from_import': import_obj.get('access_status'),
'provider_status_from_import': import_obj.get('provider_status'),
'direct_models_http200': '200 OK' in models_headers,
'direct_models_has_expected_model': expected_model in models,
'direct_models_has_expected_model': direct_has_expected_model,
'direct_models': models,
'direct_chat_http200': '200 OK' in chat_headers,
'direct_chat_status': host_chat_status,
'upstream_models': upstream_models,
'upstream_models_has_expected_model': upstream_has_expected_model,
'upstream_chat_status': upstream_chat_status,
'completion_classification': classification,
'latest_access_status': access_status.get('latest_access_status') or access_status.get('batch_access_status'),
'preview_available': preview.get('available'),
'accepted_keys_count': import_obj.get('accepted_keys_count'),
'subscription_group_id': subscription_group_id,
'import_group_id': (import_obj.get('group') or {}).get('id'),
}
print(json.dumps(summary, ensure_ascii=False))
summary_json = json.dumps(summary, ensure_ascii=False)
(art / '21-summary.json').write_text(summary_json, encoding='utf-8')
print(summary_json)
PY