liuzhao521 commited on
Commit
f12ea8b
·
1 Parent(s): 054b72c

Update app.py and add model discovery tools

Browse files
.github/workflows/docker-publish.yml DELETED
@@ -1,98 +0,0 @@
1
- name: Docker
2
-
3
- # This workflow uses actions that are not certified by GitHub.
4
- # They are provided by a third-party and are governed by
5
- # separate terms of service, privacy policy, and support
6
- # documentation.
7
-
8
- on:
9
- schedule:
10
- - cron: '17 23 * * *'
11
- push:
12
- branches: [ "main" ]
13
- # Publish semver tags as releases.
14
- tags: [ 'v*.*.*' ]
15
- pull_request:
16
- branches: [ "main" ]
17
-
18
- env:
19
- # Use docker.io for Docker Hub if empty
20
- REGISTRY: ghcr.io
21
- # github.repository as <account>/<repo>
22
- IMAGE_NAME: ${{ github.repository }}
23
-
24
-
25
- jobs:
26
- build:
27
-
28
- runs-on: ubuntu-latest
29
- permissions:
30
- contents: read
31
- packages: write
32
- # This is used to complete the identity challenge
33
- # with sigstore/fulcio when running outside of PRs.
34
- id-token: write
35
-
36
- steps:
37
- - name: Checkout repository
38
- uses: actions/checkout@v4
39
-
40
- # Install the cosign tool except on PR
41
- # https://github.com/sigstore/cosign-installer
42
- - name: Install cosign
43
- if: github.event_name != 'pull_request'
44
- uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0
45
- with:
46
- cosign-release: 'v2.2.4'
47
-
48
- # Set up BuildKit Docker container builder to be able to build
49
- # multi-platform images and export cache
50
- # https://github.com/docker/setup-buildx-action
51
- - name: Set up Docker Buildx
52
- uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
53
-
54
- # Login against a Docker registry except on PR
55
- # https://github.com/docker/login-action
56
- - name: Log into registry ${{ env.REGISTRY }}
57
- if: github.event_name != 'pull_request'
58
- uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
59
- with:
60
- registry: ${{ env.REGISTRY }}
61
- username: ${{ github.actor }}
62
- password: ${{ secrets.GITHUB_TOKEN }}
63
-
64
- # Extract metadata (tags, labels) for Docker
65
- # https://github.com/docker/metadata-action
66
- - name: Extract Docker metadata
67
- id: meta
68
- uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
69
- with:
70
- images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
71
-
72
- # Build and push Docker image with Buildx (don't push on PR)
73
- # https://github.com/docker/build-push-action
74
- - name: Build and push Docker image
75
- id: build-and-push
76
- uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
77
- with:
78
- context: .
79
- push: ${{ github.event_name != 'pull_request' }}
80
- tags: ${{ steps.meta.outputs.tags }}
81
- labels: ${{ steps.meta.outputs.labels }}
82
- cache-from: type=gha
83
- cache-to: type=gha,mode=max
84
-
85
- # Sign the resulting Docker image digest except on PRs.
86
- # This will only write to the public Rekor transparency log when the Docker
87
- # repository is public to avoid leaking data. If you would like to publish
88
- # transparency data even for private images, pass --force to cosign below.
89
- # https://github.com/sigstore/cosign
90
- - name: Sign the published Docker image
91
- if: ${{ github.event_name != 'pull_request' }}
92
- env:
93
- # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
94
- TAGS: ${{ steps.meta.outputs.tags }}
95
- DIGEST: ${{ steps.build-and-push.outputs.digest }}
96
- # This step uses the identity token to provision an ephemeral certificate
97
- # against the sigstore community Fulcio instance.
98
- run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app.py CHANGED
@@ -374,6 +374,24 @@ def _openai_non_streaming_response(text: str, model: Optional[str]) -> Dict[str,
374
  def _sse_format(obj: Dict[str, Any]) -> str:
375
  return f"data: {json.dumps(obj, ensure_ascii=False)}\n\n"
376
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
  @app.post("/v1/chat/completions")
378
  def chat_completions(req: ChatCompletionRequest, account: Dict[str, Any] = Depends(require_account)):
379
  """
 
374
  def _sse_format(obj: Dict[str, Any]) -> str:
375
  return f"data: {json.dumps(obj, ensure_ascii=False)}\n\n"
376
 
377
+ @app.get("/v1/models")
378
+ def list_models():
379
+ """
380
+ OpenAI-compatible models endpoint.
381
+ Returns the available models that can be used with this API.
382
+ """
383
+ return {
384
+ "object": "list",
385
+ "data": [
386
+ {
387
+ "id": "claude-sonnet-4",
388
+ "object": "model",
389
+ "created": int(time.time()),
390
+ "owned_by": "amazon-q"
391
+ }
392
+ ]
393
+ }
394
+
395
  @app.post("/v1/chat/completions")
396
  def chat_completions(req: ChatCompletionRequest, account: Dict[str, Any] = Depends(require_account)):
397
  """
available_models.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ 可用模型列表:
2
+ claude-sonnet-4
check_docs.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ 检查 API 文档和规范
4
+ """
5
+
6
+ import requests
7
+ import json
8
+
9
+ def check_openapi_spec():
10
+ """检查 OpenAPI 规范"""
11
+ try:
12
+ response = requests.get("http://localhost:8000/openapi.json", timeout=10)
13
+ if response.status_code == 200:
14
+ spec = response.json()
15
+
16
+ print("📋 OpenAPI 规范分析")
17
+ print("=" * 50)
18
+
19
+ # 查找模型相关的信息
20
+ def find_model_info(obj, path=""):
21
+ models_found = []
22
+
23
+ if isinstance(obj, dict):
24
+ for key, value in obj.items():
25
+ current_path = f"{path}.{key}" if path else key
26
+
27
+ if "model" in key.lower() or "claude" in key.lower() or "anthropic" in key.lower():
28
+ models_found.append((current_path, value))
29
+
30
+ models_found.extend(find_model_info(value, current_path))
31
+
32
+ elif isinstance(obj, list):
33
+ for i, item in enumerate(obj):
34
+ models_found.extend(find_model_info(item, f"{path}[{i}]"))
35
+
36
+ return models_found
37
+
38
+ model_info = find_model_info(spec)
39
+
40
+ if model_info:
41
+ print("🔍 发现模型相关信息:")
42
+ for path, value in model_info[:10]: # 只显示前10个
43
+ print(f" {path}: {value}")
44
+ else:
45
+ print("❌ 未在 OpenAPI 规范中发现模型信息")
46
+
47
+ # 检查 schemas
48
+ if "components" in spec and "schemas" in spec["components"]:
49
+ schemas = spec["components"]["schemas"]
50
+ print(f"\n📝 发现 {len(schemas)} 个 schema:")
51
+ for schema_name in schemas.keys():
52
+ print(f" • {schema_name}")
53
+
54
+ # 保存完整规范
55
+ with open("openapi_spec.json", "w", encoding="utf-8") as f:
56
+ json.dump(spec, f, ensure_ascii=False, indent=2)
57
+ print(f"\n📁 完整 OpenAPI 规范已保存到: openapi_spec.json")
58
+
59
+ else:
60
+ print(f"❌ 获取 OpenAPI 规范失败: {response.status_code}")
61
+
62
+ except Exception as e:
63
+ print(f"❌ 检查 OpenAPI 规范异常: {e}")
64
+
65
+ def check_docs_page():
66
+ """检查文档页面"""
67
+ try:
68
+ response = requests.get("http://localhost:8000/docs", timeout=10)
69
+ if response.status_code == 200:
70
+ print("📖 /docs 页面可访问")
71
+ print("建议在浏览器中访问 http://localhost:8000/docs 查看完整文档")
72
+ else:
73
+ print(f"❌ /docs 页面不可访问: {response.status_code}")
74
+ except Exception as e:
75
+ print(f"❌ 检查 /docs 页面异常: {e}")
76
+
77
+ if __name__ == "__main__":
78
+ check_openapi_spec()
79
+ check_docs_page()
discover_models.py ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ 尝试获取 Amazon Q API 支持的所有模型
4
+ """
5
+
6
+ import requests
7
+ import json
8
+ import re
9
+ from typing import List, Dict, Optional
10
+
11
+ BASE_URL = "http://localhost:8000"
12
+
13
+ def try_models_endpoint():
14
+ """尝试调用 /v1/models 端点(如果存在)"""
15
+ try:
16
+ response = requests.get(f"{BASE_URL}/v1/models", timeout=10)
17
+ if response.status_code == 200:
18
+ data = response.json()
19
+ print("✅ 发现 /v1/models 端点")
20
+ return data
21
+ else:
22
+ print(f"❌ /v1/models 端点返回: {response.status_code}")
23
+ return None
24
+ except Exception as e:
25
+ print(f"❌ /v1/models 端点异常: {e}")
26
+ return None
27
+
28
+ def extract_model_from_error(error_text: str) -> List[str]:
29
+ """从错误信息中提取可能的模型名称"""
30
+ models = []
31
+
32
+ # 常见的模型名称模式
33
+ patterns = [
34
+ r'claude-[\w\.-]+',
35
+ r'anthropic\.[\w\.-]+',
36
+ r'model\s+["\']?([a-zA-Z0-9_\.-]+)["\']?',
37
+ r'available\s+models?[:\s]+([^\n\r]+)',
38
+ r'supported\s+models?[:\s]+([^\n\r]+)',
39
+ ]
40
+
41
+ for pattern in patterns:
42
+ matches = re.findall(pattern, error_text, re.IGNORECASE)
43
+ models.extend(matches)
44
+
45
+ return list(set(models)) # 去重
46
+
47
+ def try_common_amazon_q_models() -> List[str]:
48
+ """尝试 Amazon Q 可能支持的其他模型名称"""
49
+ # 基于 AWS Bedrock 和 Amazon Q 的常见模型命名
50
+ amazon_q_models = [
51
+ # Claude 模型 (AWS Bedrock 格式)
52
+ "anthropic.claude-v2:1",
53
+ "anthropic.claude-v2",
54
+ "anthropic.claude-instant-v1",
55
+ "anthropic.claude-3-sonnet-20240229-v1:0",
56
+ "anthropic.claude-3-sonnet-20240229-v1",
57
+ "anthropic.claude-3-haiku-20240307-v1:0",
58
+ "anthropic.claude-3-haiku-20240307-v1",
59
+ "anthropic.claude-3-opus-20240229-v1:0",
60
+ "anthropic.claude-3-opus-20240229-v1",
61
+
62
+ # Amazon Q 特定模型
63
+ "amazon.q",
64
+ "amazon.q-turbo",
65
+ "amazon.q-pro",
66
+ "amazon.q-max",
67
+ "q.amazon",
68
+ "q-turbo.amazon",
69
+ "q-pro.amazon",
70
+ "q-max.amazon",
71
+
72
+ # 其他可能的命名
73
+ "claude",
74
+ "claude-v2",
75
+ "claude-instant",
76
+ "claude-3-sonnet",
77
+ "claude-3-haiku",
78
+ "claude-3-opus",
79
+ "sonnet",
80
+ "haiku",
81
+ "opus",
82
+
83
+ # 通用模型名
84
+ "default",
85
+ "base",
86
+ "latest",
87
+ "text-davinci-003", # 测试是否支持OpenAI模型名
88
+ "gpt-3.5-turbo",
89
+ "gpt-4",
90
+ ]
91
+
92
+ return amazon_q_models
93
+
94
+ def test_model_with_details(model_name: str) -> Dict[str, any]:
95
+ """详细测试单个模型"""
96
+ url = f"{BASE_URL}/v1/chat/completions"
97
+
98
+ payload = {
99
+ "model": model_name,
100
+ "messages": [
101
+ {"role": "user", "content": "test"}
102
+ ],
103
+ "stream": False,
104
+ "max_tokens": 5
105
+ }
106
+
107
+ try:
108
+ response = requests.post(url, json=payload, timeout=15)
109
+
110
+ result = {
111
+ "model": model_name,
112
+ "status_code": response.status_code,
113
+ "available": response.status_code == 200,
114
+ "error": None,
115
+ "error_details": None
116
+ }
117
+
118
+ if response.status_code != 200:
119
+ result["error"] = response.text[:300]
120
+ # 尝试从错误中提取模型信息
121
+ extracted_models = extract_model_from_error(response.text)
122
+ if extracted_models:
123
+ result["extracted_models"] = extracted_models
124
+
125
+ return result
126
+
127
+ except Exception as e:
128
+ return {
129
+ "model": model_name,
130
+ "status_code": None,
131
+ "available": False,
132
+ "error": str(e),
133
+ "error_details": None
134
+ }
135
+
136
+ def comprehensive_model_search():
137
+ """全面搜索可用模型"""
138
+ print("🔍 全面搜索 Amazon Q API 支持的模型")
139
+ print("=" * 60)
140
+
141
+ # 1. 尝试标准 models 端点
142
+ print("\n1️⃣ 尝试标准 /v1/models 端点...")
143
+ models_data = try_models_endpoint()
144
+ if models_data:
145
+ print("发现模型列表:")
146
+ if isinstance(models_data, dict) and "data" in models_data:
147
+ for model in models_data["data"]:
148
+ print(f" • {model.get('id', 'unknown')}")
149
+ elif isinstance(models_data, list):
150
+ for model in models_data:
151
+ if isinstance(model, dict):
152
+ print(f" • {model.get('id', model.get('model', 'unknown'))}")
153
+ else:
154
+ print(f" • {model}")
155
+ return models_data
156
+
157
+ # 2. 测试更多模型名称
158
+ print("\n2️⃣ 测试扩展的模型名称列表...")
159
+ additional_models = try_common_amazon_q_models()
160
+
161
+ print(f"测试 {len(additional_models)} 个额外的模型名称...")
162
+
163
+ available_models = []
164
+ error_models = []
165
+ extracted_from_errors = set()
166
+
167
+ for i, model in enumerate(additional_models, 1):
168
+ print(f"[{i}/{len(additional_models)}] {model}...", end=" ")
169
+
170
+ result = test_model_with_details(model)
171
+
172
+ if result["available"]:
173
+ print("✅")
174
+ available_models.append(model)
175
+ else:
176
+ print("❌")
177
+ error_models.append((model, result["error"]))
178
+
179
+ # 从错误中提取模型信息
180
+ if "extracted_models" in result:
181
+ extracted_from_errors.update(result["extracted_models"])
182
+
183
+ # 避免请求过快
184
+ import time
185
+ time.sleep(0.3)
186
+
187
+ # 3. 输出结果
188
+ print("\n" + "=" * 60)
189
+ print("📊 搜索结果")
190
+ print("=" * 60)
191
+
192
+ if available_models:
193
+ print(f"\n✅ 发现可用模型 ({len(available_models)} 个):")
194
+ for model in available_models:
195
+ print(f" • {model}")
196
+ else:
197
+ print("\n❌ 没有发现新的可用模型")
198
+
199
+ if extracted_from_errors:
200
+ print(f"\n🔍 从错误信息中提取的可能模型 ({len(extracted_from_errors)} 个):")
201
+ for model in sorted(extracted_from_errors):
202
+ print(f" • {model}")
203
+
204
+ # 4. 保存结果
205
+ search_results = {
206
+ "available_models": available_models,
207
+ "error_models": [{"model": m, "error": e} for m, e in error_models[:10]], # 只保存前10个错误
208
+ "extracted_from_errors": list(extracted_from_errors),
209
+ "timestamp": str(time.time())
210
+ }
211
+
212
+ with open("model_search_results.json", "w", encoding="utf-8") as f:
213
+ json.dump(search_results, f, ensure_ascii=False, indent=2)
214
+
215
+ print(f"\n📁 详细结果已保存到: model_search_results.json")
216
+
217
+ return search_results
218
+
219
+ def inspect_api_info():
220
+ """检查API的其他信息端点"""
221
+ endpoints_to_try = [
222
+ "/",
223
+ "/docs",
224
+ "/openapi.json",
225
+ "/info",
226
+ "/v1",
227
+ "/status",
228
+ "/health"
229
+ ]
230
+
231
+ print("\n🔍 检查API信息端点...")
232
+
233
+ for endpoint in endpoints_to_try:
234
+ try:
235
+ response = requests.get(f"{BASE_URL}{endpoint}", timeout=5)
236
+ if response.status_code == 200:
237
+ print(f"✅ {endpoint} - 可访问")
238
+ # 检查是否包含模型信息
239
+ content = response.text.lower()
240
+ if any(keyword in content for keyword in ["model", "claude", "anthropic"]):
241
+ print(f" 📄 可能包含模型相关信息")
242
+ else:
243
+ print(f"❌ {endpoint} - {response.status_code}")
244
+ except:
245
+ print(f"❌ {endpoint} - 连接失败")
246
+
247
+ if __name__ == "__main__":
248
+ import time
249
+
250
+ # 检查API信息端点
251
+ inspect_api_info()
252
+
253
+ # 全面模型搜索
254
+ comprehensive_model_search()
255
+
256
+ print("\n✨ 搜索完成!")
model_search_results.json ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "available_models": [],
3
+ "error_models": [
4
+ {
5
+ "model": "anthropic.claude-v2:1",
6
+ "error": "Internal Server Error"
7
+ },
8
+ {
9
+ "model": "anthropic.claude-v2",
10
+ "error": "Internal Server Error"
11
+ },
12
+ {
13
+ "model": "anthropic.claude-instant-v1",
14
+ "error": "Internal Server Error"
15
+ },
16
+ {
17
+ "model": "anthropic.claude-3-sonnet-20240229-v1:0",
18
+ "error": "Internal Server Error"
19
+ },
20
+ {
21
+ "model": "anthropic.claude-3-sonnet-20240229-v1",
22
+ "error": "Internal Server Error"
23
+ },
24
+ {
25
+ "model": "anthropic.claude-3-haiku-20240307-v1:0",
26
+ "error": "Internal Server Error"
27
+ },
28
+ {
29
+ "model": "anthropic.claude-3-haiku-20240307-v1",
30
+ "error": "Internal Server Error"
31
+ },
32
+ {
33
+ "model": "anthropic.claude-3-opus-20240229-v1:0",
34
+ "error": "Internal Server Error"
35
+ },
36
+ {
37
+ "model": "anthropic.claude-3-opus-20240229-v1",
38
+ "error": "Internal Server Error"
39
+ },
40
+ {
41
+ "model": "amazon.q",
42
+ "error": "Internal Server Error"
43
+ }
44
+ ],
45
+ "extracted_from_errors": [],
46
+ "timestamp": "1763807315.45737"
47
+ }
openapi_spec.json ADDED
@@ -0,0 +1,699 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "openapi": "3.1.0",
3
+ "info": {
4
+ "title": "v2 OpenAI-compatible Server (Amazon Q Backend)",
5
+ "version": "0.1.0"
6
+ },
7
+ "paths": {
8
+ "/v1/chat/completions": {
9
+ "post": {
10
+ "summary": "Chat Completions",
11
+ "description": "OpenAI-compatible chat endpoint.\n - stream default False\n - messages will be converted into \"{role}:\n{content}\" and injected into template\n - account is chosen randomly among enabled accounts (API key is for authorization only)",
12
+ "operationId": "chat_completions_v1_chat_completions_post",
13
+ "parameters": [
14
+ {
15
+ "name": "authorization",
16
+ "in": "header",
17
+ "required": false,
18
+ "schema": {
19
+ "anyOf": [
20
+ {
21
+ "type": "string"
22
+ },
23
+ {
24
+ "type": "null"
25
+ }
26
+ ],
27
+ "title": "Authorization"
28
+ }
29
+ }
30
+ ],
31
+ "requestBody": {
32
+ "required": true,
33
+ "content": {
34
+ "application/json": {
35
+ "schema": {
36
+ "$ref": "#/components/schemas/ChatCompletionRequest"
37
+ }
38
+ }
39
+ }
40
+ },
41
+ "responses": {
42
+ "200": {
43
+ "description": "Successful Response",
44
+ "content": {
45
+ "application/json": {
46
+ "schema": {}
47
+ }
48
+ }
49
+ },
50
+ "422": {
51
+ "description": "Validation Error",
52
+ "content": {
53
+ "application/json": {
54
+ "schema": {
55
+ "$ref": "#/components/schemas/HTTPValidationError"
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
62
+ },
63
+ "/v2/auth/start": {
64
+ "post": {
65
+ "summary": "Auth Start",
66
+ "description": "Start device authorization and return verification URL for user login.\nSession lifetime capped at 5 minutes on claim.",
67
+ "operationId": "auth_start_v2_auth_start_post",
68
+ "requestBody": {
69
+ "content": {
70
+ "application/json": {
71
+ "schema": {
72
+ "$ref": "#/components/schemas/AuthStartBody"
73
+ }
74
+ }
75
+ },
76
+ "required": true
77
+ },
78
+ "responses": {
79
+ "200": {
80
+ "description": "Successful Response",
81
+ "content": {
82
+ "application/json": {
83
+ "schema": {}
84
+ }
85
+ }
86
+ },
87
+ "422": {
88
+ "description": "Validation Error",
89
+ "content": {
90
+ "application/json": {
91
+ "schema": {
92
+ "$ref": "#/components/schemas/HTTPValidationError"
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ },
100
+ "/v2/auth/status/{auth_id}": {
101
+ "get": {
102
+ "summary": "Auth Status",
103
+ "operationId": "auth_status_v2_auth_status__auth_id__get",
104
+ "parameters": [
105
+ {
106
+ "name": "auth_id",
107
+ "in": "path",
108
+ "required": true,
109
+ "schema": {
110
+ "type": "string",
111
+ "title": "Auth Id"
112
+ }
113
+ }
114
+ ],
115
+ "responses": {
116
+ "200": {
117
+ "description": "Successful Response",
118
+ "content": {
119
+ "application/json": {
120
+ "schema": {}
121
+ }
122
+ }
123
+ },
124
+ "422": {
125
+ "description": "Validation Error",
126
+ "content": {
127
+ "application/json": {
128
+ "schema": {
129
+ "$ref": "#/components/schemas/HTTPValidationError"
130
+ }
131
+ }
132
+ }
133
+ }
134
+ }
135
+ }
136
+ },
137
+ "/v2/auth/claim/{auth_id}": {
138
+ "post": {
139
+ "summary": "Auth Claim",
140
+ "description": "Block up to 5 minutes to exchange the device code for tokens after user completed login.\nOn success, creates an enabled account and returns it.",
141
+ "operationId": "auth_claim_v2_auth_claim__auth_id__post",
142
+ "parameters": [
143
+ {
144
+ "name": "auth_id",
145
+ "in": "path",
146
+ "required": true,
147
+ "schema": {
148
+ "type": "string",
149
+ "title": "Auth Id"
150
+ }
151
+ }
152
+ ],
153
+ "responses": {
154
+ "200": {
155
+ "description": "Successful Response",
156
+ "content": {
157
+ "application/json": {
158
+ "schema": {}
159
+ }
160
+ }
161
+ },
162
+ "422": {
163
+ "description": "Validation Error",
164
+ "content": {
165
+ "application/json": {
166
+ "schema": {
167
+ "$ref": "#/components/schemas/HTTPValidationError"
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+ }
174
+ },
175
+ "/v2/accounts": {
176
+ "get": {
177
+ "summary": "List Accounts",
178
+ "operationId": "list_accounts_v2_accounts_get",
179
+ "responses": {
180
+ "200": {
181
+ "description": "Successful Response",
182
+ "content": {
183
+ "application/json": {
184
+ "schema": {}
185
+ }
186
+ }
187
+ }
188
+ }
189
+ },
190
+ "post": {
191
+ "summary": "Create Account",
192
+ "operationId": "create_account_v2_accounts_post",
193
+ "requestBody": {
194
+ "content": {
195
+ "application/json": {
196
+ "schema": {
197
+ "$ref": "#/components/schemas/AccountCreate"
198
+ }
199
+ }
200
+ },
201
+ "required": true
202
+ },
203
+ "responses": {
204
+ "200": {
205
+ "description": "Successful Response",
206
+ "content": {
207
+ "application/json": {
208
+ "schema": {}
209
+ }
210
+ }
211
+ },
212
+ "422": {
213
+ "description": "Validation Error",
214
+ "content": {
215
+ "application/json": {
216
+ "schema": {
217
+ "$ref": "#/components/schemas/HTTPValidationError"
218
+ }
219
+ }
220
+ }
221
+ }
222
+ }
223
+ }
224
+ },
225
+ "/v2/accounts/{account_id}": {
226
+ "get": {
227
+ "summary": "Get Account Detail",
228
+ "operationId": "get_account_detail_v2_accounts__account_id__get",
229
+ "parameters": [
230
+ {
231
+ "name": "account_id",
232
+ "in": "path",
233
+ "required": true,
234
+ "schema": {
235
+ "type": "string",
236
+ "title": "Account Id"
237
+ }
238
+ }
239
+ ],
240
+ "responses": {
241
+ "200": {
242
+ "description": "Successful Response",
243
+ "content": {
244
+ "application/json": {
245
+ "schema": {}
246
+ }
247
+ }
248
+ },
249
+ "422": {
250
+ "description": "Validation Error",
251
+ "content": {
252
+ "application/json": {
253
+ "schema": {
254
+ "$ref": "#/components/schemas/HTTPValidationError"
255
+ }
256
+ }
257
+ }
258
+ }
259
+ }
260
+ },
261
+ "delete": {
262
+ "summary": "Delete Account",
263
+ "operationId": "delete_account_v2_accounts__account_id__delete",
264
+ "parameters": [
265
+ {
266
+ "name": "account_id",
267
+ "in": "path",
268
+ "required": true,
269
+ "schema": {
270
+ "type": "string",
271
+ "title": "Account Id"
272
+ }
273
+ }
274
+ ],
275
+ "responses": {
276
+ "200": {
277
+ "description": "Successful Response",
278
+ "content": {
279
+ "application/json": {
280
+ "schema": {}
281
+ }
282
+ }
283
+ },
284
+ "422": {
285
+ "description": "Validation Error",
286
+ "content": {
287
+ "application/json": {
288
+ "schema": {
289
+ "$ref": "#/components/schemas/HTTPValidationError"
290
+ }
291
+ }
292
+ }
293
+ }
294
+ }
295
+ },
296
+ "patch": {
297
+ "summary": "Update Account",
298
+ "operationId": "update_account_v2_accounts__account_id__patch",
299
+ "parameters": [
300
+ {
301
+ "name": "account_id",
302
+ "in": "path",
303
+ "required": true,
304
+ "schema": {
305
+ "type": "string",
306
+ "title": "Account Id"
307
+ }
308
+ }
309
+ ],
310
+ "requestBody": {
311
+ "required": true,
312
+ "content": {
313
+ "application/json": {
314
+ "schema": {
315
+ "$ref": "#/components/schemas/AccountUpdate"
316
+ }
317
+ }
318
+ }
319
+ },
320
+ "responses": {
321
+ "200": {
322
+ "description": "Successful Response",
323
+ "content": {
324
+ "application/json": {
325
+ "schema": {}
326
+ }
327
+ }
328
+ },
329
+ "422": {
330
+ "description": "Validation Error",
331
+ "content": {
332
+ "application/json": {
333
+ "schema": {
334
+ "$ref": "#/components/schemas/HTTPValidationError"
335
+ }
336
+ }
337
+ }
338
+ }
339
+ }
340
+ }
341
+ },
342
+ "/v2/accounts/{account_id}/refresh": {
343
+ "post": {
344
+ "summary": "Manual Refresh",
345
+ "operationId": "manual_refresh_v2_accounts__account_id__refresh_post",
346
+ "parameters": [
347
+ {
348
+ "name": "account_id",
349
+ "in": "path",
350
+ "required": true,
351
+ "schema": {
352
+ "type": "string",
353
+ "title": "Account Id"
354
+ }
355
+ }
356
+ ],
357
+ "responses": {
358
+ "200": {
359
+ "description": "Successful Response",
360
+ "content": {
361
+ "application/json": {
362
+ "schema": {}
363
+ }
364
+ }
365
+ },
366
+ "422": {
367
+ "description": "Validation Error",
368
+ "content": {
369
+ "application/json": {
370
+ "schema": {
371
+ "$ref": "#/components/schemas/HTTPValidationError"
372
+ }
373
+ }
374
+ }
375
+ }
376
+ }
377
+ }
378
+ },
379
+ "/": {
380
+ "get": {
381
+ "summary": "Index",
382
+ "operationId": "index__get",
383
+ "responses": {
384
+ "200": {
385
+ "description": "Successful Response"
386
+ }
387
+ }
388
+ }
389
+ },
390
+ "/healthz": {
391
+ "get": {
392
+ "summary": "Health",
393
+ "operationId": "health_healthz_get",
394
+ "responses": {
395
+ "200": {
396
+ "description": "Successful Response",
397
+ "content": {
398
+ "application/json": {
399
+ "schema": {}
400
+ }
401
+ }
402
+ }
403
+ }
404
+ }
405
+ }
406
+ },
407
+ "components": {
408
+ "schemas": {
409
+ "AccountCreate": {
410
+ "properties": {
411
+ "label": {
412
+ "anyOf": [
413
+ {
414
+ "type": "string"
415
+ },
416
+ {
417
+ "type": "null"
418
+ }
419
+ ],
420
+ "title": "Label"
421
+ },
422
+ "clientId": {
423
+ "type": "string",
424
+ "title": "Clientid"
425
+ },
426
+ "clientSecret": {
427
+ "type": "string",
428
+ "title": "Clientsecret"
429
+ },
430
+ "refreshToken": {
431
+ "anyOf": [
432
+ {
433
+ "type": "string"
434
+ },
435
+ {
436
+ "type": "null"
437
+ }
438
+ ],
439
+ "title": "Refreshtoken"
440
+ },
441
+ "accessToken": {
442
+ "anyOf": [
443
+ {
444
+ "type": "string"
445
+ },
446
+ {
447
+ "type": "null"
448
+ }
449
+ ],
450
+ "title": "Accesstoken"
451
+ },
452
+ "other": {
453
+ "anyOf": [
454
+ {
455
+ "type": "object"
456
+ },
457
+ {
458
+ "type": "null"
459
+ }
460
+ ],
461
+ "title": "Other"
462
+ },
463
+ "enabled": {
464
+ "anyOf": [
465
+ {
466
+ "type": "boolean"
467
+ },
468
+ {
469
+ "type": "null"
470
+ }
471
+ ],
472
+ "title": "Enabled",
473
+ "default": true
474
+ }
475
+ },
476
+ "type": "object",
477
+ "required": [
478
+ "clientId",
479
+ "clientSecret"
480
+ ],
481
+ "title": "AccountCreate"
482
+ },
483
+ "AccountUpdate": {
484
+ "properties": {
485
+ "label": {
486
+ "anyOf": [
487
+ {
488
+ "type": "string"
489
+ },
490
+ {
491
+ "type": "null"
492
+ }
493
+ ],
494
+ "title": "Label"
495
+ },
496
+ "clientId": {
497
+ "anyOf": [
498
+ {
499
+ "type": "string"
500
+ },
501
+ {
502
+ "type": "null"
503
+ }
504
+ ],
505
+ "title": "Clientid"
506
+ },
507
+ "clientSecret": {
508
+ "anyOf": [
509
+ {
510
+ "type": "string"
511
+ },
512
+ {
513
+ "type": "null"
514
+ }
515
+ ],
516
+ "title": "Clientsecret"
517
+ },
518
+ "refreshToken": {
519
+ "anyOf": [
520
+ {
521
+ "type": "string"
522
+ },
523
+ {
524
+ "type": "null"
525
+ }
526
+ ],
527
+ "title": "Refreshtoken"
528
+ },
529
+ "accessToken": {
530
+ "anyOf": [
531
+ {
532
+ "type": "string"
533
+ },
534
+ {
535
+ "type": "null"
536
+ }
537
+ ],
538
+ "title": "Accesstoken"
539
+ },
540
+ "other": {
541
+ "anyOf": [
542
+ {
543
+ "type": "object"
544
+ },
545
+ {
546
+ "type": "null"
547
+ }
548
+ ],
549
+ "title": "Other"
550
+ },
551
+ "enabled": {
552
+ "anyOf": [
553
+ {
554
+ "type": "boolean"
555
+ },
556
+ {
557
+ "type": "null"
558
+ }
559
+ ],
560
+ "title": "Enabled"
561
+ }
562
+ },
563
+ "type": "object",
564
+ "title": "AccountUpdate"
565
+ },
566
+ "AuthStartBody": {
567
+ "properties": {
568
+ "label": {
569
+ "anyOf": [
570
+ {
571
+ "type": "string"
572
+ },
573
+ {
574
+ "type": "null"
575
+ }
576
+ ],
577
+ "title": "Label"
578
+ },
579
+ "enabled": {
580
+ "anyOf": [
581
+ {
582
+ "type": "boolean"
583
+ },
584
+ {
585
+ "type": "null"
586
+ }
587
+ ],
588
+ "title": "Enabled",
589
+ "default": true
590
+ }
591
+ },
592
+ "type": "object",
593
+ "title": "AuthStartBody"
594
+ },
595
+ "ChatCompletionRequest": {
596
+ "properties": {
597
+ "model": {
598
+ "anyOf": [
599
+ {
600
+ "type": "string"
601
+ },
602
+ {
603
+ "type": "null"
604
+ }
605
+ ],
606
+ "title": "Model"
607
+ },
608
+ "messages": {
609
+ "items": {
610
+ "$ref": "#/components/schemas/ChatMessage"
611
+ },
612
+ "type": "array",
613
+ "title": "Messages"
614
+ },
615
+ "stream": {
616
+ "anyOf": [
617
+ {
618
+ "type": "boolean"
619
+ },
620
+ {
621
+ "type": "null"
622
+ }
623
+ ],
624
+ "title": "Stream",
625
+ "default": false
626
+ }
627
+ },
628
+ "type": "object",
629
+ "required": [
630
+ "messages"
631
+ ],
632
+ "title": "ChatCompletionRequest"
633
+ },
634
+ "ChatMessage": {
635
+ "properties": {
636
+ "role": {
637
+ "type": "string",
638
+ "title": "Role"
639
+ },
640
+ "content": {
641
+ "title": "Content"
642
+ }
643
+ },
644
+ "type": "object",
645
+ "required": [
646
+ "role",
647
+ "content"
648
+ ],
649
+ "title": "ChatMessage"
650
+ },
651
+ "HTTPValidationError": {
652
+ "properties": {
653
+ "detail": {
654
+ "items": {
655
+ "$ref": "#/components/schemas/ValidationError"
656
+ },
657
+ "type": "array",
658
+ "title": "Detail"
659
+ }
660
+ },
661
+ "type": "object",
662
+ "title": "HTTPValidationError"
663
+ },
664
+ "ValidationError": {
665
+ "properties": {
666
+ "loc": {
667
+ "items": {
668
+ "anyOf": [
669
+ {
670
+ "type": "string"
671
+ },
672
+ {
673
+ "type": "integer"
674
+ }
675
+ ]
676
+ },
677
+ "type": "array",
678
+ "title": "Location"
679
+ },
680
+ "msg": {
681
+ "type": "string",
682
+ "title": "Message"
683
+ },
684
+ "type": {
685
+ "type": "string",
686
+ "title": "Error Type"
687
+ }
688
+ },
689
+ "type": "object",
690
+ "required": [
691
+ "loc",
692
+ "msg",
693
+ "type"
694
+ ],
695
+ "title": "ValidationError"
696
+ }
697
+ }
698
+ }
699
+ }
query_amazon_models.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ 直接查询 Amazon Q 服务来获取支持的模型
4
+ """
5
+
6
+ import requests
7
+ import json
8
+ import time
9
+ from replicate import send_chat_request
10
+
11
+ def get_available_models_from_amazon_q():
12
+ """尝试通过特殊查询获取 Amazon Q 支持的模型"""
13
+
14
+ # 尝试不同的查询方式来获取模型信息
15
+ test_queries = [
16
+ "你支持哪些模型?",
17
+ "有哪些可用的模型?",
18
+ "列出所有支持的模型名称",
19
+ "What models are available?",
20
+ "List all supported models",
21
+ "Show model options",
22
+ "model list",
23
+ "models",
24
+ "help"
25
+ ]
26
+
27
+ print("🔍 通过查询尝试获取 Amazon Q 支持的模型信息")
28
+ print("=" * 60)
29
+
30
+ # 这里需要一个有效的 access token,我们先尝试从现有账号获取
31
+ try:
32
+ # 检查是否有可用账号
33
+ accounts_response = requests.get("http://localhost:8000/v2/accounts", timeout=10)
34
+ if accounts_response.status_code != 200:
35
+ print("❌ 无法获取账号信息")
36
+ return
37
+
38
+ accounts = accounts_response.json()
39
+ enabled_accounts = [acc for acc in accounts if acc.get("enabled", False)]
40
+
41
+ if not enabled_accounts:
42
+ print("❌ 没有可用的账号")
43
+ return
44
+
45
+ account = enabled_accounts[0]
46
+ access_token = account.get("accessToken")
47
+
48
+ if not access_token:
49
+ # 尝试刷新 token
50
+ print("🔄 刷新 access token...")
51
+ refresh_response = requests.post(f"http://localhost:8000/v2/accounts/{account['id']}/refresh", timeout=10)
52
+ if refresh_response.status_code == 200:
53
+ updated_account = refresh_response.json()
54
+ access_token = updated_account.get("accessToken")
55
+
56
+ if not access_token:
57
+ print("❌ 无法获取 access token")
58
+ return
59
+
60
+ print(f"✅ 获取到 access token,开始查询...")
61
+
62
+ # 对每个查询进行测试
63
+ for i, query in enumerate(test_queries, 1):
64
+ print(f"\n[{i}/{len(test_queries)}] 查询: {query}")
65
+ print("-" * 40)
66
+
67
+ try:
68
+ text, _, tracker = send_chat_request(
69
+ access_token,
70
+ [{"role": "user", "content": query}],
71
+ model=None, # 不指定模型,使用默认
72
+ stream=False,
73
+ timeout=(10, 30)
74
+ )
75
+
76
+ if text:
77
+ print(f"响应: {text[:200]}...")
78
+
79
+ # 检查响应中是否包含模型信息
80
+ model_keywords = ["model", "claude", "anthropic", "sonnet", "haiku", "opus"]
81
+ found_models = []
82
+
83
+ for keyword in model_keywords:
84
+ if keyword.lower() in text.lower():
85
+ found_models.append(keyword)
86
+
87
+ if found_models:
88
+ print(f"🔍 发现模型相关关键词: {', '.join(found_models)}")
89
+ else:
90
+ print("❌ 无响应")
91
+
92
+ except Exception as e:
93
+ print(f"❌ 查询失败: {e}")
94
+
95
+ time.sleep(1) # 避免请求过快
96
+
97
+ except Exception as e:
98
+ print(f"❌ 获取模型信息失败: {e}")
99
+
100
+ def test_model_variations():
101
+ """测试模型名称的变体"""
102
+ # 基于已知可用的 claude-sonnet-4,测试可能的变体
103
+ variations = [
104
+ "claude-sonnet-4",
105
+ "Claude-Sonnet-4",
106
+ "CLAUDE-SONNET-4",
107
+ "sonnet-4",
108
+ "claude-4-sonnet",
109
+ "claude-4",
110
+ "sonnet4",
111
+ "anthropic-sonnet-4",
112
+ "anthropic-claude-sonnet-4",
113
+ ]
114
+
115
+ print("\n🧪 测试模型名称变体")
116
+ print("=" * 40)
117
+
118
+ for model in variations:
119
+ try:
120
+ text, _, tracker = send_chat_request(
121
+ "dummy_token", # 这里会被替换为真实 token
122
+ [{"role": "user", "content": "test"}],
123
+ model=model,
124
+ stream=False,
125
+ timeout=(5, 15)
126
+ )
127
+
128
+ if text:
129
+ print(f"✅ {model} - 可用")
130
+ else:
131
+ print(f"❌ {model} - 不可用")
132
+
133
+ except Exception as e:
134
+ print(f"❌ {model} - 错误: {str(e)[:50]}")
135
+
136
+ time.sleep(0.5)
137
+
138
+ if __name__ == "__main__":
139
+ get_available_models_from_amazon_q()
140
+ # test_model_variations() # 暂时不运行,因为需要有效token
quick_test.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ 简单的 Amazon Q API 测试脚本
4
+ """
5
+
6
+ import requests
7
+ import json
8
+
9
+ def test_api():
10
+ """测试 API 基本功能"""
11
+ url = "http://localhost:8000/v1/chat/completions"
12
+
13
+ payload = {
14
+ "model": "claude-sonnet-4",
15
+ "messages": [
16
+ {"role": "user", "content": "你好,请简单介绍一下你自己"}
17
+ ],
18
+ "stream": False
19
+ }
20
+
21
+ try:
22
+ response = requests.post(url, json=payload, timeout=30)
23
+
24
+ if response.status_code == 200:
25
+ result = response.json()
26
+ content = result.get("choices", [{}])[0].get("message", {}).get("content", "")
27
+ print("✅ API 测试成功!")
28
+ print(f"响应内容: {content}")
29
+ return True
30
+ else:
31
+ print(f"❌ API 测试失败: {response.status_code}")
32
+ print(f"错误信息: {response.text}")
33
+ return False
34
+
35
+ except Exception as e:
36
+ print(f"❌ 请求异常: {e}")
37
+ return False
38
+
39
+ def test_streaming():
40
+ """测试流式响应"""
41
+ url = "http://localhost:8000/v1/chat/completions"
42
+
43
+ payload = {
44
+ "model": "claude-sonnet-4",
45
+ "messages": [
46
+ {"role": "user", "content": "请用一句话介绍人工智能"}
47
+ ],
48
+ "stream": True
49
+ }
50
+
51
+ try:
52
+ response = requests.post(url, json=payload, stream=True, timeout=30)
53
+
54
+ if response.status_code == 200:
55
+ print("✅ 流式测试成功!")
56
+ print("流式响应:")
57
+ for line in response.iter_lines():
58
+ if line:
59
+ line_str = line.decode('utf-8')
60
+ if line_str.startswith('data: ') and line_str != 'data: [DONE]':
61
+ data = json.loads(line_str[6:])
62
+ content = data.get("choices", [{}])[0].get("delta", {}).get("content", "")
63
+ if content:
64
+ print(content, end='', flush=True)
65
+ print() # 换行
66
+ return True
67
+ else:
68
+ print(f"❌ 流式测试失败: {response.status_code}")
69
+ return False
70
+
71
+ except Exception as e:
72
+ print(f"❌ 流式请求异常: {e}")
73
+ return False
74
+
75
+ if __name__ == "__main__":
76
+ print("🧪 Amazon Q API 快速测试")
77
+ print("=" * 40)
78
+
79
+ # 测试基本功能
80
+ print("1. 测试基本对话功能...")
81
+ test_api()
82
+
83
+ print("\n2. 测试流式响应...")
84
+ test_streaming()
85
+
86
+ print("\n✨ 测试完成!")
test_models.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ 测试 Amazon Q API 可用模型的脚本
4
+ """
5
+
6
+ import requests
7
+ import json
8
+ import time
9
+ from typing import List, Dict, Optional
10
+
11
+ # API 配置
12
+ BASE_URL = "http://localhost:8000"
13
+ API_KEY = "" # 根据你的 .env 配置设置,如果为空则使用开发模式
14
+
15
+ # 常见的 Claude 模型列表(基于 Amazon Q 可能支持的模型)
16
+ TEST_MODELS = [
17
+ "claude-sonnet-4",
18
+ "claude-sonnet-3.5",
19
+ "claude-haiku-3.5",
20
+ "claude-opus-3",
21
+ "claude-sonnet-3",
22
+ "claude-haiku-3",
23
+ "claude-instant-1.2",
24
+ "anthropic.claude-3-sonnet-20240229-v1:0",
25
+ "anthropic.claude-3-haiku-20240307-v1:0",
26
+ "anthropic.claude-3-opus-20240229-v1:0",
27
+ ]
28
+
29
+ def test_model(model_name: str, api_key: str = "") -> Dict[str, any]:
30
+ """测试单个模型是否可用"""
31
+ url = f"{BASE_URL}/v1/chat/completions"
32
+
33
+ headers = {
34
+ "Content-Type": "application/json",
35
+ }
36
+
37
+ if api_key:
38
+ headers["Authorization"] = f"Bearer {api_key}"
39
+
40
+ payload = {
41
+ "model": model_name,
42
+ "messages": [
43
+ {"role": "user", "content": "请简单回复:你好"}
44
+ ],
45
+ "stream": False,
46
+ "max_tokens": 10
47
+ }
48
+
49
+ try:
50
+ response = requests.post(url, headers=headers, json=payload, timeout=30)
51
+
52
+ result = {
53
+ "model": model_name,
54
+ "status_code": response.status_code,
55
+ "available": response.status_code == 200,
56
+ "error": None,
57
+ "response_preview": None
58
+ }
59
+
60
+ if response.status_code == 200:
61
+ try:
62
+ data = response.json()
63
+ content = data.get("choices", [{}])[0].get("message", {}).get("content", "")
64
+ result["response_preview"] = content[:100] # 只显示前100个字符
65
+ except:
66
+ result["response_preview"] = "解析响应失败"
67
+ else:
68
+ result["error"] = response.text[:200] # 只显示前200个字符的错误信息
69
+
70
+ except requests.exceptions.Timeout:
71
+ result = {
72
+ "model": model_name,
73
+ "status_code": None,
74
+ "available": False,
75
+ "error": "请求超时",
76
+ "response_preview": None
77
+ }
78
+ except Exception as e:
79
+ result = {
80
+ "model": model_name,
81
+ "status_code": None,
82
+ "available": False,
83
+ "error": str(e)[:200],
84
+ "response_preview": None
85
+ }
86
+
87
+ return result
88
+
89
+ def check_api_health() -> bool:
90
+ """检查 API 服务是否健康"""
91
+ try:
92
+ response = requests.get(f"{BASE_URL}/healthz", timeout=10)
93
+ return response.status_code == 200
94
+ except:
95
+ return False
96
+
97
+ def check_accounts() -> bool:
98
+ """检查是否有可用账号"""
99
+ try:
100
+ headers = {"Authorization": f"Bearer {API_KEY}"} if API_KEY else {}
101
+ response = requests.get(f"{BASE_URL}/v2/accounts", headers=headers, timeout=10)
102
+ if response.status_code == 200:
103
+ accounts = response.json()
104
+ enabled_accounts = [acc for acc in accounts if acc.get("enabled", False)]
105
+ return len(enabled_accounts) > 0
106
+ return False
107
+ except:
108
+ return False
109
+
110
+ def main():
111
+ """主函数"""
112
+ print("🔍 Amazon Q API 模型测试脚本")
113
+ print("=" * 50)
114
+
115
+ # 检查 API 服务状态
116
+ print("📡 检查 API 服务状态...")
117
+ if not check_api_health():
118
+ print("❌ API 服务不可用,请确保服务正在运行在 http://localhost:8000")
119
+ return
120
+
121
+ print("✅ API 服务正常")
122
+
123
+ # 检查账号状态
124
+ print("👤 检查可用账号...")
125
+ if not check_accounts():
126
+ print("❌ 没有可用的账号,请先添加并启用 Amazon Q 账号")
127
+ return
128
+
129
+ print("✅ 发现可用账号")
130
+
131
+ # 测试模型
132
+ print(f"\n🧪 开始测试 {len(TEST_MODELS)} 个模型...")
133
+ print("-" * 80)
134
+
135
+ available_models = []
136
+ failed_models = []
137
+
138
+ for i, model in enumerate(TEST_MODELS, 1):
139
+ print(f"[{i}/{len(TEST_MODELS)}] 测试: {model}...", end=" ")
140
+
141
+ result = test_model(model, API_KEY)
142
+
143
+ if result["available"]:
144
+ print("✅ 可用")
145
+ available_models.append(model)
146
+ if result["response_preview"]:
147
+ print(f" 响应预览: {result['response_preview']}")
148
+ else:
149
+ print("❌ 不可用")
150
+ failed_models.append((model, result["error"]))
151
+
152
+ # 避免请求过于频繁
153
+ time.sleep(0.5)
154
+
155
+ # 输出结果总结
156
+ print("\n" + "=" * 80)
157
+ print("📊 测试结果总结")
158
+ print("=" * 80)
159
+
160
+ if available_models:
161
+ print(f"\n✅ 可用模型 ({len(available_models)} 个):")
162
+ for model in available_models:
163
+ print(f" • {model}")
164
+ else:
165
+ print("\n❌ 没有发现可用的模型")
166
+
167
+ if failed_models:
168
+ print(f"\n❌ 不可用模型 ({len(failed_models)} 个):")
169
+ for model, error in failed_models[:5]: # 只显示前5个错误
170
+ print(f" • {model}: {error}")
171
+ if len(failed_models) > 5:
172
+ print(f" ... 还有 {len(failed_models) - 5} 个模型不可用")
173
+
174
+ # 生成配置建议
175
+ if available_models:
176
+ print(f"\n💡 建议配置:")
177
+ print(f"推荐使用: {available_models[0]}")
178
+ print(f"在代码中使用: model = \"{available_models[0]}\"")
179
+
180
+ # 保存可用模型到文件
181
+ with open("available_models.txt", "w", encoding="utf-8") as f:
182
+ f.write("可用模型列表:\n")
183
+ for model in available_models:
184
+ f.write(f"{model}\n")
185
+ print(f"📁 可用模型已保存到: available_models.txt")
186
+
187
+ if __name__ == "__main__":
188
+ main()