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

View File

@@ -243,14 +243,17 @@ EOF
}
run_test_import_remote43_provider_subscription_prep() {
local tmpdir fakebin artifact_dir ssh_log psql_sql
local tmpdir fakebin artifact_dir ssh_log psql_sql pack_dir
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' RETURN
fakebin="$tmpdir/bin"
artifact_dir="$tmpdir/artifacts"
ssh_log="$artifact_dir/ssh-log.txt"
psql_sql="$artifact_dir/prep.sql"
pack_dir="$tmpdir/pack"
mkdir -p "$fakebin"
mkdir -p "$pack_dir/providers"
printf '%s\n' '{"provider_id":"deepseek","base_url":"https://upstream.example.com/v1"}' > "$pack_dir/providers/deepseek.json"
cat > "$fakebin/curl" <<'EOF'
#!/usr/bin/env bash
@@ -338,7 +341,7 @@ if [[ "$cmd" == *'***'* ]]; then
echo "unexpected redacted auth placeholder in ssh command: $cmd" >&2
exit 1
fi
case "$cmd" in
case "$cmd" in
*"/api/v1/auth/login"*)
printf '%s\n' 'host-bearer-token'
;;
@@ -390,6 +393,26 @@ case "$cmd" in
"cat /tmp/chat_body.json")
cat /tmp/chat_body.json
;;
*"curl -sS -D /tmp/upstream_models_headers.txt"*)
printf '%s\n' 'HTTP/1.1 200 OK' > /tmp/upstream_models_headers.txt
printf '%s\n' '{"data":[{"id":"openai/gpt-4"},{"id":"openai/gpt-4.1"}]}' > /tmp/upstream_models_body.json
;;
"cat /tmp/upstream_models_headers.txt")
cat /tmp/upstream_models_headers.txt
;;
"cat /tmp/upstream_models_body.json")
cat /tmp/upstream_models_body.json
;;
*"curl -sS -D /tmp/upstream_chat_headers.txt"*)
printf '%s\n' 'HTTP/1.1 200 OK' > /tmp/upstream_chat_headers.txt
printf '%s\n' '{"choices":[{"message":{"content":"upstream-pong"}}]}' > /tmp/upstream_chat_body.txt
;;
"cat /tmp/upstream_chat_headers.txt")
cat /tmp/upstream_chat_headers.txt
;;
"cat /tmp/upstream_chat_body.txt")
cat /tmp/upstream_chat_body.txt
;;
*"/api/providers/deepseek/status"*)
printf '%s\n' '{"status":"ready"}'
;;
@@ -451,7 +474,7 @@ EOF
CRM_HOST_BASE="http://127.0.0.1:18093" \
ROOT="$artifact_dir/root" \
ART="$artifact_dir/run" \
PACK_PATH="/tmp/openai-pack" \
PACK_PATH="$pack_dir" \
REMOTE_PG_CONTAINER="fresh-pg" \
REMOTE_REDIS_CONTAINER="fresh-redis" \
UPSTREAM_KEY="upstream-test-key" \
@@ -474,14 +497,21 @@ EOF
assert_contains "$invalidation_log" "auth_cache_key=apikey:auth:"
assert_contains "$invalidation_log" "balance_cache_key=billing:balance:84"
assert_contains "$invalidation_log" "subscription_cache_key=billing:sub:84:7"
local subscription_state models_body chat_body
local subscription_state models_body chat_body upstream_models upstream_chat summary_json
subscription_state="$(cat "$artifact_dir/run/08-subscription-group-state.json")"
assert_contains "$subscription_state" '"group_id":7'
assert_contains "$subscription_state" '"status":"active"'
models_body="$(cat "$artifact_dir/run/10-models.body.json")"
chat_body="$(cat "$artifact_dir/run/12-chat.body.json")"
upstream_models="$(cat "$artifact_dir/run/18-upstream-models.body.json")"
upstream_chat="$(cat "$artifact_dir/run/20-upstream-chat.body.txt")"
summary_json="$(cat "$artifact_dir/run/21-summary.json" 2>/dev/null || true)"
assert_contains "$models_body" '"id":"gpt-4"'
assert_contains "$chat_body" '"content":"pong"'
assert_contains "$upstream_models" '"id":"openai/gpt-4"'
assert_contains "$upstream_chat" '"content":"upstream-pong"'
assert_contains "$summary_json" '"upstream_models_has_expected_model": true'
assert_contains "$summary_json" '"completion_classification": "unknown"'
[[ -s "$ssh_log" ]] || fail "ssh log was empty"
}