From bec1be115902f7d4038b650b651a1c82c9d874b7 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 05:33:29 +0000 Subject: [PATCH] docs: sync all API endpoint docs with codebase implementation Align openapi.yaml and all 11 API reference pages with the actual Supabase edge function implementations. Key corrections: - /classify: intent is now an array, added cost_usd/credits_used fields, removed undocumented platform/channel_id/metadata params - /rubric-classify: categories is array of {name, description} objects, added multi_label and confidence_threshold params - /comments/reply: endpoint is POST /v1/comments/reply (not /:id/reply), text is required body param, response uses classification/reply objects - /feedback: body uses classification_id (not request_id), rating enum is correct/incorrect/partial with corrected_intent/corrected_sentiment - /health: response is flat JSON (not standard envelope), services are database/redis/nagl with latency_ms - /analytics: group_by supports endpoint/day/provider (not week/month), response uses analytics summary object with breakdown - /usage: group_by supports endpoint/day only, response uses usage object - /comments: offset-based pagination (not cursor-based) - /translate: added tokens_used field, noted source!=target requirement - All pages: added curl/Python/TypeScript code samples, corrected error type names to match error envelope v1.2 Generated-By: mintlify-agent --- api-reference/analytics.mdx | 128 ++-- api-reference/classify.mdx | 313 ++++------ api-reference/comments-list.mdx | 86 ++- api-reference/comments-reply.mdx | 115 ++-- api-reference/detect.mdx | 39 +- api-reference/feedback.mdx | 89 ++- api-reference/health.mdx | 97 +++- api-reference/moderate.mdx | 111 ++-- api-reference/rubric-classify.mdx | 160 ++--- api-reference/translate.mdx | 121 ++-- api-reference/usage.mdx | 97 ++-- openapi.yaml | 932 ++++++++++++++---------------- 12 files changed, 1191 insertions(+), 1097 deletions(-) diff --git a/api-reference/analytics.mdx b/api-reference/analytics.mdx index c2879c7..399f0e3 100644 --- a/api-reference/analytics.mdx +++ b/api-reference/analytics.mdx @@ -1,11 +1,11 @@ --- title: "Analytics" sidebarTitle: "GET /v1/analytics" -description: "Aggregated analytics on comment trends, sentiment shifts, and engagement patterns." +description: "Aggregated analytics on API usage patterns, costs, cache performance, and latency." api: "GET https://api.trynawa.com/v1/analytics" --- -Retrieve aggregated analytics across your classified comments. This endpoint is **free**. +Retrieve aggregated analytics on your API usage including request counts, costs, cache hits, latency, and error rates. This endpoint is **free**. ## Request @@ -13,19 +13,40 @@ Retrieve aggregated analytics across your classified comments. This endpoint is | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `channel_id` | string | No | Filter by channel ID | -| `platform` | string | No | Filter by platform | | `from` | string | No | Start date (ISO 8601) | | `to` | string | No | End date (ISO 8601) | -| `group_by` | string | No | Group results by: `day`, `week`, `month`. Default: `day`. | +| `group_by` | string | No | Group breakdown by: `endpoint`, `day`, or `provider`. Default: `endpoint`. | ### Example request -```bash -curl "https://api.trynawa.com/v1/analytics?platform=youtube&from=2025-01-01T00:00:00Z&to=2025-01-31T23:59:59Z&group_by=week" \ + + +```bash cURL +curl "https://api.trynawa.com/v1/analytics?from=2025-01-01T00:00:00Z&to=2025-01-31T23:59:59Z&group_by=endpoint" \ -H "Authorization: Bearer nawa_test_sk_xxx" ``` +```python Python +import requests + +response = requests.get( + "https://api.trynawa.com/v1/analytics", + params={"from": "2025-01-01T00:00:00Z", "group_by": "endpoint"}, + headers={"Authorization": "Bearer nawa_test_sk_xxx"} +) +data = response.json() +``` + +```typescript TypeScript +const response = await fetch( + 'https://api.trynawa.com/v1/analytics?from=2025-01-01T00:00:00Z&group_by=endpoint', + { headers: { Authorization: 'Bearer nawa_test_sk_xxx' } } +) +const data = await response.json() +``` + + + ## Response ### Success response (200) @@ -34,49 +55,72 @@ curl "https://api.trynawa.com/v1/analytics?platform=youtube&from=2025-01-01T00:0 { "success": true, "result": { - "period": { - "from": "2025-01-01T00:00:00Z", - "to": "2025-01-31T23:59:59Z" - }, - "total_comments": 4521, - "summary": { - "intent": { - "question": 1205, - "praise": 1890, - "complaint": 678, - "suggestion": 412, - "spam": 198, - "other": 138 - }, - "sentiment": { - "positive": 2100, - "negative": 890, - "neutral": 1231, - "mixed": 300 + "analytics": { + "total_requests": 12450, + "total_cost": 68.70, + "cache_hits": 2231, + "cache_hit_rate": 0.18, + "avg_latency_ms": 920, + "success_rate": 0.99, + "error_count": 125, + "endpoint_distribution": { + "/v1/classify": 8500, + "/v1/translate": 1200, + "/v1/comments/reply": 1750, + "/v1/detect": 1000 }, - "dialect": { - "gulf": 2015, - "egyptian": 1302, - "levantine": 789, - "msa": 415 + "provider_distribution": { + "allam": 9200, + "claude": 3250 }, - "toxicity": { - "none": 3950, - "mild": 320, - "moderate": 180, - "severe": 71 + "period": { + "from": "2025-01-01T00:00:00Z", + "to": "2025-01-31T23:59:59Z" } }, - "trends": [ + "by_endpoint": [ { - "period": "2025-01-06/2025-01-12", - "total": 1123, - "sentiment_score": 0.72, - "top_intent": "praise" + "endpoint": "/v1/classify", + "requests": 8500, + "cost": 51.00, + "avg_latency_ms": 820 + }, + { + "endpoint": "/v1/translate", + "requests": 1200, + "cost": 6.00, + "avg_latency_ms": 1100 } ] }, "errors": [], - "request_id": "req_ana_abc123" + "request_id": "req_nw_ana1a2b3c4d5e" } ``` + +### Analytics summary fields + +| Field | Type | Description | +|-------|------|-------------| +| `total_requests` | integer | Total API requests in the period | +| `total_cost` | number | Total cost in USD | +| `cache_hits` | integer | Number of requests served from cache | +| `cache_hit_rate` | number | Cache hit rate as a decimal (0-1) | +| `avg_latency_ms` | number | Average response latency in milliseconds | +| `success_rate` | number | Success rate as a decimal (0-1) | +| `error_count` | integer | Number of requests with status >= 400 | +| `endpoint_distribution` | object | Request counts per endpoint | +| `provider_distribution` | object | Request counts per AI provider | +| `period` | object | The `from` and `to` dates (null if not specified) | + +### Breakdown items + +The breakdown key changes based on `group_by`: + +| `group_by` | Key | Breakdown fields | +|------------|-----|-----------------| +| `endpoint` | `by_endpoint` | `endpoint`, `requests`, `cost`, `avg_latency_ms` | +| `day` | `by_day` | `day` (YYYY-MM-DD), `requests`, `cost`, `avg_latency_ms` | +| `provider` | `by_provider` | `provider`, `requests`, `cost`, `avg_latency_ms` | + +Breakdowns are sorted by `requests` descending. diff --git a/api-reference/classify.mdx b/api-reference/classify.mdx index ac3d730..6c17100 100644 --- a/api-reference/classify.mdx +++ b/api-reference/classify.mdx @@ -1,18 +1,18 @@ --- -title: "Classify Comment" +title: "Classify text" sidebarTitle: "POST /v1/classify" -description: "Classify an Arabic or English comment by intent, sentiment, dialect, and toxicity in a single API call." +description: "Classify Arabic or English text by intent, sentiment, dialect, and priority in a single API call." api: "POST https://api.trynawa.com/v1/classify" --- -Classify any comment with a single request. Returns intent, sentiment, dialect, and toxicity analysis. Arabic comments are routed to HUMAIN's ALLaM model for dialect detection. English comments are routed to Claude for high-accuracy classification. - -## Request +Classify any comment with a single request. Returns intent (as an array), sentiment, dialect, priority, and a suggested reply direction. Arabic text is routed to HUMAIN's ALLaM model for dialect-aware classification. English text is routed to Claude. Semantic caching reduces cost for repeated or similar inputs. - Cost: **$0.006** per request. Semantic cache hits are free (`X-NAWA-Cache: HIT`). + Cost: **$0.006** per request (6 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`). +## Request + ### Headers | Header | Required | Description | @@ -20,14 +20,18 @@ Classify any comment with a single request. Returns intent, sentiment, dialect, | `Authorization` | Yes | `Bearer nawa_live_sk_xxx` or `Bearer nawa_test_sk_xxx` | | `Content-Type` | Yes | `application/json` | +### Query parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `provider` | string | No | Force a specific AI provider for A/B testing: `claude`, `gemini`, or `allam`. | + ### Body parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `text` | string | Yes | The comment text to classify. Max 5,000 characters. | -| `platform` | string | No | Source platform: `youtube`, `instagram`, `twitter`, `facebook`. Improves accuracy with platform-specific context. | -| `channel_id` | string | No | Your channel or account identifier for analytics grouping. | -| `metadata` | object | No | Arbitrary key-value metadata to attach to the classification. | +| `text` | string | Yes | The text to classify. | +| `context` | object | No | Optional context object (reserved for future use). | ### Example request @@ -38,36 +42,28 @@ curl -X POST https://api.trynawa.com/v1/classify \ -H "Authorization: Bearer nawa_test_sk_xxx" \ -H "Content-Type: application/json" \ -d '{ - "text": "متى الجزء الثاني؟", - "platform": "youtube", - "channel_id": "ch_123" + "text": "متى الجزء الثاني؟" }' ``` +```python Python +from nawa import Nawa + +nawa = Nawa(api_key="your_api_key") + +result = nawa.classify(text="متى الجزء الثاني؟") +``` + ```typescript TypeScript import { Nawa } from '@nawalabs/sdk' const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY }) const { data, error } = await nawa.classify({ - text: 'متى الجزء الثاني؟', - platform: 'youtube', - channelId: 'ch_123' + text: 'متى الجزء الثاني؟' }) ``` -```python Python -from nawa import Nawa - -nawa = Nawa(api_key="your_api_key") - -result = nawa.classify( - text="متى الجزء الثاني؟", - platform="youtube", - channel_id="ch_123" -) -``` - ## Response @@ -78,22 +74,28 @@ result = nawa.classify( { "success": true, "result": { - "text": "متى الجزء الثاني؟", - "intent": "question", - "intent_confidence": 0.97, + "id": "cls_nw_a1b2c3d4e5f6", + "object": "classification", + "intent": ["question"], "sentiment": "neutral", - "sentiment_confidence": 0.91, - "dialect": "gulf", - "dialect_confidence": 0.95, - "toxicity": "none", - "toxicity_confidence": 0.99, - "categories": ["engagement"], "language": "ar", - "model": "nagl-v1", - "cached": false + "dialect": "gulf", + "dialect_confidence": 0.92, + "requires_response": true, + "priority": "medium", + "suggested_reply": { + "text": "", + "direction": "rtl" + }, + "provider": "allam", + "model": "sdaia/allam-1-13b-instruct", + "fallback_used": false, + "tokens_used": null, + "cost_usd": 0.006, + "credits_used": 6 }, "errors": [], - "request_id": "req_abc123def456" + "request_id": "req_nw_a1b2c3d4e5f67890abcdef12" } ``` @@ -105,35 +107,37 @@ result = nawa.classify( | `X-RateLimit-Limit` | Rate limit ceiling for current window | | `X-RateLimit-Remaining` | Requests remaining in current window | | `X-RateLimit-Reset` | Window reset time (RFC 3339) | -| `X-NAWA-Balance` | Current credit balance in USD | -| `X-NAWA-Balance-Warning` | `low_balance` when below $5 | -| `X-NAWA-Cache` | `HIT` if served from semantic cache (no cost) | +| `X-NAWA-Cache` | `HIT` if served from semantic cache (no cost), `MISS` otherwise | +| `X-NAWA-Provider` | AI provider used (`allam`, `claude`, or `gemini`) | ### Result fields | Field | Type | Description | |-------|------|-------------| -| `text` | string | The original input text | -| `intent` | string | `question`, `complaint`, `praise`, `suggestion`, `spam`, `other` | -| `intent_confidence` | number | Confidence score (0–1) | -| `sentiment` | string | `positive`, `negative`, `neutral`, `mixed` | -| `sentiment_confidence` | number | Confidence score (0–1) | -| `dialect` | string \| null | `gulf`, `egyptian`, `levantine`, `msa`. Returns `null` for English text. | -| `dialect_confidence` | number \| null | Confidence score (0-1). Returns `null` for English text. | -| `toxicity` | string | `none`, `mild`, `moderate`, `severe` | -| `toxicity_confidence` | number | Confidence score (0–1) | -| `categories` | string[] | Content categories: `engagement`, `support`, `feedback`, `spam` | -| `language` | string | Detected language code (e.g., `ar`, `en`) | -| `model` | string | Model version used for classification | -| `cached` | boolean | Whether this was served from semantic cache | +| `id` | string | Classification ID in `cls_nw_xxx` format | +| `object` | string | Always `"classification"` | +| `intent` | string[] | Detected intents as an array (e.g. `["question"]`, `["praise", "suggestion"]`) | +| `sentiment` | string | `positive`, `negative`, `neutral`, or `mixed` | +| `language` | string | Detected language: `ar`, `en`, or `mixed` | +| `dialect` | string or null | Arabic dialect: `gulf`, `egyptian`, `levantine`, `msa`. Null for English text. | +| `dialect_confidence` | number or null | Confidence score 0-1. Null for English text. | +| `requires_response` | boolean | Whether the comment warrants a reply | +| `priority` | string | `high`, `medium`, or `low` | +| `suggested_reply` | object | Contains `text` (reply suggestion) and `direction` (`rtl` or `ltr`) | +| `provider` | string | AI provider used: `allam`, `claude`, or `gemini` | +| `model` | string | Model identifier | +| `fallback_used` | boolean | Whether a fallback provider was used | +| `tokens_used` | integer or null | Always `null` in the current version | +| `cost_usd` | number | Cost in USD | +| `credits_used` | integer | Credits deducted (6 per request) | ### Error responses | Status | Type | When | |--------|------|------| -| 400 | `invalid_request_error` | Missing `text`, invalid `platform`, text too long | +| 400 | `invalid_request_error` | Missing `text` or invalid `provider` query parameter | | 401 | `authentication_error` | Invalid or missing API key | -| 402 | `insufficient_credits` | No credits remaining | +| 402 | `permission_error` | No credits remaining | | 429 | `rate_limit_error` | Rate limit exceeded | | 500 | `api_error` | Internal or provider error | @@ -145,7 +149,7 @@ result = nawa.classify( curl -X POST https://api.trynawa.com/v1/classify \ -H "Authorization: Bearer nawa_test_sk_xxx" \ -H "Content-Type: application/json" \ - -d '{"text": "This is hands down the best review I have seen on this phone. Subscribed!", "platform": "youtube"}' + -d '{"text": "This is hands down the best review I have seen on this phone. Subscribed!"}' ``` Response: @@ -153,98 +157,38 @@ result = nawa.classify( { "success": true, "result": { - "text": "This is hands down the best review I have seen on this phone. Subscribed!", - "intent": "praise", - "intent_confidence": 0.96, + "id": "cls_nw_en_praise_001", + "object": "classification", + "intent": ["praise"], "sentiment": "positive", - "sentiment_confidence": 0.98, - "dialect": null, - "dialect_confidence": null, - "toxicity": "none", - "toxicity_confidence": 0.99, - "categories": ["engagement"], - "language": "en", - "model": "claude-v1", - "cached": false - }, - "errors": [], - "request_id": "req_en_praise_001" - } - ``` - - - - ```bash - curl -X POST https://api.trynawa.com/v1/classify \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ - -H "Content-Type: application/json" \ - -d '{"text": "The audio quality is terrible in this one. Can barely hear anything after the 5 minute mark.", "platform": "youtube"}' - ``` - - Response: - ```json - { - "success": true, - "result": { - "text": "The audio quality is terrible in this one. Can barely hear anything after the 5 minute mark.", - "intent": "complaint", - "intent_confidence": 0.94, - "sentiment": "negative", - "sentiment_confidence": 0.96, - "dialect": null, - "dialect_confidence": null, - "toxicity": "none", - "toxicity_confidence": 0.97, - "categories": ["feedback"], "language": "en", - "model": "claude-v1", - "cached": false - }, - "errors": [], - "request_id": "req_en_complaint_001" - } - ``` - - - - ```bash - curl -X POST https://api.trynawa.com/v1/classify \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ - -H "Content-Type: application/json" \ - -d '{"text": "What camera and lens setup are you using for these shots?", "platform": "youtube"}' - ``` - - Response: - ```json - { - "success": true, - "result": { - "text": "What camera and lens setup are you using for these shots?", - "intent": "question", - "intent_confidence": 0.97, - "sentiment": "neutral", - "sentiment_confidence": 0.92, "dialect": null, "dialect_confidence": null, - "toxicity": "none", - "toxicity_confidence": 0.99, - "categories": ["engagement"], - "language": "en", - "model": "claude-v1", - "cached": false + "requires_response": false, + "priority": "low", + "suggested_reply": { + "text": "", + "direction": "ltr" + }, + "provider": "claude", + "model": "claude-haiku-4-5-20251001", + "fallback_used": false, + "tokens_used": null, + "cost_usd": 0.006, + "credits_used": 6 }, "errors": [], - "request_id": "req_en_question_001" + "request_id": "req_nw_en_praise_001" } ``` - + ```bash curl -X POST https://api.trynawa.com/v1/classify \ -H "Authorization: Bearer nawa_test_sk_xxx" \ -H "Content-Type: application/json" \ - -d '{"text": "ما شاء الله عليك، محتوى رهيب!", "platform": "youtube"}' + -d '{"text": "ما شاء الله عليك، محتوى رهيب!"}' ``` Response: @@ -252,32 +196,38 @@ result = nawa.classify( { "success": true, "result": { - "text": "ما شاء الله عليك، محتوى رهيب!", - "intent": "praise", - "intent_confidence": 0.98, + "id": "cls_nw_ar_praise_001", + "object": "classification", + "intent": ["praise"], "sentiment": "positive", - "sentiment_confidence": 0.97, + "language": "ar", "dialect": "gulf", "dialect_confidence": 0.93, - "toxicity": "none", - "toxicity_confidence": 0.99, - "categories": ["engagement"], - "language": "ar", - "model": "nagl-v1", - "cached": false + "requires_response": false, + "priority": "low", + "suggested_reply": { + "text": "", + "direction": "rtl" + }, + "provider": "allam", + "model": "sdaia/allam-1-13b-instruct", + "fallback_used": false, + "tokens_used": null, + "cost_usd": 0.006, + "credits_used": 6 }, "errors": [], - "request_id": "req_ghi789jkl012" + "request_id": "req_nw_ar_praise_001" } ``` - + ```bash curl -X POST https://api.trynawa.com/v1/classify \ -H "Authorization: Bearer nawa_test_sk_xxx" \ -H "Content-Type: application/json" \ - -d '{"text": "الصوت وحش أوي في الفيديو ده", "platform": "youtube"}' + -d '{"text": "الصوت وحش أوي في الفيديو ده"}' ``` Response: @@ -285,55 +235,28 @@ result = nawa.classify( { "success": true, "result": { - "text": "الصوت وحش أوي في الفيديو ده", - "intent": "complaint", - "intent_confidence": 0.94, + "id": "cls_nw_eg_comp_001", + "object": "classification", + "intent": ["complaint"], "sentiment": "negative", - "sentiment_confidence": 0.96, - "dialect": "egyptian", - "dialect_confidence": 0.97, - "toxicity": "none", - "toxicity_confidence": 0.95, - "categories": ["feedback"], "language": "ar", - "model": "nagl-v1", - "cached": false - }, - "errors": [], - "request_id": "req_mno345pqr678" - } - ``` - - - - ```bash - curl -X POST https://api.trynawa.com/v1/classify \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ - -H "Content-Type: application/json" \ - -d '{"text": "لو سمحت حاول تحكي عن المطاعم بلبنان", "platform": "instagram"}' - ``` - - Response: - ```json - { - "success": true, - "result": { - "text": "لو سمحت حاول تحكي عن المطاعم بلبنان", - "intent": "suggestion", - "intent_confidence": 0.92, - "sentiment": "neutral", - "sentiment_confidence": 0.88, - "dialect": "levantine", - "dialect_confidence": 0.96, - "toxicity": "none", - "toxicity_confidence": 0.99, - "categories": ["engagement"], - "language": "ar", - "model": "nagl-v1", - "cached": false + "dialect": "gulf", + "dialect_confidence": 0.94, + "requires_response": true, + "priority": "high", + "suggested_reply": { + "text": "", + "direction": "rtl" + }, + "provider": "allam", + "model": "sdaia/allam-1-13b-instruct", + "fallback_used": false, + "tokens_used": null, + "cost_usd": 0.006, + "credits_used": 6 }, "errors": [], - "request_id": "req_stu901vwx234" + "request_id": "req_nw_eg_comp_001" } ``` diff --git a/api-reference/comments-list.mdx b/api-reference/comments-list.mdx index 1e18d08..f410085 100644 --- a/api-reference/comments-list.mdx +++ b/api-reference/comments-list.mdx @@ -1,11 +1,11 @@ --- -title: "List Comments" +title: "List comments" sidebarTitle: "GET /v1/comments" -description: "Retrieve and filter classified comments with pagination." +description: "Retrieve and filter classified comments with offset-based pagination." api: "GET https://api.trynawa.com/v1/comments" --- -Retrieve classified comments with filtering and pagination. This endpoint is **free**. +Retrieve classified comments with filtering and offset-based pagination. This endpoint is **free**. ## Request @@ -13,27 +13,43 @@ Retrieve classified comments with filtering and pagination. This endpoint is **f | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `channel_id` | string | No | Filter by channel ID | | `platform` | string | No | Filter by platform: `youtube`, `instagram`, `twitter`, `facebook` | | `intent` | string | No | Filter by intent: `question`, `complaint`, `praise`, `suggestion`, `spam`, `other` | | `sentiment` | string | No | Filter by sentiment: `positive`, `negative`, `neutral`, `mixed` | | `dialect` | string | No | Filter by dialect: `gulf`, `egyptian`, `levantine`, `msa` | -| `toxicity` | string | No | Filter by toxicity: `none`, `mild`, `moderate`, `severe` | -| `from` | string | No | Start date (ISO 8601): `2025-01-01T00:00:00Z` | -| `to` | string | No | End date (ISO 8601): `2025-01-31T23:59:59Z` | -| `limit` | integer | No | Results per page (1–100). Default: 25. | -| `cursor` | string | No | Pagination cursor from previous response. | +| `limit` | integer | No | Results per page (1-100). Default: `25`. | +| `offset` | integer | No | Number of results to skip. Default: `0`. | ### Example request ```bash cURL -curl "https://api.trynawa.com/v1/comments?platform=youtube&intent=question&limit=10" \ +curl "https://api.trynawa.com/v1/comments?platform=youtube&intent=question&limit=10&offset=0" \ -H "Authorization: Bearer nawa_test_sk_xxx" ``` +```python Python +from nawa import Nawa + +nawa = Nawa(api_key="your_api_key") + +result = nawa.comments.list( + platform="youtube", + intent="question", + limit=10 +) + +# Iterate through pages +for comment in nawa.comments.list(platform="youtube"): + print(comment.text) +``` + ```typescript TypeScript +import { Nawa } from '@nawalabs/sdk' + +const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY }) + const { data, error } = await nawa.comments.list({ platform: 'youtube', intent: 'question', @@ -46,18 +62,6 @@ for await (const comment of nawa.comments.list({ platform: 'youtube' })) { } ``` -```python Python -result = nawa.comments.list( - platform="youtube", - intent="question", - limit=10 -) - -# Auto-pagination -for comment in nawa.comments.list(platform="youtube"): - print(comment.text) -``` - ## Response @@ -71,23 +75,47 @@ for comment in nawa.comments.list(platform="youtube"): "comments": [ { "id": "cmt_abc123", + "request_id": "req_nw_a1b2c3d4e5f6", "text": "متى الجزء الثاني؟", "platform": "youtube", "intent": "question", "sentiment": "neutral", "dialect": "gulf", - "toxicity": "none", - "created_at": "2025-01-15T10:30:00Z", - "channel_id": "ch_123" + "created_at": "2025-01-15T10:30:00Z" } ], "pagination": { - "has_more": true, - "next_cursor": "cur_def456", - "total_count": 1523 + "total": 1523, + "limit": 25, + "offset": 0, + "has_more": true } }, "errors": [], - "request_id": "req_lst_abc123" + "request_id": "req_nw_lst1a2b3c4d5e" } ``` + +### Comment fields + +| Field | Type | Description | +|-------|------|-------------| +| `id` | string | Comment ID | +| `request_id` | string | The original classification request ID | +| `text` | string | The comment text | +| `platform` | string | Source platform | +| `intent` | string | Detected intent | +| `sentiment` | string | Detected sentiment | +| `dialect` | string | Detected Arabic dialect | +| `created_at` | string | ISO 8601 timestamp | + +### Pagination fields + +| Field | Type | Description | +|-------|------|-------------| +| `total` | integer | Total matching comments | +| `limit` | integer | Results per page | +| `offset` | integer | Current offset | +| `has_more` | boolean | Whether more results exist beyond this page | + +Results are ordered by `created_at` descending (newest first). diff --git a/api-reference/comments-reply.mdx b/api-reference/comments-reply.mdx index d6321d7..efcc9b6 100644 --- a/api-reference/comments-reply.mdx +++ b/api-reference/comments-reply.mdx @@ -1,62 +1,71 @@ --- -title: "Reply to Comment" -sidebarTitle: "POST /v1/comments/:id/reply" -description: "Generate a context-aware reply to a comment in Arabic or English." -api: "POST https://api.trynawa.com/v1/comments/{id}/reply" +title: "Classify and reply" +sidebarTitle: "POST /v1/comments/reply" +description: "Classify a comment and generate a contextual reply in a single API call." +api: "POST https://api.trynawa.com/v1/comments/reply" --- -Generate an AI-powered reply that matches the commenter's language and cultural context. For Arabic comments, replies match the detected dialect (Gulf, Egyptian, Levantine, MSA). For English comments, replies are natural and platform-appropriate. Language is auto-detected unless overridden. +Classify a comment and generate a culturally-aware reply in a single call. For Arabic comments, replies match the detected dialect. Supports tone control and max length configuration. - Cost: **$0.008** per request (8 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`). + Cost: **$0.008** per request (8 credits). This endpoint does not use semantic caching. ## Request -### Path parameters +### Query parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `id` | string | Yes | The comment ID to reply to. | +| `provider` | string | No | Force a specific AI provider: `claude`, `gemini`, or `allam`. | ### Body parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `tone` | string | No | Reply tone: `friendly`, `professional`, `casual`, `formal`. Default: `friendly`. | -| `max_length` | integer | No | Maximum reply length in characters. Default: 500. | -| `context` | string | No | Additional context about the channel or video to improve reply relevance. | -| `language` | string | No | Force reply language: `ar`, `en`, `auto`. Default: `auto` (matches commenter's language). | +| `text` | string | Yes | The comment text to classify and reply to. | +| `tone` | string | No | Reply tone: `friendly`, `professional`, `casual`, `formal`. Default: `professional`. | +| `max_length` | integer | No | Maximum reply length in characters (1-2000). Default: `500`. | ### Example request ```bash cURL -curl -X POST https://api.trynawa.com/v1/comments/cmt_abc123/reply \ +curl -X POST https://api.trynawa.com/v1/comments/reply \ -H "Authorization: Bearer nawa_test_sk_xxx" \ -H "Content-Type: application/json" \ -d '{ - "tone": "friendly", - "context": "Tech review channel focused on smartphones" + "text": "هذا المنتج سيء جداً ولا أنصح به", + "tone": "professional", + "max_length": 500 }' ``` -```typescript TypeScript -const { data, error } = await nawa.comments.reply('cmt_abc123', { - tone: 'friendly', - context: 'Tech review channel focused on smartphones' -}) -``` - ```python Python +from nawa import Nawa + +nawa = Nawa(api_key="your_api_key") + result = nawa.comments.reply( - comment_id="cmt_abc123", - tone="friendly", - context="Tech review channel focused on smartphones" + text="هذا المنتج سيء جداً ولا أنصح به", + tone="professional", + max_length=500 ) ``` +```typescript TypeScript +import { Nawa } from '@nawalabs/sdk' + +const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY }) + +const { data, error } = await nawa.comments.reply({ + text: 'هذا المنتج سيء جداً ولا أنصح به', + tone: 'professional', + maxLength: 500 +}) +``` + ## Response @@ -67,15 +76,26 @@ result = nawa.comments.reply( { "success": true, "result": { - "comment_id": "cmt_abc123", - "reply_text": "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔", - "reply_dialect": "gulf", - "tone": "friendly", - "original_intent": "question", - "original_dialect": "gulf" + "id": "rpl_nw_a1b2c3d4e5f6", + "object": "comment_reply", + "classification": { + "intent": ["complaint"], + "sentiment": "negative", + "priority": "high", + "requires_response": true + }, + "reply": { + "text": "نعتذر عن التجربة السيئة. نتمنى تتواصل معنا عشان نقدر نساعدك.", + "direction": "rtl", + "tone": "professional" + }, + "provider": "allam", + "model": "sdaia/allam-1-13b-instruct", + "cost_usd": 0.008, + "credits_used": 8 }, "errors": [], - "request_id": "req_rep789xyz012" + "request_id": "req_nw_rpl1a2b3c4d5e" } ``` @@ -83,9 +103,28 @@ result = nawa.comments.reply( | Field | Type | Description | |-------|------|-------------| -| `comment_id` | string | The comment that was replied to | -| `reply_text` | string | The generated reply text | -| `reply_dialect` | string \| null | Dialect used in the reply (matches original). `null` for English replies. | -| `tone` | string | The tone used for the reply | -| `original_intent` | string | Detected intent of the original comment | -| `original_dialect` | string | Detected dialect of the original comment | +| `id` | string | Reply ID in `rpl_nw_xxx` format | +| `object` | string | Always `"comment_reply"` | +| `classification` | object | Classification results for the input comment | +| `classification.intent` | string[] | Detected intents as an array | +| `classification.sentiment` | string | `positive`, `negative`, `neutral`, or `mixed` | +| `classification.priority` | string | `high`, `medium`, or `low` | +| `classification.requires_response` | boolean | Whether the comment warrants a reply | +| `reply` | object | The generated reply | +| `reply.text` | string | Generated reply text | +| `reply.direction` | string | `rtl` for Arabic, `ltr` for English | +| `reply.tone` | string | The tone used | +| `provider` | string | AI provider used | +| `model` | string | Model identifier | +| `cost_usd` | number | Cost in USD | +| `credits_used` | integer | Credits deducted (8 per request) | + +### Error responses + +| Status | Type | When | +|--------|------|------| +| 400 | `invalid_request_error` | Missing `text`. Invalid `tone` or `max_length`. | +| 401 | `authentication_error` | Invalid or missing API key | +| 402 | `permission_error` | No credits remaining | +| 429 | `rate_limit_error` | Rate limit exceeded | +| 500 | `api_error` | Internal or provider error | diff --git a/api-reference/detect.mdx b/api-reference/detect.mdx index 2169b2c..da78907 100644 --- a/api-reference/detect.mdx +++ b/api-reference/detect.mdx @@ -1,5 +1,5 @@ --- -title: "Detect Language & Dialect" +title: "Detect language and dialect" sidebarTitle: "POST /v1/detect" description: "Lightweight language and dialect detection. Returns in under 100ms -- no AI model call required." api: "POST https://api.trynawa.com/v1/detect" @@ -29,12 +29,23 @@ Detect language, dialect, script, and text direction from any text input. Uses l ### Example request + ```bash cURL curl -X POST https://api.trynawa.com/v1/detect \ -H "Authorization: Bearer nawa_test_sk_xxx" \ -H "Content-Type: application/json" \ -d '{"text": "وش رايكم بالمحتوى الجديد؟"}' ``` + +```python Python +from nawa import Nawa + +nawa = Nawa(api_key="your_api_key") + +result = nawa.detect(text="وش رايكم بالمحتوى الجديد؟") +print(f"{result.data.language} / {result.data.dialect}") +``` + ```typescript TypeScript import { Nawa } from '@nawalabs/sdk' @@ -43,19 +54,15 @@ const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY }) const { data, error } = await nawa.detect({ text: 'وش رايكم بالمحتوى الجديد؟' }) +console.log(`${data.language} / ${data.dialect}`) ``` -```python Python -from nawa import Nawa -nawa = Nawa(api_key="your_api_key") - -result = nawa.detect(text="وش رايكم بالمحتوى الجديد؟") -``` ## Response ### Success response (200) + ```json { "success": true, @@ -70,12 +77,12 @@ result = nawa.detect(text="وش رايكم بالمحتوى الجديد؟") "text_direction": "rtl", "word_count": 4, "fallback_used": false, - "cached": false, "cost_usd": 0.002, - "credits_used": 2 + "credits_used": 2, + "cached": false }, "errors": [], - "request_id": "req_nw_4c11w71e44cb" + "request_id": "req_nw_det1a2b3c4d5e" } ``` @@ -83,17 +90,19 @@ result = nawa.detect(text="وش رايكم بالمحتوى الجديد؟") | Field | Type | Description | |-------|------|-------------| +| `id` | string | Detection ID in `det_nw_xxx` format | +| `object` | string | Always `"detection"` | | `text` | string | The original input text | | `language` | string | Detected language: `ar`, `en`, or `mixed` | | `dialect` | string or null | Detected Arabic dialect: `gulf`, `egyptian`, `levantine`, `msa`. Null if not Arabic. | -| `dialect_confidence` | number or null | Confidence score 0--1 for dialect detection. Null if not Arabic. | +| `dialect_confidence` | number or null | Confidence score 0-1 for dialect detection. Null if not Arabic. | | `script` | string | Script type: `arabic`, `latin`, or `mixed` | | `text_direction` | string | Text direction: `rtl` or `ltr` | -| `word_count` | number | Number of words in the input text | +| `word_count` | integer | Number of words in the input text | | `fallback_used` | boolean | Always `false` for this endpoint (local detection only) | -| `cached` | boolean | Whether served from semantic cache | | `cost_usd` | number | Cost in USD | -| `credits_used` | number | Credits deducted | +| `credits_used` | integer | Credits deducted (2 per request) | +| `cached` | boolean | Whether served from semantic cache | ### Detection examples @@ -149,6 +158,6 @@ curl -X POST https://api.trynawa.com/v1/detect \ |--------|------|------| | 400 | `invalid_request_error` | Missing `text`. Text too long. | | 401 | `authentication_error` | Invalid or missing API key | -| 402 | `insufficient_credits` | No credits remaining | +| 402 | `permission_error` | No credits remaining | | 429 | `rate_limit_error` | Rate limit exceeded | | 500 | `api_error` | Internal error | diff --git a/api-reference/feedback.mdx b/api-reference/feedback.mdx index 4cddf8b..769759d 100644 --- a/api-reference/feedback.mdx +++ b/api-reference/feedback.mdx @@ -1,14 +1,14 @@ --- -title: "Submit Feedback" +title: "Submit feedback" sidebarTitle: "POST /v1/feedback" -description: "Submit RLHF feedback to improve classification accuracy over time." +description: "Submit RLHF feedback on a classification result to improve NAWA's accuracy over time." api: "POST https://api.trynawa.com/v1/feedback" --- Submit reinforcement learning from human feedback (RLHF) to continuously improve NAWA's classification accuracy. - This endpoint is **free** -- 0 credits, no cost. + This endpoint is **free** - 0 credits, no cost. @@ -21,10 +21,11 @@ Submit reinforcement learning from human feedback (RLHF) to continuously improve | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `request_id` | string | Yes | The `request_id` from the original classification response. | -| `field` | string | Yes | The field to correct: `intent`, `sentiment`, `dialect`, `toxicity`, `category`. | -| `expected_value` | string | Yes | The correct value the model should have returned. | -| `comment` | string | No | Optional free-text explanation of why this correction is needed. | +| `classification_id` | string | Yes | The `id` (e.g. `cls_nw_xxx`) from the original classification response. | +| `rating` | string | Yes | Your assessment: `correct`, `incorrect`, or `partial`. | +| `corrected_intent` | string[] | No | Array of correct intents if the classification was wrong. | +| `corrected_sentiment` | string | No | Correct sentiment: `positive`, `negative`, `neutral`, `mixed`. | +| `comment` | string | No | Free-text explanation of why this correction is needed. | ### Example request @@ -35,31 +36,42 @@ curl -X POST https://api.trynawa.com/v1/feedback \ -H "Authorization: Bearer nawa_test_sk_xxx" \ -H "Content-Type: application/json" \ -d '{ - "request_id": "req_abc123def456", - "field": "dialect", - "expected_value": "levantine", - "comment": "This is Lebanese Arabic, not Gulf" + "classification_id": "cls_nw_a1b2c3d4e5f6", + "rating": "incorrect", + "corrected_intent": ["question"], + "corrected_sentiment": "neutral", + "comment": "This was a question, not a complaint" }' ``` -```typescript TypeScript -const { data, error } = await nawa.feedback.submit({ - requestId: 'req_abc123def456', - field: 'dialect', - expectedValue: 'levantine', - comment: 'This is Lebanese Arabic, not Gulf' -}) -``` - ```python Python +from nawa import Nawa + +nawa = Nawa(api_key="your_api_key") + result = nawa.feedback.submit( - request_id="req_abc123def456", - field="dialect", - expected_value="levantine", - comment="This is Lebanese Arabic, not Gulf" + classification_id="cls_nw_a1b2c3d4e5f6", + rating="incorrect", + corrected_intent=["question"], + corrected_sentiment="neutral", + comment="This was a question, not a complaint" ) ``` +```typescript TypeScript +import { Nawa } from '@nawalabs/sdk' + +const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY }) + +const { data, error } = await nawa.feedback.submit({ + classificationId: 'cls_nw_a1b2c3d4e5f6', + rating: 'incorrect', + correctedIntent: ['question'], + correctedSentiment: 'neutral', + comment: 'This was a question, not a complaint' +}) +``` + ## Response @@ -70,11 +82,32 @@ result = nawa.feedback.submit( { "success": true, "result": { - "feedback_id": "fb_xyz789", - "status": "accepted", - "message": "Thank you! Your feedback helps improve NAWA's accuracy." + "id": "fb_nw_a1b2c3d4e5f6", + "object": "feedback", + "classification_id": "cls_nw_a1b2c3d4e5f6", + "rating": "incorrect", + "acknowledged": true }, "errors": [], - "request_id": "req_fb_abc123" + "request_id": "req_nw_fb1a2b3c4d5e" } ``` + +### Result fields + +| Field | Type | Description | +|-------|------|-------------| +| `id` | string | Feedback ID in `fb_nw_xxx` format | +| `object` | string | Always `"feedback"` | +| `classification_id` | string | The classification this feedback applies to | +| `rating` | string | Your rating: `correct`, `incorrect`, or `partial` | +| `acknowledged` | boolean | Always `true` on success | + +### Error responses + +| Status | Type | When | +|--------|------|------| +| 400 | `invalid_request_error` | Missing `classification_id` or `rating`. Invalid `rating` value. | +| 401 | `authentication_error` | Invalid or missing API key | +| 429 | `rate_limit_error` | Rate limit exceeded | +| 500 | `api_error` | Internal error | diff --git a/api-reference/health.mdx b/api-reference/health.mdx index ca110e8..15f62f7 100644 --- a/api-reference/health.mdx +++ b/api-reference/health.mdx @@ -1,5 +1,5 @@ --- -title: "Health Check" +title: "Health check" sidebarTitle: "GET /v1/health" description: "Check the operational status of the NAWA API and its dependencies." api: "GET https://api.trynawa.com/v1/health" @@ -7,56 +7,99 @@ api: "GET https://api.trynawa.com/v1/health" Check the operational status of the NAWA API. This endpoint is **free** and does **not** require authentication. + + The health endpoint returns a flat JSON object, not the standard `{ success, result, errors, request_id }` envelope used by other endpoints. + + ## Request -```bash + + +```bash cURL curl https://api.trynawa.com/v1/health ``` +```python Python +import requests + +response = requests.get("https://api.trynawa.com/v1/health") +data = response.json() +print(data["status"]) +``` + +```typescript TypeScript +const response = await fetch('https://api.trynawa.com/v1/health') +const data = await response.json() +console.log(data.status) +``` + + + ## Response ### Healthy response (200) ```json { - "success": true, - "result": { - "status": "healthy", - "version": "1.0.0", - "services": { - "api": "operational", - "classification": "operational", - "database": "operational", - "cache": "operational" + "status": "healthy", + "version": "v1", + "services": { + "database": { + "status": "healthy", + "latency_ms": 12 }, - "timestamp": "2025-01-15T12:00:00Z" + "redis": { + "status": "healthy", + "latency_ms": 3 + }, + "nagl": { + "status": "healthy" + } }, - "errors": [], - "request_id": "req_hlt_abc123" + "timestamp": "2025-01-15T12:00:00Z", + "latency_ms": 15 } ``` -### Degraded response (200) +### Degraded response (503) ```json { - "success": true, - "result": { - "status": "degraded", - "version": "1.0.0", - "services": { - "api": "operational", - "classification": "degraded", - "database": "operational", - "cache": "operational" + "status": "degraded", + "version": "v1", + "services": { + "database": { + "status": "healthy", + "latency_ms": 12 + }, + "redis": { + "status": "down", + "latency_ms": 0 }, - "timestamp": "2025-01-15T12:00:00Z" + "nagl": { + "status": "healthy" + } }, - "errors": [], - "request_id": "req_hlt_def456" + "timestamp": "2025-01-15T12:00:00Z", + "latency_ms": 12 } ``` +### Response fields + +| Field | Type | Description | +|-------|------|-------------| +| `status` | string | `healthy` when all services are up, `degraded` when one or more services are down | +| `version` | string | API version (always `v1`) | +| `services` | object | Individual service statuses | +| `services.database` | object | Database health with `status` and `latency_ms` | +| `services.redis` | object | Redis cache health with `status` and `latency_ms` | +| `services.nagl` | object | NAGL classification engine health with `status` | +| `timestamp` | string | ISO 8601 timestamp of the check | +| `latency_ms` | integer | Total health check latency | + +Each service reports `status` as `healthy`, `degraded`, or `down`. + For real-time status monitoring, visit [status.trynawa.com](https://status.trynawa.com). diff --git a/api-reference/moderate.mdx b/api-reference/moderate.mdx index 947d30e..4fca7b3 100644 --- a/api-reference/moderate.mdx +++ b/api-reference/moderate.mdx @@ -1,11 +1,11 @@ --- -title: "Moderate Content" +title: "Moderate content" sidebarTitle: "POST /v1/moderate" description: "Dialect-aware Arabic content moderation. Catches profanity and slang that Western APIs miss." api: "POST https://api.trynawa.com/v1/moderate" --- -Dialect-aware Arabic content moderation powered by ALLaM. Understands profanity, slang, and offensive language differences across Gulf, Egyptian, Levantine, and MSA Arabic -- catching what Western moderation APIs miss entirely. +Dialect-aware Arabic content moderation powered by ALLaM. Understands profanity, slang, and offensive language differences across Gulf, Egyptian, Levantine, and MSA Arabic -- catching what Western moderation APIs miss entirely. Falls back to Claude Haiku 4.5 if ALLaM is unavailable. Cost: **$0.004** per request (4 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`). @@ -25,8 +25,8 @@ Dialect-aware Arabic content moderation powered by ALLaM. Understands profanity, | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `text` | string | Yes | The text to moderate. Max 5,000 characters. | -| `context` | string | No | Where the text appears (e.g. "youtube comment", "product review"). Helps the model calibrate. | | `strictness` | string | No | Moderation strictness: `low`, `medium`, `high`. Default: `medium`. | +| `context` | string | No | Where the text appears (e.g. "youtube comment", "product review"). Helps the model calibrate. | | `platform` | string | No | Source platform: `youtube`, `instagram`, `twitter`, `facebook`. | **Strictness levels:** @@ -37,6 +37,7 @@ Dialect-aware Arabic content moderation powered by ALLaM. Understands profanity, ### Example request + ```bash cURL curl -X POST https://api.trynawa.com/v1/moderate \ -H "Authorization: Bearer nawa_test_sk_xxx" \ @@ -46,6 +47,24 @@ curl -X POST https://api.trynawa.com/v1/moderate \ "strictness": "medium" }' ``` + +```python Python +from nawa import Nawa + +nawa = Nawa(api_key="your_api_key") + +result = nawa.moderate( + text="هذا محتوى رائع جدا", + strictness="medium" +) + +if result.data.is_safe: + print("Content is safe") +else: + print(f"Flagged: {result.data.flags}") + print(f"Cleaned: {result.data.cleaned_text}") +``` + ```typescript TypeScript import { Nawa } from '@nawalabs/sdk' @@ -63,27 +82,13 @@ if (data.is_safe) { console.log('Cleaned:', data.cleaned_text) } ``` -```python Python -from nawa import Nawa - -nawa = Nawa(api_key="your_api_key") - -result = nawa.moderate( - text="هذا محتوى رائع جدا", - strictness="medium" -) -if result.data.is_safe: - print("Content is safe") -else: - print(f"Flagged: {result.data.flags}") - print(f"Cleaned: {result.data.cleaned_text}") -``` ## Response ### Safe content (200) + ```json { "success": true, @@ -126,7 +131,7 @@ else: "credits_used": 4 }, "errors": [], - "request_id": "req_nw_4c11w71e44cb" + "request_id": "req_nw_mod1a2b3c4d5e" } ``` @@ -147,7 +152,7 @@ When content is flagged, the response includes specific flags, severity scores, "severity": "high", "severity_score": 0.87, "language": "ar", - "dialect": "egyptian", + "dialect": "gulf", "categories": { "profanity": true, "hate_speech": false, @@ -184,89 +189,45 @@ When content is flagged, the response includes specific flags, severity scores, | Field | Type | Description | |-------|------|-------------| +| `id` | string | Moderation ID in `mod_nw_xxx` format | +| `object` | string | Always `"moderation"` | | `text` | string | The original input text | | `is_safe` | boolean | Whether the content passed moderation | | `verdict` | string | `safe` or `flagged` | -| `flags` | array | List of triggered category names | +| `flags` | string[] | List of triggered category names | | `severity` | string | Overall severity: `none`, `low`, `medium`, `high` | -| `severity_score` | number | Severity score 0--1 | +| `severity_score` | number | Severity score 0-1 | | `language` | string | Detected language: `ar`, `en`, or `mixed` | | `dialect` | string or null | Detected Arabic dialect. Null if not Arabic. | | `categories` | object | Boolean flags for each moderation category | -| `category_scores` | object | Confidence scores 0--1 for each category | -| `flagged_terms` | array | Specific problematic words/phrases found | -| `cleaned_text` | string or null | Text with flagged terms replaced by `***`. Null if safe. | -| `model` | string | Model version used | +| `category_scores` | object | Confidence scores (0-1) for each category | +| `flagged_terms` | string[] | Specific problematic words/phrases found | +| `cleaned_text` | string or null | Text with flagged terms replaced by `***`. Null if content is safe. | +| `model` | string | Model version (always `nagl-v1`) | | `provider` | string | AI provider used (`allam` or `claude`) | | `fallback_used` | boolean | Whether the fallback provider was used | | `cached` | boolean | Whether served from semantic cache | | `cost_usd` | number | Cost in USD | -| `credits_used` | number | Credits deducted | +| `credits_used` | integer | Credits deducted (4 per request) | ### Moderation categories | Category | Description | |----------|-------------| | `profanity` | Swearing, vulgar language, dialect-specific profanity | -| `hate_speech` | Content targeting groups based on race, religion, ethnicity, etc. | +| `hate_speech` | Content targeting groups based on race, religion, ethnicity | | `harassment` | Personal attacks, bullying, intimidation | | `sexual` | Sexually explicit or suggestive content | | `spam` | Promotional content, repetitive text, link spam | | `self_harm` | Content promoting or describing self-harm | | `violence` | Threats, glorification of violence, graphic descriptions | -### Examples - - - - **Input:** "هذا محتوى رائع جدا" - **Result:** is_safe: `true`, verdict: `safe`, severity: `none` -```bash -curl -X POST https://api.trynawa.com/v1/moderate \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ - -H "Content-Type: application/json" \ - -d '{"text": "هذا محتوى رائع جدا", "strictness": "medium"}' -``` - - - - **Input:** "اشتركوا في قناتي الرابط في البايو!! فولو فولو فولو!!" - **Result:** is_safe: `false`, flags: `["spam"]`, severity: `low` -```bash -curl -X POST https://api.trynawa.com/v1/moderate \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ - -H "Content-Type: application/json" \ - -d '{"text": "اشتركوا في قناتي الرابط في البايو!! فولو فولو فولو!!", "strictness": "medium"}' -``` - - - - **Input:** Borderline content that passes on medium but flags on high strictness -```bash -curl -X POST https://api.trynawa.com/v1/moderate \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ - -H "Content-Type: application/json" \ - -d '{"text": "your text here", "strictness": "high"}' -``` - - - - **Input:** Moderate a YouTube comment with platform context -```bash -curl -X POST https://api.trynawa.com/v1/moderate \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ - -H "Content-Type: application/json" \ - -d '{"text": "your text here", "strictness": "medium", "platform": "youtube", "context": "youtube comment"}' -``` - - - ### Error responses | Status | Type | When | |--------|------|------| | 400 | `invalid_request_error` | Missing `text`. Invalid strictness or platform. Text too long. | | 401 | `authentication_error` | Invalid or missing API key | -| 402 | `insufficient_credits` | No credits remaining | +| 402 | `permission_error` | No credits remaining | | 429 | `rate_limit_error` | Rate limit exceeded | | 500 | `api_error` | Internal or provider error | diff --git a/api-reference/rubric-classify.mdx b/api-reference/rubric-classify.mdx index 4489175..2b5bde9 100644 --- a/api-reference/rubric-classify.mdx +++ b/api-reference/rubric-classify.mdx @@ -1,83 +1,97 @@ --- -title: "Rubric Classify" -sidebarTitle: "POST /v1/rubric/classify" -description: "Classify a comment against a custom rubric with predefined scoring criteria." -api: "POST https://api.trynawa.com/v1/rubric/classify" +title: "Rubric classify" +sidebarTitle: "POST /v1/rubric-classify" +description: "Classify text against a custom rubric with user-defined categories and configurable confidence thresholds." +api: "POST https://api.trynawa.com/v1/rubric-classify" --- -Classify a comment against a custom rubric. Define your own categories and scoring criteria for domain-specific classification. +Classify text against a custom rubric you define. Bring your own categories and optional descriptions for domain-specific classification. Supports multi-label mode and configurable confidence thresholds. - Cost: **$0.003** per request (3 credits). **500 free requests/month** on this endpoint -- no credit card required. Semantic cache hits are free (`X-NAWA-Cache: HIT`). + Cost: **$0.003** per request (3 credits). This endpoint does not use semantic caching. ## Request +### Query parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `provider` | string | No | Force a specific AI provider: `claude`, `gemini`, or `allam`. | + ### Body parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `text` | string | Yes | The comment text to classify. Max 5,000 characters. | -| `rubric` | object | Yes | The rubric definition with categories and criteria. | -| `rubric.categories` | string[] | Yes | List of category names to classify against. | -| `rubric.descriptions` | object | No | Category descriptions to guide the model. Keys are category names, values are description strings. | -| `platform` | string | No | Source platform for context. | +| `text` | string | Yes | The text to classify. | +| `rubric` | object | Yes | The rubric definition. | +| `rubric.categories` | array | Yes | Array of category objects. Each must have a `name` (string). Optional `description` (string) guides the model. Min 1, max 20. | +| `rubric.multi_label` | boolean | No | If `true`, returns all matching categories above the threshold. Default: `false`. | +| `rubric.confidence_threshold` | number | No | Minimum confidence to include a category. Range 0-1. Default: `0.5`. | ### Example request ```bash cURL -curl -X POST https://api.trynawa.com/v1/rubric/classify \ +curl -X POST https://api.trynawa.com/v1/rubric-classify \ -H "Authorization: Bearer nawa_test_sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "text": "وين الترجمة العربية؟ مافهمت شي", "rubric": { - "categories": ["translation_request", "technical_issue", "content_feedback", "off_topic"], - "descriptions": { - "translation_request": "User is asking for subtitles or translation", - "technical_issue": "Audio, video, or playback problems", - "content_feedback": "Comments about the content quality", - "off_topic": "Not related to the video" - } - }, - "platform": "youtube" - }' -``` - -```typescript TypeScript -const { data, error } = await nawa.rubric.classify({ - text: 'وين الترجمة العربية؟ مافهمت شي', - rubric: { - categories: ['translation_request', 'technical_issue', 'content_feedback', 'off_topic'], - descriptions: { - translation_request: 'User is asking for subtitles or translation', - technical_issue: 'Audio, video, or playback problems', - content_feedback: 'Comments about the content quality', - off_topic: 'Not related to the video' + "categories": [ + { "name": "translation_request", "description": "User is asking for subtitles or translation" }, + { "name": "technical_issue", "description": "Audio, video, or playback problems" }, + { "name": "content_feedback" }, + { "name": "off_topic" } + ], + "multi_label": false, + "confidence_threshold": 0.5 } - }, - platform: 'youtube' -}) + }' ``` ```python Python +from nawa import Nawa + +nawa = Nawa(api_key="your_api_key") + result = nawa.rubric.classify( text="وين الترجمة العربية؟ مافهمت شي", rubric={ - "categories": ["translation_request", "technical_issue", "content_feedback", "off_topic"], - "descriptions": { - "translation_request": "User is asking for subtitles or translation", - "technical_issue": "Audio, video, or playback problems", - "content_feedback": "Comments about the content quality", - "off_topic": "Not related to the video", - }, - }, - platform="youtube", + "categories": [ + {"name": "translation_request", "description": "User is asking for subtitles or translation"}, + {"name": "technical_issue", "description": "Audio, video, or playback problems"}, + {"name": "content_feedback"}, + {"name": "off_topic"} + ], + "multi_label": False, + "confidence_threshold": 0.5 + } ) ``` +```typescript TypeScript +import { Nawa } from '@nawalabs/sdk' + +const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY }) + +const { data, error } = await nawa.rubric.classify({ + text: 'وين الترجمة العربية؟ مافهمت شي', + rubric: { + categories: [ + { name: 'translation_request', description: 'User is asking for subtitles or translation' }, + { name: 'technical_issue', description: 'Audio, video, or playback problems' }, + { name: 'content_feedback' }, + { name: 'off_topic' } + ], + multiLabel: false, + confidenceThreshold: 0.5 + } +}) +``` + ## Response @@ -88,23 +102,22 @@ result = nawa.rubric.classify( { "success": true, "result": { - "text": "وين الترجمة العربية؟ مافهمت شي", - "category": "translation_request", - "category_confidence": 0.94, - "scores": { - "translation_request": 0.94, - "technical_issue": 0.03, - "content_feedback": 0.02, - "off_topic": 0.01 - }, - "dialect": "gulf", - "dialect_confidence": 0.91, + "id": "rcl_nw_a1b2c3d4e5f6", + "object": "rubric_classification", + "categories": [ + { "name": "translation_request", "confidence": 0.94 }, + { "name": "technical_issue", "confidence": 0.03 } + ], + "multi_label": false, "language": "ar", - "model": "nagl-v1", - "cached": false + "provider": "allam", + "model": "sdaia/allam-1-13b-instruct", + "fallback_used": false, + "cost_usd": 0.003, + "credits_used": 3 }, "errors": [], - "request_id": "req_rub123abc456" + "request_id": "req_nw_rub123abc456" } ``` @@ -112,10 +125,23 @@ result = nawa.rubric.classify( | Field | Type | Description | |-------|------|-------------| -| `category` | string | The top matching category from your rubric | -| `category_confidence` | number | Confidence score (0–1) for the top category | -| `scores` | object | Confidence scores for all rubric categories | -| `dialect` | string | Detected Arabic dialect | -| `dialect_confidence` | number | Dialect confidence score (0–1) | -| `language` | string | Detected language code | -| `cached` | boolean | Whether served from semantic cache | +| `id` | string | Classification ID in `rcl_nw_xxx` format | +| `object` | string | Always `"rubric_classification"` | +| `categories` | array | Array of `{ name, confidence }` objects. Filtered by `confidence_threshold`. Sorted by confidence descending. | +| `multi_label` | boolean | Whether multi-label mode was used | +| `language` | string | Detected language: `ar`, `en`, or `mixed` | +| `provider` | string | AI provider used | +| `model` | string | Model identifier | +| `fallback_used` | boolean | Whether a fallback provider was used | +| `cost_usd` | number | Cost in USD | +| `credits_used` | integer | Credits deducted (3 per request) | + +### Error responses + +| Status | Type | When | +|--------|------|------| +| 400 | `invalid_request_error` | Missing `text` or `rubric`. Empty or oversized `categories` array. Category missing `name`. | +| 401 | `authentication_error` | Invalid or missing API key | +| 402 | `permission_error` | No credits remaining | +| 429 | `rate_limit_error` | Rate limit exceeded | +| 500 | `api_error` | Internal or provider error | diff --git a/api-reference/translate.mdx b/api-reference/translate.mdx index c10d2ba..b90e72b 100644 --- a/api-reference/translate.mdx +++ b/api-reference/translate.mdx @@ -5,7 +5,7 @@ description: "Dialect-aware Arabic translation powered by HUMAIN's ALLaM model." api: "POST https://api.trynawa.com/v1/translate" --- -Translate text between English and Arabic with dialect awareness. Generates culturally appropriate translations in Gulf, Egyptian, Levantine, or MSA Arabic. +Translate text between English and Arabic with dialect awareness. Generates culturally appropriate translations in Gulf, Egyptian, Levantine, or MSA Arabic. Falls back to Claude Haiku 4.5 if ALLaM is unavailable. Cost: **$0.005** per request (5 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`). @@ -25,59 +25,65 @@ Translate text between English and Arabic with dialect awareness. Generates cult | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `text` | string | Yes | The text to translate. Max 5,000 characters. | -| `source` | string | No | Source language: `ar`, `en`, `auto`. Default: `auto` (detects automatically). | -| `target` | string | Yes | Target language: `ar`, `en`. | +| `source` | string | No | Source language: `ar`, `en`, `auto`. Default: `auto`. | +| `target` | string | Yes | Target language: `ar` or `en`. Must differ from `source`. | | `dialect` | string | No | Target dialect for Arabic output: `gulf`, `egyptian`, `levantine`, `msa`. Default: `msa`. | | `tone` | string | No | Translation tone: `formal`, `casual`, `social_media`. Default: `formal`. | -| `platform` | string | No | Source platform for social media tone context: `youtube`, `instagram`, `twitter`, `facebook`. | -| `metadata` | object | No | Arbitrary key-value metadata to attach to the translation. | +| `platform` | string | No | Source platform: `youtube`, `instagram`, `twitter`, `facebook` (validated but reserved for future use). | ### Example request + ```bash cURL - curl -X POST https://api.trynawa.com/v1/translate \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ - -H "Content-Type: application/json" \ - -d '{ - "text": "Hello, how are you?", - "source": "en", - "target": "ar", - "dialect": "gulf", - "tone": "casual" - }' +curl -X POST https://api.trynawa.com/v1/translate \ + -H "Authorization: Bearer nawa_test_sk_xxx" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "Hello, how are you?", + "source": "en", + "target": "ar", + "dialect": "gulf", + "tone": "casual" + }' ``` -```typescript TypeScript - import { Nawa } from '@nawalabs/sdk' - const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY }) - - const { data, error } = await nawa.translate({ - text: 'Hello, how are you?', - source: 'en', - target: 'ar', - dialect: 'gulf', - tone: 'casual' - }) -``` ```python Python - from nawa import Nawa - - nawa = Nawa(api_key="your_api_key") +from nawa import Nawa + +nawa = Nawa(api_key="your_api_key") + +result = nawa.translate( + text="Hello, how are you?", + source="en", + target="ar", + dialect="gulf", + tone="casual" +) +print(result.data.translated_text) +``` - result = nawa.translate( - text="Hello, how are you?", - source="en", - target="ar", - dialect="gulf", - tone="casual" - ) +```typescript TypeScript +import { Nawa } from '@nawalabs/sdk' + +const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY }) + +const { data, error } = await nawa.translate({ + text: 'Hello, how are you?', + source: 'en', + target: 'ar', + dialect: 'gulf', + tone: 'casual' +}) +console.log(data.translatedText) ``` + ## Response ### Success response (200) + ```json { "success": true, @@ -91,9 +97,10 @@ Translate text between English and Arabic with dialect awareness. Generates cult "source_dialect": null, "target_dialect": "gulf", "tone": "casual", - "model": "nagl-v1", "provider": "allam", + "model": "sdaia/allam-1-13b-instruct", "fallback_used": false, + "tokens_used": null, "cached": false, "cost_usd": 0.005, "credits_used": 5 @@ -107,19 +114,22 @@ Translate text between English and Arabic with dialect awareness. Generates cult | Field | Type | Description | |-------|------|-------------| -| `text` | string | The original input text | -| `translated_text` | string | The translated output | -| `source_language` | string | Detected or specified source language | -| `target_language` | string | Target language | -| `source_dialect` | string or null | Detected dialect if source is Arabic | -| `target_dialect` | string or null | Target dialect if target is Arabic | -| `tone` | string | The tone used for translation | -| `model` | string | Model version used | +| `id` | string | Translation ID in `trn_nw_xxx` format | +| `object` | string | Always `"translation"` | +| `text` | string | Original input text | +| `translated_text` | string | Translated output | +| `source_language` | string | Detected or specified source language: `ar` or `en` | +| `target_language` | string | Target language: `ar` or `en` | +| `source_dialect` | string or null | Detected dialect if source is Arabic. Null otherwise. | +| `target_dialect` | string or null | Target dialect if target is Arabic. Null otherwise. | +| `tone` | string | Tone used: `formal`, `casual`, or `social_media` | | `provider` | string | AI provider used (`allam` or `claude`) | +| `model` | string | Model identifier | | `fallback_used` | boolean | Whether the fallback provider was used | +| `tokens_used` | integer or null | Always `null` in the current version | | `cached` | boolean | Whether served from semantic cache | | `cost_usd` | number | Cost in USD | -| `credits_used` | number | Credits deducted | +| `credits_used` | integer | Credits deducted (5 per request) | ### Dialect examples @@ -142,7 +152,7 @@ Translate text between English and Arabic with dialect awareness. Generates cult curl -X POST https://api.trynawa.com/v1/translate \ -H "Authorization: Bearer nawa_test_sk_xxx" \ -H "Content-Type: application/json" \ - -d '{"text": "Subscribe to my channel for more content!", "source": "en", "target": "ar", "dialect": "egyptian", "tone": "social_media", "platform": "youtube"}' + -d '{"text": "Subscribe to my channel for more content!", "source": "en", "target": "ar", "dialect": "egyptian", "tone": "social_media"}' ``` @@ -154,17 +164,6 @@ Translate text between English and Arabic with dialect awareness. Generates cult -H "Authorization: Bearer nawa_test_sk_xxx" \ -H "Content-Type: application/json" \ -d '{"text": "وش رايكم بالمحتوى الجديد؟", "source": "ar", "target": "en", "tone": "formal"}' -``` - - - - **Input:** "كيف حالك اليوم؟" (MSA) - **Output:** "شلونك اليوم؟" (Gulf) -```bash - curl -X POST https://api.trynawa.com/v1/translate \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ - -H "Content-Type: application/json" \ - -d '{"text": "كيف حالك اليوم؟", "source": "ar", "target": "ar", "dialect": "gulf", "tone": "casual"}' ``` @@ -173,8 +172,8 @@ Translate text between English and Arabic with dialect awareness. Generates cult | Status | Type | When | |--------|------|------| -| 400 | `invalid_request_error` | Missing `text`, `source`, or `target`. Invalid values. Text too long. | +| 400 | `invalid_request_error` | Missing `text` or `target`. Source equals target. Text too long. Invalid enum value. | | 401 | `authentication_error` | Invalid or missing API key | -| 402 | `insufficient_credits` | No credits remaining | +| 402 | `permission_error` | No credits remaining | | 429 | `rate_limit_error` | Rate limit exceeded | | 500 | `api_error` | Internal or provider error | diff --git a/api-reference/usage.mdx b/api-reference/usage.mdx index d4b1b6b..a8a6cff 100644 --- a/api-reference/usage.mdx +++ b/api-reference/usage.mdx @@ -1,7 +1,7 @@ --- title: "Usage" sidebarTitle: "GET /v1/usage" -description: "Retrieve API usage statistics, credit consumption, and request counts." +description: "Retrieve API usage statistics, credit consumption, and cache hit rates." api: "GET https://api.trynawa.com/v1/usage" --- @@ -13,17 +13,40 @@ Retrieve your API usage statistics including request counts, credit consumption, | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `from` | string | No | Start date (ISO 8601). Default: start of current month. | -| `to` | string | No | End date (ISO 8601). Default: now. | -| `group_by` | string | No | Group by: `day`, `week`, `month`, `endpoint`. Default: `day`. | +| `from` | string | No | Start date (ISO 8601). | +| `to` | string | No | End date (ISO 8601). | +| `group_by` | string | No | Group by: `endpoint` or `day`. Default: `endpoint`. | ### Example request -```bash + + +```bash cURL curl "https://api.trynawa.com/v1/usage?from=2025-01-01T00:00:00Z&group_by=endpoint" \ -H "Authorization: Bearer nawa_test_sk_xxx" ``` +```python Python +import requests + +response = requests.get( + "https://api.trynawa.com/v1/usage", + params={"from": "2025-01-01T00:00:00Z", "group_by": "endpoint"}, + headers={"Authorization": "Bearer nawa_test_sk_xxx"} +) +data = response.json() +``` + +```typescript TypeScript +const response = await fetch( + 'https://api.trynawa.com/v1/usage?from=2025-01-01T00:00:00Z&group_by=endpoint', + { headers: { Authorization: 'Bearer nawa_test_sk_xxx' } } +) +const data = await response.json() +``` + + + ## Response ### Success response (200) @@ -32,53 +55,53 @@ curl "https://api.trynawa.com/v1/usage?from=2025-01-01T00:00:00Z&group_by=endpoi { "success": true, "result": { - "period": { - "from": "2025-01-01T00:00:00Z", - "to": "2025-01-31T23:59:59Z" + "usage": { + "total_requests": 12450, + "total_cost": 68.70, + "cache_hits": 2231, + "cache_hit_rate": 0.18, + "period": { + "from": "2025-01-01T00:00:00Z", + "to": "2025-01-31T23:59:59Z" + } }, - "total_requests": 12450, - "total_cost": 68.70, - "cache_hit_rate": 0.23, "by_endpoint": [ { "endpoint": "/v1/classify", "requests": 8500, "cost": 51.00, - "cache_hits": 1955, - "avg_latency_ms": 820 - }, - { - "endpoint": "/v1/rubric/classify", - "requests": 2200, - "cost": 6.60, - "cache_hits": 880, - "avg_latency_ms": 650 + "cache_hits": 1955 }, { - "endpoint": "/v1/comments/:id/reply", - "requests": 1750, - "cost": 14.00, - "cache_hits": 0, - "avg_latency_ms": 1200 + "endpoint": "/v1/translate", + "requests": 1200, + "cost": 6.00, + "cache_hits": 276 } - ], - "balance": { - "current": 31.30, - "credits_purchased": 100.00, - "credits_used": 68.70 - } + ] }, "errors": [], - "request_id": "req_usg_abc123" + "request_id": "req_nw_usg1a2b3c4d5e" } ``` -### Result fields +### Usage summary fields | Field | Type | Description | |-------|------|-------------| | `total_requests` | integer | Total API requests in the period | -| `total_cost` | number | Total credit cost in USD | -| `cache_hit_rate` | number | Percentage of requests served from cache (0–1) | -| `by_endpoint` | array | Breakdown by endpoint | -| `balance` | object | Current balance and credit history | +| `total_cost` | number | Total cost in USD | +| `cache_hits` | integer | Number of requests served from cache | +| `cache_hit_rate` | number | Cache hit rate as a decimal (0-1) | +| `period` | object | The `from` and `to` dates (null if not specified) | + +### Breakdown items + +The breakdown key changes based on `group_by`: + +| `group_by` | Key | Breakdown fields | +|------------|-----|-----------------| +| `endpoint` | `by_endpoint` | `endpoint`, `requests`, `cost`, `cache_hits` | +| `day` | `by_day` | `date` (YYYY-MM-DD), `requests`, `cost`, `cache_hits` | + +Breakdowns are sorted by `requests` descending. diff --git a/openapi.yaml b/openapi.yaml index 17706f2..8c59fa5 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1,10 +1,10 @@ openapi: 3.1.0 info: title: NAWA API - version: 1.1.1 + version: 1.2.0 description: | Arabic language intelligence API. - تصنيف ذكي للمحتوى العربي - من الخليج إلى المغرب. + تصنيف ذكي للمحتوى العربي - من الخليج إلى المغرب. NAWA classifies Arabic and English text with dialect detection, sentiment analysis, intent classification, translation, content moderation, and automated reply generation. @@ -53,12 +53,20 @@ paths: operationId: classify summary: Classify text description: | - Classify Arabic or English text. Returns intent, sentiment, dialect, toxicity, - and categories in a single API call. Supports semantic caching. + Classify Arabic or English text. Returns intent, sentiment, dialect, priority, + and a suggested reply direction. Supports semantic caching. tags: [Classification] x-nawa-cost: "$0.006" x-nawa-cache: true x-nawa-free: false + parameters: + - name: provider + in: query + required: false + description: Force a specific AI provider (for A/B testing) + schema: + type: string + enum: [claude, gemini, allam] requestBody: required: true content: @@ -70,25 +78,15 @@ paths: text: type: string minLength: 1 - maxLength: 5000 - description: The comment text to classify + description: The text to classify examples: - "متى الجزء الثاني؟" - - "ما شاء الله عليك، محتوى رهيب!" - platform: - type: string - enum: [youtube, instagram, twitter, facebook] - description: Source platform for context-aware classification - channel_id: - type: string - description: Your channel or account identifier for analytics grouping - metadata: + - "Great video, love the content!" + context: type: object - description: Arbitrary key-value metadata to attach to the classification + description: Optional context for classification (reserved for future use) example: text: "متى الجزء الثاني؟" - platform: "youtube" - channel_id: "ch_123" responses: "200": description: Classification result @@ -99,8 +97,6 @@ paths: $ref: "#/components/headers/X-NAWA-Provider" X-NAWA-Cache: $ref: "#/components/headers/X-NAWA-Cache" - X-NAWA-Balance: - $ref: "#/components/headers/X-NAWA-Balance" X-RateLimit-Limit: $ref: "#/components/headers/X-RateLimit-Limit" X-RateLimit-Remaining: @@ -112,26 +108,31 @@ paths: schema: $ref: "#/components/schemas/ClassifySuccessResponse" examples: - gulf_question: + arabic_gulf: summary: Gulf Arabic question value: success: true result: - text: "متى الجزء الثاني؟" - intent: question - intent_confidence: 0.97 + id: cls_nw_a1b2c3d4e5f6 + object: classification + intent: [question] sentiment: neutral - sentiment_confidence: 0.91 - dialect: gulf - dialect_confidence: 0.95 - toxicity: none - toxicity_confidence: 0.99 - categories: ["engagement"] language: ar - model: nagl-v1 - cached: false + dialect: gulf + dialect_confidence: 0.92 + requires_response: true + priority: medium + suggested_reply: + text: "" + direction: rtl + provider: allam + model: sdaia/allam-1-13b-instruct + fallback_used: false + tokens_used: null + cost_usd: 0.006 + credits_used: 6 errors: [] - request_id: req_nw_a1b2c3d4e5f67890 + request_id: req_nw_a1b2c3d4e5f67890abcdef12 "400": $ref: "#/components/responses/BadRequest" "401": @@ -193,10 +194,7 @@ paths: platform: type: string enum: [youtube, instagram, twitter, facebook] - description: Source platform for social media tone context - metadata: - type: object - description: Arbitrary key-value metadata + description: Source platform (validated but reserved for future use) example: text: "Hello, how are you?" source: "en" @@ -213,8 +211,6 @@ paths: $ref: "#/components/headers/X-NAWA-Provider" X-NAWA-Cache: $ref: "#/components/headers/X-NAWA-Cache" - X-NAWA-Balance: - $ref: "#/components/headers/X-NAWA-Balance" X-RateLimit-Limit: $ref: "#/components/headers/X-RateLimit-Limit" X-RateLimit-Remaining: @@ -243,6 +239,7 @@ paths: provider: allam model: sdaia/allam-1-13b-instruct fallback_used: false + tokens_used: null cached: false cost_usd: 0.005 credits_used: 5 @@ -296,8 +293,6 @@ paths: $ref: "#/components/headers/X-Request-Id" X-NAWA-Cache: $ref: "#/components/headers/X-NAWA-Cache" - X-NAWA-Balance: - $ref: "#/components/headers/X-NAWA-Balance" X-RateLimit-Limit: $ref: "#/components/headers/X-RateLimit-Limit" X-RateLimit-Remaining: @@ -392,8 +387,6 @@ paths: $ref: "#/components/headers/X-NAWA-Provider" X-NAWA-Cache: $ref: "#/components/headers/X-NAWA-Cache" - X-NAWA-Balance: - $ref: "#/components/headers/X-NAWA-Balance" X-RateLimit-Limit: $ref: "#/components/headers/X-RateLimit-Limit" X-RateLimit-Remaining: @@ -457,17 +450,24 @@ paths: "500": $ref: "#/components/responses/InternalError" - /rubric/classify: + /rubric-classify: post: operationId: rubricClassify summary: Classify with custom rubric description: | - Classify text against a user-defined rubric. Define your own categories and - scoring criteria for domain-specific classification. + Classify text against a user-defined rubric. Supports multi-label classification + and configurable confidence thresholds. tags: [Classification] x-nawa-cost: "$0.003" - x-nawa-cache: true + x-nawa-cache: false x-nawa-free: false + parameters: + - name: provider + in: query + required: false + schema: + type: string + enum: [claude, gemini, allam] requestBody: required: true content: @@ -479,40 +479,23 @@ paths: text: type: string minLength: 1 - maxLength: 5000 - description: The comment text to classify + description: Text to classify examples: - "وين الترجمة العربية؟ مافهمت شي" rubric: - type: object - required: [categories] - properties: - categories: - type: array - minItems: 1 - maxItems: 20 - items: - type: string - description: List of category names to classify against - descriptions: - type: object - additionalProperties: - type: string - description: Category descriptions to guide the model - platform: - type: string - enum: [youtube, instagram, twitter, facebook] - description: Source platform for context + $ref: "#/components/schemas/Rubric" example: text: "وين الترجمة العربية؟ مافهمت شي" rubric: - categories: ["translation_request", "technical_issue", "content_feedback", "off_topic"] - descriptions: - translation_request: "User is asking for subtitles or translation" - technical_issue: "Audio, video, or playback problems" - content_feedback: "Comments about the content quality" - off_topic: "Not related to the video" - platform: "youtube" + categories: + - name: "translation_request" + description: "User is asking for subtitles or translation" + - name: "technical_issue" + description: "Audio, video, or playback problems" + - name: "content_feedback" + - name: "off_topic" + multi_label: false + confidence_threshold: 0.5 responses: "200": description: Rubric classification result @@ -521,10 +504,6 @@ paths: $ref: "#/components/headers/X-Request-Id" X-NAWA-Provider: $ref: "#/components/headers/X-NAWA-Provider" - X-NAWA-Cache: - $ref: "#/components/headers/X-NAWA-Cache" - X-NAWA-Balance: - $ref: "#/components/headers/X-NAWA-Balance" X-RateLimit-Limit: $ref: "#/components/headers/X-RateLimit-Limit" X-RateLimit-Remaining: @@ -537,25 +516,26 @@ paths: $ref: "#/components/schemas/RubricSuccessResponse" examples: translation_request: - summary: Gulf Arabic translation request + summary: Gulf Arabic classified against rubric value: success: true result: - text: "وين الترجمة العربية؟ مافهمت شي" - category: translation_request - category_confidence: 0.94 - scores: - translation_request: 0.94 - technical_issue: 0.03 - content_feedback: 0.02 - off_topic: 0.01 - dialect: gulf - dialect_confidence: 0.91 + id: rcl_nw_a1b2c3d4e5f6 + object: rubric_classification + categories: + - name: translation_request + confidence: 0.94 + - name: technical_issue + confidence: 0.03 + multi_label: false language: ar - model: nagl-v1 - cached: false + provider: allam + model: sdaia/allam-1-13b-instruct + fallback_used: false + cost_usd: 0.003 + credits_used: 3 errors: [] - request_id: req_rub123abc456 + request_id: req_nw_rub123abc456 "400": $ref: "#/components/responses/BadRequest" "401": @@ -567,67 +547,59 @@ paths: "500": $ref: "#/components/responses/InternalError" - /comments/{id}/reply: + /comments/reply: post: - operationId: generateReply - summary: Generate reply to comment + operationId: commentsReply + summary: Classify and generate reply description: | - Generate a culturally-aware, dialect-matched reply to an Arabic comment. - Replies match the commenter's dialect and are contextually appropriate. + Classify a comment and generate a contextual reply in a single call. + Supports tone control and max length configuration. tags: [Reply] x-nawa-cost: "$0.008" - x-nawa-cache: true + x-nawa-cache: false x-nawa-free: false parameters: - - name: id - in: path - required: true - description: The comment ID to reply to + - name: provider + in: query + required: false schema: type: string - examples: - - "cmt_abc123" + enum: [claude, gemini, allam] requestBody: - required: false + required: true content: application/json: schema: type: object + required: [text] properties: + text: + type: string + minLength: 1 + description: Comment text to classify and reply to + examples: + - "هذا المنتج سيء جداً ولا أنصح به" tone: type: string enum: [friendly, professional, casual, formal] - default: friendly - description: Reply tone + default: professional max_length: type: integer minimum: 1 maximum: 2000 default: 500 - description: Maximum reply length in characters - context: - type: string - description: Additional context about the channel or video - language: - type: string - enum: [ar, en, auto] - default: auto - description: "Force reply language. Default: auto (matches commenter's language)." example: - tone: "friendly" - context: "Tech review channel focused on smartphones" + text: "هذا المنتج سيء جداً ولا أنصح به" + tone: "professional" + max_length: 500 responses: "200": - description: Generated reply + description: Classification and reply result headers: X-Request-Id: $ref: "#/components/headers/X-Request-Id" X-NAWA-Provider: $ref: "#/components/headers/X-NAWA-Provider" - X-NAWA-Cache: - $ref: "#/components/headers/X-NAWA-Cache" - X-NAWA-Balance: - $ref: "#/components/headers/X-NAWA-Balance" X-RateLimit-Limit: $ref: "#/components/headers/X-RateLimit-Limit" X-RateLimit-Remaining: @@ -639,19 +611,28 @@ paths: schema: $ref: "#/components/schemas/ReplySuccessResponse" examples: - gulf_reply: - summary: Friendly reply to Gulf Arabic question + arabic_reply: + summary: Reply to Arabic complaint value: success: true result: - comment_id: cmt_abc123 - reply_text: "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔" - reply_dialect: gulf - tone: friendly - original_intent: question - original_dialect: gulf + id: rpl_nw_a1b2c3d4e5f6 + object: comment_reply + classification: + intent: [complaint] + sentiment: negative + priority: high + requires_response: true + reply: + text: "نعتذر عن التجربة السيئة. نتمنى تتواصل معنا عشان نقدر نساعدك." + direction: rtl + tone: professional + provider: allam + model: sdaia/allam-1-13b-instruct + cost_usd: 0.008 + credits_used: 8 errors: [] - request_id: req_rep789xyz012 + request_id: req_nw_rpl1a2b3c4d5e "400": $ref: "#/components/responses/BadRequest" "401": @@ -668,8 +649,8 @@ paths: operationId: submitFeedback summary: Submit classification feedback description: | - Submit feedback on a classification result to improve NAWA's accuracy. - Free for all tiers. Used for RLHF-based model improvement. + Submit feedback on a classification result. Free for all tiers. + Used for RLHF-based model improvement. tags: [Feedback] x-nawa-cost: "Free" x-nawa-cache: false @@ -681,13 +662,14 @@ paths: schema: $ref: "#/components/schemas/FeedbackInput" example: - request_id: "req_abc123def456" - field: "dialect" - expected_value: "levantine" - comment: "This is Lebanese Arabic, not Gulf" + classification_id: "cls_nw_a1b2c3d4e5f6" + rating: "incorrect" + corrected_intent: ["question"] + corrected_sentiment: "neutral" + comment: "This was a question, not a complaint" responses: "200": - description: Feedback accepted + description: Feedback acknowledged headers: X-Request-Id: $ref: "#/components/headers/X-Request-Id" @@ -701,11 +683,13 @@ paths: value: success: true result: - feedback_id: fb_xyz789 - status: accepted - message: "Thank you! Your feedback helps improve NAWA's accuracy." + id: fb_nw_a1b2c3d4e5f6 + object: feedback + classification_id: cls_nw_a1b2c3d4e5f6 + rating: incorrect + acknowledged: true errors: [] - request_id: req_fb_abc123 + request_id: req_nw_fb1a2b3c4d5e "400": $ref: "#/components/responses/BadRequest" "401": @@ -720,18 +704,13 @@ paths: operationId: listComments summary: List classified comments description: | - Retrieve classified comments with filtering and pagination. Free endpoint. + Retrieve classified comments with filtering and offset-based pagination. + Free endpoint. tags: [Comments] x-nawa-cost: "Free" x-nawa-cache: false x-nawa-free: true parameters: - - name: channel_id - in: query - required: false - schema: - type: string - description: Filter by channel ID - name: platform in: query required: false @@ -760,27 +739,6 @@ paths: type: string enum: [gulf, egyptian, levantine, msa] description: Filter by dialect - - name: toxicity - in: query - required: false - schema: - type: string - enum: [none, mild, moderate, severe] - description: Filter by toxicity - - name: from - in: query - required: false - schema: - type: string - format: date-time - description: Start date (ISO 8601) - - name: to - in: query - required: false - schema: - type: string - format: date-time - description: End date (ISO 8601) - name: limit in: query required: false @@ -789,13 +747,15 @@ paths: minimum: 1 maximum: 100 default: 25 - description: Results per page - - name: cursor + description: Results per page (1-100) + - name: offset in: query required: false schema: - type: string - description: Pagination cursor from previous response + type: integer + minimum: 0 + default: 0 + description: Number of results to skip responses: "200": description: List of classified comments @@ -814,20 +774,20 @@ paths: result: comments: - id: cmt_abc123 + request_id: req_nw_a1b2c3d4e5f6 text: "متى الجزء الثاني؟" platform: youtube intent: question sentiment: neutral dialect: gulf - toxicity: none created_at: "2025-01-15T10:30:00Z" - channel_id: ch_123 pagination: + total: 1523 + limit: 25 + offset: 0 has_more: true - next_cursor: cur_def456 - total_count: 1523 errors: [] - request_id: req_lst_abc123 + request_id: req_nw_lst1a2b3c4d5e "401": $ref: "#/components/responses/Unauthorized" "429": @@ -840,26 +800,13 @@ paths: operationId: getAnalytics summary: Get analytics description: | - Retrieve aggregated analytics on comment trends, sentiment shifts, and - engagement patterns. Free endpoint. + Retrieve aggregated analytics on API usage patterns including request counts, + costs, cache performance, and latency. Free endpoint. tags: [Analytics] x-nawa-cost: "Free" x-nawa-cache: false x-nawa-free: true parameters: - - name: channel_id - in: query - required: false - schema: - type: string - description: Filter by channel ID - - name: platform - in: query - required: false - schema: - type: string - enum: [youtube, instagram, twitter, facebook] - description: Filter by platform - name: from in: query required: false @@ -879,9 +826,9 @@ paths: required: false schema: type: string - enum: [day, week, month] - default: day - description: Group results by time period + enum: [endpoint, day, provider] + default: endpoint + description: Group breakdown results by endpoint, day, or provider responses: "200": description: Analytics data @@ -893,45 +840,40 @@ paths: schema: $ref: "#/components/schemas/AnalyticsSuccessResponse" examples: - monthly: - summary: Monthly YouTube analytics + by_endpoint: + summary: Analytics grouped by endpoint value: success: true result: - period: - from: "2025-01-01T00:00:00Z" - to: "2025-01-31T23:59:59Z" - total_comments: 4521 - summary: - intent: - question: 1205 - praise: 1890 - complaint: 678 - suggestion: 412 - spam: 198 - other: 138 - sentiment: - positive: 2100 - negative: 890 - neutral: 1231 - mixed: 300 - dialect: - gulf: 2015 - egyptian: 1302 - levantine: 789 - msa: 415 - toxicity: - none: 3950 - mild: 320 - moderate: 180 - severe: 71 - trends: - - period: "2025-01-06/2025-01-12" - total: 1123 - sentiment_score: 0.72 - top_intent: praise + analytics: + total_requests: 12450 + total_cost: 68.70 + cache_hits: 2231 + cache_hit_rate: 0.18 + avg_latency_ms: 920 + success_rate: 0.99 + error_count: 125 + endpoint_distribution: + "/v1/classify": 8500 + "/v1/translate": 1200 + "/v1/comments/reply": 1750 + provider_distribution: + allam: 9200 + claude: 3250 + period: + from: "2025-01-01T00:00:00Z" + to: "2025-01-31T23:59:59Z" + by_endpoint: + - endpoint: "/v1/classify" + requests: 8500 + cost: 51.00 + avg_latency_ms: 820 + - endpoint: "/v1/translate" + requests: 1200 + cost: 6.00 + avg_latency_ms: 1100 errors: [] - request_id: req_ana_abc123 + request_id: req_nw_ana1a2b3c4d5e "401": $ref: "#/components/responses/Unauthorized" "429": @@ -945,7 +887,7 @@ paths: summary: Health check description: | Check the operational status of the NAWA API and its dependencies. - No authentication required. + No authentication required. Returns a flat JSON object (not the standard envelope). tags: [System] x-nawa-cost: "Free" x-nawa-cache: false @@ -957,29 +899,30 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/HealthSuccessResponse" + $ref: "#/components/schemas/HealthResponse" examples: healthy: summary: All services operational value: - success: true - result: - status: healthy - version: "1.0.0" - services: - api: operational - classification: operational - database: operational - cache: operational - timestamp: "2025-01-15T12:00:00Z" - errors: [] - request_id: req_hlt_abc123 + status: healthy + version: v1 + services: + database: + status: healthy + latency_ms: 12 + redis: + status: healthy + latency_ms: 3 + nagl: + status: healthy + timestamp: "2025-01-15T12:00:00Z" + latency_ms: 15 "503": description: One or more services degraded content: application/json: schema: - $ref: "#/components/schemas/HealthSuccessResponse" + $ref: "#/components/schemas/HealthResponse" /usage: get: @@ -999,22 +942,22 @@ paths: schema: type: string format: date-time - description: Start date (ISO 8601). Default: start of current month. + description: Start date (ISO 8601) - name: to in: query required: false schema: type: string format: date-time - description: End date (ISO 8601). Default: now. + description: End date (ISO 8601) - name: group_by in: query required: false schema: type: string - enum: [day, week, month, endpoint] - default: day - description: Group results by time period or endpoint + enum: [endpoint, day] + default: endpoint + description: Group results by endpoint or day responses: "200": description: Usage statistics @@ -1031,34 +974,25 @@ paths: value: success: true result: - period: - from: "2025-01-01T00:00:00Z" - to: "2025-01-31T23:59:59Z" - total_requests: 12450 - total_cost: 68.70 - cache_hit_rate: 0.23 + usage: + total_requests: 12450 + total_cost: 68.70 + cache_hits: 2231 + cache_hit_rate: 0.18 + period: + from: "2025-01-01T00:00:00Z" + to: "2025-01-31T23:59:59Z" by_endpoint: - endpoint: "/v1/classify" requests: 8500 cost: 51.00 cache_hits: 1955 - avg_latency_ms: 820 - endpoint: "/v1/translate" requests: 1200 cost: 6.00 cache_hits: 276 - avg_latency_ms: 1100 - - endpoint: "/v1/comments/:id/reply" - requests: 1750 - cost: 14.00 - cache_hits: 0 - avg_latency_ms: 1200 - balance: - current: 31.30 - credits_purchased: 100.00 - credits_used: 68.70 errors: [] - request_id: req_usg_abc123 + request_id: req_nw_usg1a2b3c4d5e "401": $ref: "#/components/responses/Unauthorized" "429": @@ -1163,14 +1097,14 @@ components: headers: X-Request-Id: - description: Unique request identifier + description: Unique request identifier (req_nw_xxx format) schema: type: string X-NAWA-Provider: description: AI provider used for this request schema: type: string - enum: [allam, claude] + enum: [allam, claude, gemini] X-NAWA-Cache: description: Whether the response was served from semantic cache schema: @@ -1324,7 +1258,7 @@ components: items: $ref: "#/components/schemas/CommentItem" pagination: - $ref: "#/components/schemas/Pagination" + $ref: "#/components/schemas/OffsetPagination" errors: type: array maxItems: 0 @@ -1348,22 +1282,6 @@ components: request_id: type: string - HealthSuccessResponse: - type: object - required: [success, result, errors, request_id] - properties: - success: - type: boolean - const: true - result: - $ref: "#/components/schemas/HealthResult" - errors: - type: array - maxItems: 0 - items: {} - request_id: - type: string - UsageSuccessResponse: type: object required: [success, result, errors, request_id] @@ -1424,51 +1342,67 @@ components: ClassificationResult: type: object + required: [id, object, intent, sentiment, language, requires_response, priority, provider, model, cost_usd, credits_used] properties: - text: + id: type: string - description: The original input text - intent: + description: Classification ID (cls_nw_xxx format) + examples: [cls_nw_a1b2c3d4e5f6] + object: type: string - enum: [question, complaint, praise, suggestion, spam, other] - description: Detected intent - intent_confidence: - type: number - minimum: 0 - maximum: 1 + const: classification + intent: + type: array + items: + type: string + description: Detected intents (array) + examples: [[question], [praise, suggestion]] sentiment: type: string enum: [positive, negative, neutral, mixed] - sentiment_confidence: - type: number - minimum: 0 - maximum: 1 + language: + type: string + enum: [ar, en, mixed] dialect: type: string + nullable: true + description: Arabic dialect. Null for English text. enum: [gulf, egyptian, levantine, msa] dialect_confidence: type: number + nullable: true minimum: 0 maximum: 1 - toxicity: + requires_response: + type: boolean + priority: type: string - enum: [none, mild, moderate, severe] - toxicity_confidence: - type: number - minimum: 0 - maximum: 1 - categories: - type: array - items: - type: string - description: "Content categories: engagement, support, feedback, spam" - language: + enum: [high, medium, low] + suggested_reply: + type: object + properties: + text: + type: string + direction: + type: string + enum: [ltr, rtl] + provider: type: string - enum: [ar, en, mixed] + enum: [claude, gemini, allam] model: type: string - cached: + fallback_used: type: boolean + tokens_used: + type: integer + nullable: true + description: Always null in the current version + cost_usd: + type: number + description: Cost in USD + credits_used: + type: integer + description: Credits deducted (6 per request) TranslateResult: type: object @@ -1507,12 +1441,14 @@ components: enum: [formal, casual, social_media] provider: type: string - description: "AI provider used (allam or claude)" model: type: string fallback_used: type: boolean - description: Whether the fallback provider was used + tokens_used: + type: integer + nullable: true + description: Always null in the current version cached: type: boolean cost_usd: @@ -1532,36 +1468,29 @@ components: const: detection text: type: string - description: The original input text language: type: string enum: [ar, en, mixed] - description: Detected language dialect: type: string nullable: true enum: [gulf, egyptian, levantine, msa] - description: Detected Arabic dialect. Null if not Arabic. dialect_confidence: type: number nullable: true minimum: 0 maximum: 1 - description: Confidence score for dialect detection. Null if not Arabic. script: type: string enum: [arabic, latin, mixed] - description: Script type text_direction: type: string enum: [rtl, ltr] - description: Text direction word_count: type: integer - description: Number of words in the input text fallback_used: type: boolean - description: Always false for this endpoint (local detection only) + description: Always false (local detection only) cost_usd: type: number credits_used: @@ -1581,10 +1510,8 @@ components: const: moderation text: type: string - description: The original input text is_safe: type: boolean - description: Whether the content passed moderation verdict: type: string enum: [safe, flagged] @@ -1592,7 +1519,6 @@ components: type: array items: type: string - description: List of triggered category names severity: type: string enum: [none, low, medium, high] @@ -1615,19 +1541,16 @@ components: type: array items: type: string - description: Specific problematic words/phrases found cleaned_text: type: string nullable: true - description: Text with flagged terms replaced by ***. Null if safe. + description: Text with flagged terms replaced. Null if safe. model: type: string provider: type: string - description: "AI provider used (allam or claude)" fallback_used: type: boolean - description: Whether the fallback provider was used cost_usd: type: number credits_used: @@ -1687,97 +1610,128 @@ components: RubricResult: type: object + required: [id, object, categories, multi_label, language, provider, model, cost_usd, credits_used] properties: - text: - type: string - description: The original input text - category: + id: type: string - description: The top matching category from the rubric - category_confidence: - type: number - minimum: 0 - maximum: 1 - scores: - type: object - additionalProperties: - type: number - description: Confidence scores for all rubric categories - dialect: + examples: [rcl_nw_a1b2c3d4e5f6] + object: type: string - enum: [gulf, egyptian, levantine, msa] - dialect_confidence: - type: number - minimum: 0 - maximum: 1 + const: rubric_classification + categories: + type: array + items: + type: object + required: [name, confidence] + properties: + name: + type: string + confidence: + type: number + minimum: 0 + maximum: 1 + multi_label: + type: boolean language: type: string - enum: [ar, en, mixed] + provider: + type: string model: type: string - cached: + fallback_used: type: boolean + cost_usd: + type: number + credits_used: + type: integer ReplyResult: type: object + required: [id, object, classification, reply, provider, model, cost_usd, credits_used] properties: - comment_id: - type: string - description: The comment that was replied to - reply_text: - type: string - description: The generated reply text - reply_dialect: + id: type: string - enum: [gulf, egyptian, levantine, msa] - description: Dialect used in the reply - tone: + examples: [rpl_nw_a1b2c3d4e5f6] + object: type: string - enum: [friendly, professional, casual, formal] - description: The tone used for the reply - original_intent: + const: comment_reply + classification: + type: object + properties: + intent: + type: array + items: + type: string + sentiment: + type: string + priority: + type: string + requires_response: + type: boolean + reply: + type: object + properties: + text: + type: string + direction: + type: string + enum: [ltr, rtl] + tone: + type: string + provider: type: string - enum: [question, complaint, praise, suggestion, spam, other] - description: Detected intent of the original comment - original_dialect: + model: type: string - enum: [gulf, egyptian, levantine, msa] - description: Detected dialect of the original comment + cost_usd: + type: number + credits_used: + type: integer FeedbackInput: type: object - required: [request_id, field, expected_value] + required: [classification_id, rating] properties: - request_id: + classification_id: type: string - description: The request_id from the original classification response - field: + description: The cls_nw_xxx ID from a classify response + rating: type: string - enum: [intent, sentiment, dialect, toxicity, category] - description: The field to correct - expected_value: + enum: [correct, incorrect, partial] + corrected_intent: + type: array + items: + type: string + corrected_sentiment: type: string - description: The correct value the model should have returned + enum: [positive, negative, neutral, mixed] comment: type: string - description: Optional explanation of why this correction is needed FeedbackResult: type: object + required: [id, object, classification_id, rating, acknowledged] properties: - feedback_id: + id: type: string - status: + examples: [fb_nw_a1b2c3d4e5f6] + object: + type: string + const: feedback + classification_id: type: string - enum: [accepted] - message: + rating: type: string + acknowledged: + type: boolean + const: true CommentItem: type: object properties: id: type: string + request_id: + type: string text: type: string platform: @@ -1785,82 +1739,103 @@ components: enum: [youtube, instagram, twitter, facebook] intent: type: string - enum: [question, complaint, praise, suggestion, spam, other] sentiment: type: string enum: [positive, negative, neutral, mixed] dialect: type: string enum: [gulf, egyptian, levantine, msa] - toxicity: - type: string - enum: [none, mild, moderate, severe] created_at: type: string format: date-time - channel_id: - type: string - Pagination: + OffsetPagination: type: object properties: + total: + type: integer + limit: + type: integer + offset: + type: integer has_more: type: boolean - next_cursor: - type: string - nullable: true - total_count: - type: integer AnalyticsResult: type: object properties: - period: + analytics: type: object properties: - from: - type: string - format: date-time - to: - type: string - format: date-time - total_comments: - type: integer - summary: - type: object - properties: - intent: + total_requests: + type: integer + total_cost: + type: number + cache_hits: + type: integer + cache_hit_rate: + type: number + minimum: 0 + maximum: 1 + avg_latency_ms: + type: number + success_rate: + type: number + minimum: 0 + maximum: 1 + error_count: + type: integer + endpoint_distribution: type: object additionalProperties: type: integer - sentiment: + provider_distribution: type: object additionalProperties: type: integer - dialect: + period: type: object - additionalProperties: - type: integer - toxicity: + properties: + from: + type: string + format: date-time + nullable: true + to: + type: string + format: date-time + nullable: true + + UsageResult: + type: object + properties: + usage: + type: object + properties: + total_requests: + type: integer + total_cost: + type: number + cache_hits: + type: integer + cache_hit_rate: + type: number + minimum: 0 + maximum: 1 + period: type: object - additionalProperties: - type: integer - trends: - type: array - items: - type: object - properties: - period: - type: string - total: - type: integer - sentiment_score: - type: number - top_intent: - type: string + properties: + from: + type: string + format: date-time + nullable: true + to: + type: string + format: date-time + nullable: true - HealthResult: + HealthResponse: type: object + required: [status, version, services, timestamp] properties: status: type: string @@ -1870,67 +1845,58 @@ components: services: type: object properties: - api: - type: string - enum: [operational, degraded, down] - classification: - type: string - enum: [operational, degraded, down] database: - type: string - enum: [operational, degraded, down] - cache: - type: string - enum: [operational, degraded, down] + type: object + properties: + status: + type: string + enum: [healthy, degraded, down] + latency_ms: + type: integer + redis: + type: object + properties: + status: + type: string + enum: [healthy, degraded, down] + latency_ms: + type: integer + nagl: + type: object + properties: + status: + type: string + enum: [healthy, down] timestamp: type: string format: date-time + latency_ms: + type: integer - UsageResult: + Rubric: type: object + required: [categories] properties: - period: - type: object - properties: - from: - type: string - format: date-time - to: - type: string - format: date-time - total_requests: - type: integer - total_cost: - type: number - description: Total cost in USD - cache_hit_rate: - type: number - minimum: 0 - maximum: 1 - by_endpoint: + categories: type: array + minItems: 1 + maxItems: 20 items: type: object + required: [name] properties: - endpoint: + name: type: string - requests: - type: integer - cost: - type: number - cache_hits: - type: integer - avg_latency_ms: - type: integer - balance: - type: object - properties: - current: - type: number - credits_purchased: - type: number - credits_used: - type: number + description: + type: string + multi_label: + type: boolean + default: false + confidence_threshold: + type: number + minimum: 0 + maximum: 1 + default: 0.5 WebhookEndpoint: type: object