kamau1 commited on
Commit
be6b137
Β·
1 Parent(s): 86c9796

fix(api): resolve SlowAPI request param conflict, update schema_extra naming, enhance tests, and serve Swagger UI at root

Browse files
README.md CHANGED
@@ -72,10 +72,10 @@ app/
72
  ## πŸ”— API Endpoints
73
 
74
  ### Health & Monitoring
75
- - **`GET /`** - Basic health check
 
76
  - **`GET /health`** - Detailed health monitoring
77
  - **`GET /metrics`** - Prometheus metrics
78
- - **`GET /docs`** - Swagger UI documentation
79
  - **`GET /redoc`** - ReDoc documentation
80
 
81
  ### Translation
 
72
  ## πŸ”— API Endpoints
73
 
74
  ### Health & Monitoring
75
+ - **`GET /`** - Interactive Swagger UI documentation
76
+ - **`GET /status`** - Basic health check
77
  - **`GET /health`** - Detailed health monitoring
78
  - **`GET /metrics`** - Prometheus metrics
 
79
  - **`GET /redoc`** - ReDoc documentation
80
 
81
  ### Translation
app/api/v1/endpoints.py CHANGED
@@ -48,13 +48,13 @@ router = APIRouter()
48
 
49
 
50
  @router.get(
51
- "/",
52
  response_model=HealthResponse,
53
  tags=["Health & Monitoring"],
54
  summary="Basic Health Check",
55
  description="Quick health check endpoint that returns basic API status information."
56
  )
57
- async def root():
58
  """
59
  ## Basic Health Check
60
 
@@ -182,8 +182,8 @@ async def get_metrics():
182
  )
183
  @limiter.limit(f"{settings.max_requests_per_minute}/minute")
184
  async def translate_endpoint(
185
- request: TranslationRequest,
186
- http_request: Request
187
  ):
188
  """
189
  ## 🌍 Translate Text
@@ -239,47 +239,47 @@ async def translate_endpoint(
239
  - Unique request ID for debugging
240
  - Timestamp in Nairobi timezone
241
  """
242
- request_id = http_request.state.request_id
243
 
244
  # Validate text length
245
- if len(request.text) > settings.max_text_length:
246
  raise HTTPException(
247
  status_code=413,
248
  detail=f"Text too long. Maximum {settings.max_text_length} characters allowed."
249
  )
250
 
251
  full_date, _ = get_nairobi_time()
252
- character_count = len(request.text)
253
 
254
  # Log translation request
255
  logger.info(
256
  "translation_started",
257
  request_id=request_id,
258
- source_language=request.source_language,
259
- target_language=request.target_language,
260
  character_count=character_count
261
  )
262
 
263
  try:
264
- if request.source_language:
265
  # Use provided source language
266
  translated_text, inference_time = translate_with_source(
267
- request.text,
268
- request.source_language,
269
- request.target_language
270
  )
271
- source_lang = request.source_language
272
  else:
273
  # Auto-detect source language
274
  source_lang, translated_text, inference_time = translate_with_detection(
275
- request.text,
276
- request.target_language
277
  )
278
 
279
  # Update metrics
280
  TRANSLATION_COUNT.labels(
281
  source_lang=source_lang,
282
- target_lang=request.target_language
283
  ).inc()
284
 
285
  CHARACTER_COUNT.inc(character_count)
@@ -289,7 +289,7 @@ async def translate_endpoint(
289
  "translation_completed",
290
  request_id=request_id,
291
  source_language=source_lang,
292
- target_language=request.target_language,
293
  character_count=character_count,
294
  inference_time=inference_time
295
  )
@@ -297,7 +297,7 @@ async def translate_endpoint(
297
  return TranslationResponse(
298
  translated_text=translated_text,
299
  source_language=source_lang,
300
- target_language=request.target_language,
301
  inference_time=inference_time,
302
  character_count=character_count,
303
  timestamp=full_date,
@@ -311,8 +311,8 @@ async def translate_endpoint(
311
  request_id=request_id,
312
  error=str(e),
313
  error_type=type(e).__name__,
314
- source_language=request.source_language,
315
- target_language=request.target_language
316
  )
317
 
318
  # Update error metrics
 
48
 
49
 
50
  @router.get(
51
+ "/status",
52
  response_model=HealthResponse,
53
  tags=["Health & Monitoring"],
54
  summary="Basic Health Check",
55
  description="Quick health check endpoint that returns basic API status information."
56
  )
57
+ async def status_check():
58
  """
59
  ## Basic Health Check
60
 
 
182
  )
183
  @limiter.limit(f"{settings.max_requests_per_minute}/minute")
184
  async def translate_endpoint(
185
+ translation_request: TranslationRequest,
186
+ request: Request
187
  ):
188
  """
189
  ## 🌍 Translate Text
 
239
  - Unique request ID for debugging
240
  - Timestamp in Nairobi timezone
241
  """
242
+ request_id = request.state.request_id
243
 
244
  # Validate text length
245
+ if len(translation_request.text) > settings.max_text_length:
246
  raise HTTPException(
247
  status_code=413,
248
  detail=f"Text too long. Maximum {settings.max_text_length} characters allowed."
249
  )
250
 
251
  full_date, _ = get_nairobi_time()
252
+ character_count = len(translation_request.text)
253
 
254
  # Log translation request
255
  logger.info(
256
  "translation_started",
257
  request_id=request_id,
258
+ source_language=translation_request.source_language,
259
+ target_language=translation_request.target_language,
260
  character_count=character_count
261
  )
262
 
263
  try:
264
+ if translation_request.source_language:
265
  # Use provided source language
266
  translated_text, inference_time = translate_with_source(
267
+ translation_request.text,
268
+ translation_request.source_language,
269
+ translation_request.target_language
270
  )
271
+ source_lang = translation_request.source_language
272
  else:
273
  # Auto-detect source language
274
  source_lang, translated_text, inference_time = translate_with_detection(
275
+ translation_request.text,
276
+ translation_request.target_language
277
  )
278
 
279
  # Update metrics
280
  TRANSLATION_COUNT.labels(
281
  source_lang=source_lang,
282
+ target_lang=translation_request.target_language
283
  ).inc()
284
 
285
  CHARACTER_COUNT.inc(character_count)
 
289
  "translation_completed",
290
  request_id=request_id,
291
  source_language=source_lang,
292
+ target_language=translation_request.target_language,
293
  character_count=character_count,
294
  inference_time=inference_time
295
  )
 
297
  return TranslationResponse(
298
  translated_text=translated_text,
299
  source_language=source_lang,
300
+ target_language=translation_request.target_language,
301
  inference_time=inference_time,
302
  character_count=character_count,
303
  timestamp=full_date,
 
311
  request_id=request_id,
312
  error=str(e),
313
  error_type=type(e).__name__,
314
+ source_language=translation_request.source_language,
315
+ target_language=translation_request.target_language
316
  )
317
 
318
  # Update error metrics
app/main.py CHANGED
@@ -68,7 +68,7 @@ curl -X POST "/translate" \\
68
  ```
69
  """,
70
  version=settings.app_version,
71
- docs_url="/docs",
72
  redoc_url="/redoc",
73
  openapi_url="/openapi.json",
74
  contact={
@@ -114,7 +114,8 @@ curl -X POST "/translate" \\
114
 
115
  # Include API routes
116
  app.include_router(v1_router, prefix="/api/v1")
117
- app.include_router(v1_router) # Also include at root for backward compatibility
 
118
 
119
  return app
120
 
@@ -137,9 +138,10 @@ async def startup_event():
137
  print("πŸŽ‰ API started successfully!")
138
  print(f"πŸ“Š Metrics enabled: {settings.enable_metrics}")
139
  print(f"πŸ”’ Environment: {settings.environment}")
140
- print(f"πŸ“ Documentation: /docs")
141
  print(f"πŸ“ˆ Metrics: /metrics")
142
  print(f"❀️ Health: /health")
 
143
  print(f"πŸ”— API v1: /api/v1/")
144
  print()
145
 
 
68
  ```
69
  """,
70
  version=settings.app_version,
71
+ docs_url="/",
72
  redoc_url="/redoc",
73
  openapi_url="/openapi.json",
74
  contact={
 
114
 
115
  # Include API routes
116
  app.include_router(v1_router, prefix="/api/v1")
117
+ # Include routes at root level (excluding root path which is now Swagger UI)
118
+ app.include_router(v1_router, prefix="", include_in_schema=False)
119
 
120
  return app
121
 
 
138
  print("πŸŽ‰ API started successfully!")
139
  print(f"πŸ“Š Metrics enabled: {settings.enable_metrics}")
140
  print(f"πŸ”’ Environment: {settings.environment}")
141
+ print(f"πŸ“ Documentation: / (Swagger UI)")
142
  print(f"πŸ“ˆ Metrics: /metrics")
143
  print(f"❀️ Health: /health")
144
+ print(f"πŸ” Status: /status")
145
  print(f"πŸ”— API v1: /api/v1/")
146
  print()
147
 
app/models/schemas.py CHANGED
@@ -37,7 +37,7 @@ class TranslationRequest(BaseModel):
37
  )
38
 
39
  class Config:
40
- schema_extra = {
41
  "examples": [
42
  {
43
  "summary": "Auto-detect source language",
@@ -128,7 +128,7 @@ class TranslationResponse(BaseModel):
128
  )
129
 
130
  class Config:
131
- schema_extra = {
132
  "example": {
133
  "translated_text": "Good morning",
134
  "source_language": "swh_Latn",
@@ -184,7 +184,7 @@ class LanguagesResponse(BaseModel):
184
  total_count: int = Field(..., description="Total number of languages")
185
 
186
  class Config:
187
- schema_extra = {
188
  "example": {
189
  "languages": {
190
  "swh_Latn": {
@@ -218,7 +218,7 @@ class LanguageStatsResponse(BaseModel):
218
  by_region: Dict[str, int] = Field(..., description="Language count by region")
219
 
220
  class Config:
221
- schema_extra = {
222
  "example": {
223
  "total_languages": 200,
224
  "regions": 6,
 
37
  )
38
 
39
  class Config:
40
+ json_schema_extra = {
41
  "examples": [
42
  {
43
  "summary": "Auto-detect source language",
 
128
  )
129
 
130
  class Config:
131
+ json_schema_extra = {
132
  "example": {
133
  "translated_text": "Good morning",
134
  "source_language": "swh_Latn",
 
184
  total_count: int = Field(..., description="Total number of languages")
185
 
186
  class Config:
187
+ json_schema_extra = {
188
  "example": {
189
  "languages": {
190
  "swh_Latn": {
 
218
  by_region: Dict[str, int] = Field(..., description="Language count by region")
219
 
220
  class Config:
221
+ json_schema_extra = {
222
  "example": {
223
  "total_languages": 200,
224
  "regions": 6,
curl_commands.md ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Sema Translation API - Curl Commands
2
+
3
+ ## 🌐 Base URL
4
+ ```bash
5
+ # Production
6
+ API_URL="https://sematech-sema-api.hf.space"
7
+
8
+ # Local development
9
+ # API_URL="http://localhost:8000"
10
+ ```
11
+
12
+ ## πŸ₯ Health Check Endpoints
13
+
14
+ ### Basic Health Check
15
+ ```bash
16
+ curl -X GET "$API_URL/status"
17
+ ```
18
+
19
+ ### Detailed Health Check
20
+ ```bash
21
+ curl -X GET "$API_URL/health"
22
+ ```
23
+
24
+ ### Metrics (if enabled)
25
+ ```bash
26
+ curl -X GET "$API_URL/metrics"
27
+ ```
28
+
29
+ ## 🌍 Translation Endpoints
30
+
31
+ ### Basic Translation (Auto-detect source language)
32
+ ```bash
33
+ curl -X POST "$API_URL/translate" \
34
+ -H "Content-Type: application/json" \
35
+ -d '{
36
+ "text": "Habari ya asubuhi",
37
+ "target_language": "eng_Latn"
38
+ }'
39
+ ```
40
+
41
+ ### Translation with Specified Source Language
42
+ ```bash
43
+ curl -X POST "$API_URL/translate" \
44
+ -H "Content-Type: application/json" \
45
+ -d '{
46
+ "text": "Good morning",
47
+ "source_language": "eng_Latn",
48
+ "target_language": "swh_Latn"
49
+ }'
50
+ ```
51
+
52
+ ### Translation with Versioned Endpoint
53
+ ```bash
54
+ curl -X POST "$API_URL/api/v1/translate" \
55
+ -H "Content-Type: application/json" \
56
+ -d '{
57
+ "text": "WΔ© mwega?",
58
+ "source_language": "kik_Latn",
59
+ "target_language": "eng_Latn"
60
+ }'
61
+ ```
62
+
63
+ ## πŸ—£οΈ Language Information Endpoints
64
+
65
+ ### Get All Supported Languages
66
+ ```bash
67
+ curl -X GET "$API_URL/languages"
68
+ ```
69
+
70
+ ### Get Popular Languages
71
+ ```bash
72
+ curl -X GET "$API_URL/languages/popular"
73
+ ```
74
+
75
+ ### Get African Languages
76
+ ```bash
77
+ curl -X GET "$API_URL/languages/african"
78
+ ```
79
+
80
+ ### Get Languages by Region
81
+ ```bash
82
+ # European languages
83
+ curl -X GET "$API_URL/languages/region/Europe"
84
+
85
+ # Asian languages
86
+ curl -X GET "$API_URL/languages/region/Asia"
87
+
88
+ # African languages
89
+ curl -X GET "$API_URL/languages/region/Africa"
90
+ ```
91
+
92
+ ### Search Languages
93
+ ```bash
94
+ # Search for Swahili
95
+ curl -X GET "$API_URL/languages/search?q=Swahili"
96
+
97
+ # Search for Chinese
98
+ curl -X GET "$API_URL/languages/search?q=Chinese"
99
+
100
+ # Search by language code
101
+ curl -X GET "$API_URL/languages/search?q=eng"
102
+ ```
103
+
104
+ ### Get Language Statistics
105
+ ```bash
106
+ curl -X GET "$API_URL/languages/stats"
107
+ ```
108
+
109
+ ### Get Specific Language Information
110
+ ```bash
111
+ # Swahili
112
+ curl -X GET "$API_URL/languages/swh_Latn"
113
+
114
+ # English
115
+ curl -X GET "$API_URL/languages/eng_Latn"
116
+
117
+ # Chinese (Simplified)
118
+ curl -X GET "$API_URL/languages/cmn_Hans"
119
+ ```
120
+
121
+ ## πŸ“š Documentation Endpoints
122
+
123
+ ### Swagger UI (Open in browser)
124
+ ```bash
125
+ open "$API_URL/"
126
+ # or
127
+ curl -X GET "$API_URL/"
128
+ ```
129
+
130
+ ### ReDoc Documentation (Open in browser)
131
+ ```bash
132
+ open "$API_URL/redoc"
133
+ # or
134
+ curl -X GET "$API_URL/redoc"
135
+ ```
136
+
137
+ ### OpenAPI JSON Schema
138
+ ```bash
139
+ curl -X GET "$API_URL/openapi.json"
140
+ ```
141
+
142
+ ## πŸ§ͺ Test Scenarios
143
+
144
+ ### Complete Translation Workflow
145
+ ```bash
146
+ # 1. Check API health
147
+ echo "1. Health Check:"
148
+ curl -s "$API_URL/status" | jq '.status'
149
+
150
+ # 2. Get popular languages
151
+ echo "2. Popular Languages:"
152
+ curl -s "$API_URL/languages/popular" | jq '.total_count'
153
+
154
+ # 3. Validate language code
155
+ echo "3. Validate Swahili:"
156
+ curl -s "$API_URL/languages/swh_Latn" | jq '.name'
157
+
158
+ # 4. Perform translation
159
+ echo "4. Translation:"
160
+ curl -s -X POST "$API_URL/translate" \
161
+ -H "Content-Type: application/json" \
162
+ -d '{"text": "Habari ya asubuhi", "target_language": "eng_Latn"}' \
163
+ | jq '.translated_text'
164
+ ```
165
+
166
+ ### Error Testing
167
+ ```bash
168
+ # Test invalid language code
169
+ curl -X GET "$API_URL/languages/invalid_code"
170
+
171
+ # Test empty translation text
172
+ curl -X POST "$API_URL/translate" \
173
+ -H "Content-Type: application/json" \
174
+ -d '{"text": "", "target_language": "eng_Latn"}'
175
+
176
+ # Test invalid search query
177
+ curl -X GET "$API_URL/languages/search?q=x"
178
+ ```
179
+
180
+ ## πŸ”§ Advanced Usage
181
+
182
+ ### Pretty Print JSON Response
183
+ ```bash
184
+ curl -s "$API_URL/languages/popular" | jq '.'
185
+ ```
186
+
187
+ ### Save Response to File
188
+ ```bash
189
+ curl -s "$API_URL/languages" > all_languages.json
190
+ ```
191
+
192
+ ### Check Response Headers
193
+ ```bash
194
+ curl -I "$API_URL/health"
195
+ ```
196
+
197
+ ### Measure Response Time
198
+ ```bash
199
+ curl -w "@curl-format.txt" -s -o /dev/null "$API_URL/translate" \
200
+ -H "Content-Type: application/json" \
201
+ -d '{"text": "Hello", "target_language": "swh_Latn"}'
202
+ ```
203
+
204
+ ### Multiple Translations (Batch Testing)
205
+ ```bash
206
+ # Test multiple translations
207
+ for text in "Hello" "Good morning" "Thank you" "Goodbye"; do
208
+ echo "Translating: $text"
209
+ curl -s -X POST "$API_URL/translate" \
210
+ -H "Content-Type: application/json" \
211
+ -d "{\"text\": \"$text\", \"target_language\": \"swh_Latn\"}" \
212
+ | jq -r '.translated_text'
213
+ echo "---"
214
+ done
215
+ ```
216
+
217
+ ## πŸ“Š Performance Testing
218
+
219
+ ### Simple Load Test
220
+ ```bash
221
+ # Run 10 concurrent requests
222
+ for i in {1..10}; do
223
+ curl -s -X POST "$API_URL/translate" \
224
+ -H "Content-Type: application/json" \
225
+ -d '{"text": "Hello world", "target_language": "swh_Latn"}' &
226
+ done
227
+ wait
228
+ ```
229
+
230
+ ### Rate Limit Testing
231
+ ```bash
232
+ # Test rate limiting (should hit 60 req/min limit)
233
+ for i in {1..65}; do
234
+ echo "Request $i:"
235
+ curl -s -X POST "$API_URL/translate" \
236
+ -H "Content-Type: application/json" \
237
+ -d '{"text": "Test", "target_language": "swh_Latn"}' \
238
+ | jq -r '.translated_text // .detail'
239
+ sleep 0.5
240
+ done
241
+ ```
242
+
243
+ ## πŸ’‘ Tips
244
+
245
+ ### Create curl-format.txt for timing
246
+ ```bash
247
+ cat > curl-format.txt << 'EOF'
248
+ time_namelookup: %{time_namelookup}\n
249
+ time_connect: %{time_connect}\n
250
+ time_appconnect: %{time_appconnect}\n
251
+ time_pretransfer: %{time_pretransfer}\n
252
+ time_redirect: %{time_redirect}\n
253
+ time_starttransfer: %{time_starttransfer}\n
254
+ ----------\n
255
+ time_total: %{time_total}\n
256
+ EOF
257
+ ```
258
+
259
+ ### Set API URL as environment variable
260
+ ```bash
261
+ export API_URL="https://sematech-sema-api.hf.space"
262
+ # Now you can use $API_URL in all commands
263
+ ```
docs/CURL-Test.md ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Sema Translation API - Curl Commands
2
+
3
+ ```bash
4
+ # βœ… Basic Health Check
5
+ curl https://sematech-sema-api.hf.space/health
6
+ ```
7
+
8
+ ```bash
9
+ # βœ… Simple Translation
10
+ curl -X POST "https://sematech-sema-api.hf.space/translate" \
11
+ -H "Content-Type: application/json" \
12
+ -d '{"text": "Habari ya asubuhi", "target_language": "eng_Latn"}'
13
+ ```
14
+
15
+ ```bash
16
+ # βœ… Get All Supported Languages
17
+ curl https://sematech-sema-api.hf.space/languages
18
+ ```
19
+
20
+ ```bash
21
+ # βœ… Search for a Specific Language (e.g., Swahili)
22
+ curl "https://sematech-sema-api.hf.space/languages/search?q=Swahili"
23
+ ```
24
+
25
+ ```bash
26
+ # βœ… Basic Translation with Auto-Detection (relative path)
27
+ curl -X POST "/translate" \
28
+ -H "Content-Type: application/json" \
29
+ -d '{"text": "Habari ya asubuhi", "target_language": "eng_Latn"}'
30
+ ```
31
+
32
+ ```bash
33
+ # βœ… Translation with Specified Source Language
34
+ curl -X POST "/translate" \
35
+ -H "Content-Type: application/json" \
36
+ -d '{"text": "Hello world", "source_language": "eng_Latn", "target_language": "swh_Latn"}'
37
+ ```
38
+
39
+ ---
tests/simple_test.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Simple API test script using requests
3
+ """
4
+
5
+ import requests
6
+ import json
7
+
8
+ # API base URL - change this to test different environments
9
+ API_URL = "https://sematech-sema-api.hf.space"
10
+ # API_URL = "http://localhost:8000" # For local testing
11
+
12
+ def test_health():
13
+ """Test basic health check"""
14
+ print("πŸ₯ Testing health check...")
15
+
16
+ response = requests.get(f"{API_URL}/status")
17
+ print(f"Status: {response.status_code}")
18
+
19
+ if response.status_code == 200:
20
+ data = response.json()
21
+ print(f"βœ… API is healthy")
22
+ print(f"Version: {data['version']}")
23
+ print(f"Models loaded: {data['models_loaded']}")
24
+ else:
25
+ print(f"❌ Health check failed")
26
+
27
+ print("-" * 50)
28
+
29
+ def test_translation():
30
+ """Test basic translation"""
31
+ print("🌍 Testing translation...")
32
+
33
+ # Test data
34
+ data = {
35
+ "text": "Habari ya asubuhi",
36
+ "target_language": "eng_Latn"
37
+ }
38
+
39
+ response = requests.post(
40
+ f"{API_URL}/translate",
41
+ headers={"Content-Type": "application/json"},
42
+ json=data
43
+ )
44
+
45
+ print(f"Status: {response.status_code}")
46
+
47
+ if response.status_code == 200:
48
+ result = response.json()
49
+ print(f"βœ… Translation successful")
50
+ print(f"Original: {data['text']}")
51
+ print(f"Translation: {result['translated_text']}")
52
+ print(f"Source language: {result['source_language']}")
53
+ print(f"Inference time: {result['inference_time']:.3f}s")
54
+ else:
55
+ print(f"❌ Translation failed")
56
+ print(f"Status code: {response.status_code}")
57
+ try:
58
+ error_data = response.json()
59
+ print(f"Error details: {error_data}")
60
+ except:
61
+ print(f"Error text: {response.text}")
62
+
63
+ print("-" * 50)
64
+
65
+ def test_languages():
66
+ """Test language endpoints"""
67
+ print("πŸ—£οΈ Testing language endpoints...")
68
+
69
+ # Test all languages
70
+ response = requests.get(f"{API_URL}/languages")
71
+ if response.status_code == 200:
72
+ data = response.json()
73
+ print(f"βœ… Found {data['total_count']} supported languages")
74
+ else:
75
+ print(f"❌ Failed to get languages")
76
+
77
+ # Test popular languages
78
+ response = requests.get(f"{API_URL}/languages/popular")
79
+ if response.status_code == 200:
80
+ data = response.json()
81
+ print(f"βœ… Found {data['total_count']} popular languages")
82
+ else:
83
+ print(f"❌ Failed to get popular languages")
84
+
85
+ # Test specific language
86
+ response = requests.get(f"{API_URL}/languages/swh_Latn")
87
+ if response.status_code == 200:
88
+ data = response.json()
89
+ print(f"βœ… Swahili info: {data['name']} ({data['native_name']})")
90
+ else:
91
+ print(f"❌ Failed to get Swahili info")
92
+
93
+ print("-" * 50)
94
+
95
+ def test_search():
96
+ """Test language search"""
97
+ print("πŸ” Testing language search...")
98
+
99
+ response = requests.get(f"{API_URL}/languages/search?q=Swahili")
100
+
101
+ if response.status_code == 200:
102
+ data = response.json()
103
+ print(f"βœ… Search found {data['total_count']} results")
104
+ for code, info in data['languages'].items():
105
+ print(f" {code}: {info['name']} ({info['native_name']})")
106
+ else:
107
+ print(f"❌ Search failed")
108
+
109
+ print("-" * 50)
110
+
111
+ def run_all_tests():
112
+ """Run all tests"""
113
+ print(f"πŸ§ͺ Testing API at: {API_URL}")
114
+ print("=" * 50)
115
+
116
+ test_health()
117
+ test_translation()
118
+ test_languages()
119
+ test_search()
120
+
121
+ print("πŸŽ‰ All tests completed!")
122
+
123
+ if __name__ == "__main__":
124
+ run_all_tests()