fix(api): resolve SlowAPI request param conflict, update schema_extra naming, enhance tests, and serve Swagger UI at root
Browse files- README.md +2 -2
- app/api/v1/endpoints.py +21 -21
- app/main.py +5 -3
- app/models/schemas.py +4 -4
- curl_commands.md +263 -0
- docs/CURL-Test.md +39 -0
- tests/simple_test.py +124 -0
README.md
CHANGED
@@ -72,10 +72,10 @@ app/
|
|
72 |
## π API Endpoints
|
73 |
|
74 |
### Health & Monitoring
|
75 |
-
- **`GET /`** -
|
|
|
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
|
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 |
-
|
186 |
-
|
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 =
|
243 |
|
244 |
# Validate text length
|
245 |
-
if len(
|
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(
|
253 |
|
254 |
# Log translation request
|
255 |
logger.info(
|
256 |
"translation_started",
|
257 |
request_id=request_id,
|
258 |
-
source_language=
|
259 |
-
target_language=
|
260 |
character_count=character_count
|
261 |
)
|
262 |
|
263 |
try:
|
264 |
-
if
|
265 |
# Use provided source language
|
266 |
translated_text, inference_time = translate_with_source(
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
)
|
271 |
-
source_lang =
|
272 |
else:
|
273 |
# Auto-detect source language
|
274 |
source_lang, translated_text, inference_time = translate_with_detection(
|
275 |
-
|
276 |
-
|
277 |
)
|
278 |
|
279 |
# Update metrics
|
280 |
TRANSLATION_COUNT.labels(
|
281 |
source_lang=source_lang,
|
282 |
-
target_lang=
|
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=
|
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=
|
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=
|
315 |
-
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="/
|
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 |
-
|
|
|
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: /
|
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 |
-
|
41 |
"examples": [
|
42 |
{
|
43 |
"summary": "Auto-detect source language",
|
@@ -128,7 +128,7 @@ class TranslationResponse(BaseModel):
|
|
128 |
)
|
129 |
|
130 |
class Config:
|
131 |
-
|
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 |
-
|
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 |
-
|
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()
|