diff --git a/api-reference/classify.mdx b/api-reference/classify.mdx index ac3d730..40a5a57 100644 --- a/api-reference/classify.mdx +++ b/api-reference/classify.mdx @@ -1,18 +1,20 @@ --- -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. +Classify any comment with a single request. Returns intent, sentiment, dialect, priority, and whether a response is needed. -## Request +Arabic comments (including Arabizi) are routed to ALLaM. English comments are routed to Claude. Common Arabizi patterns like greetings and praise are classified instantly via a local rubric without an LLM call. - 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,11 +22,17 @@ 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: `claude`, `gemini`, `allam`. Bypasses automatic language-based routing. Useful for A/B testing. | + ### Body parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `text` | string | Yes | The comment text to classify. Max 5,000 characters. | +| `text` | string | Yes | The comment text to classify. Supports Arabic script, Arabizi (Latin-script Arabic like `7abibi`), and English. 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. | @@ -35,7 +43,7 @@ Classify any comment with a single request. Returns intent, sentiment, dialect, ```bash cURL curl -X POST https://api.trynawa.com/v1/classify \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ + -H "Authorization: Bearer nawa_live_sk_xxx" \ -H "Content-Type: application/json" \ -d '{ "text": "متى الجزء الثاني؟", @@ -44,6 +52,18 @@ curl -X POST https://api.trynawa.com/v1/classify \ }' ``` +```python Python +from nawa import Nawa + +nawa = Nawa(api_key="nawa_live_sk_xxx") + +result = nawa.classify( + text="متى الجزء الثاني؟", + platform="youtube", + channel_id="ch_123" +) +``` + ```typescript TypeScript import { Nawa } from '@nawalabs/sdk' @@ -56,18 +76,6 @@ const { data, error } = await nawa.classify({ }) ``` -```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 +86,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, + "language": "ar", "dialect": "gulf", "dialect_confidence": 0.95, - "toxicity": "none", - "toxicity_confidence": 0.99, - "categories": ["engagement"], - "language": "ar", - "model": "nagl-v1", - "cached": false + "requires_response": true, + "priority": "medium", + "suggested_reply": { + "text": "", + "direction": "rtl" + }, + "provider": "allam", + "model": "allam-13b", + "fallback_used": false, + "tokens_used": null, + "cost_usd": 0.006, + "credits_used": 6 }, "errors": [], - "request_id": "req_abc123def456" + "request_id": "req_nw_a1b2c3d4e5f67890" } ``` @@ -102,50 +116,80 @@ result = nawa.classify( | Header | Description | |--------|-------------| | `X-Request-Id` | Unique request identifier for debugging | +| `X-NAWA-Provider` | AI provider used (`allam`, `claude`, or `gemini`) | +| `X-NAWA-Cache` | `HIT` if served from semantic cache (no cost), `MISS` otherwise | +| `X-NAWA-Balance` | Current credit balance in USD | | `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) | ### 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 (e.g. `["praise"]`, `["question", "complaint"]`) | +| `sentiment` | string | `positive`, `negative`, `neutral`, or `mixed` | +| `language` | string | Detected language: `ar`, `en`, or `mixed` | +| `dialect` | string \| null | Arabic dialect: `gulf`, `egyptian`, `levantine`, `maghrebi`, `msa`. Null for English. | +| `dialect_confidence` | number \| null | Dialect confidence (0-1). Null for English. | +| `requires_response` | boolean | Whether this comment needs a reply | +| `priority` | string | `high`, `medium`, or `low` | +| `suggested_reply` | object | Reply direction hint | +| `suggested_reply.text` | string | Summary text (may be empty) | +| `suggested_reply.direction` | string | `rtl` for Arabic, `ltr` for English | +| `provider` | string | AI provider used: `allam`, `claude`, or `gemini` | +| `model` | string | Model identifier (e.g. `allam-13b`, `claude-sonnet-4-5-20250929`, `arabizi-rubric`) | +| `fallback_used` | boolean | Whether the primary provider failed and a fallback was used | +| `tokens_used` | integer \| null | Token count (currently always null) | +| `cost_usd` | number | Cost in USD | +| `credits_used` | integer | Credits consumed | ### Error responses | Status | Type | When | |--------|------|------| -| 400 | `invalid_request_error` | Missing `text`, invalid `platform`, text too long | +| 400 | `invalid_request_error` | Missing `text`, invalid `provider` query param, text empty | | 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 | +## Provider routing + +NAWA automatically selects the best AI provider based on the detected language: + +| Language | Primary | Fallback 1 | Fallback 2 | +|----------|---------|------------|------------| +| Arabic (`ar`) | ALLaM | Claude | Gemini | +| Mixed | ALLaM | Claude | Gemini | +| English (`en`) | Claude | Gemini | ALLaM | + +Use the `?provider=` query parameter to override this routing for A/B testing: + +```bash +curl -X POST "https://api.trynawa.com/v1/classify?provider=claude" \ + -H "Authorization: Bearer nawa_live_sk_xxx" \ + -H "Content-Type: application/json" \ + -d '{"text": "ما شاء الله عليك"}' +``` + +## Arabizi support + +NAWA detects Arabizi (Latin-script Arabic that uses numbers for Arabic sounds, like `7` for ح and `3` for ع) automatically. Arabizi text is transliterated to Arabic script before classification, so the language is detected as `ar`. + +Common Arabizi greetings and praise are classified instantly via a local pattern matcher (model: `arabizi-rubric`) without an LLM call. + ### More examples - + ```bash curl -X POST https://api.trynawa.com/v1/classify \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ + -H "Authorization: Bearer nawa_live_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": "9ba7oooo ya 7abibi el video 7elo kter", "platform": "youtube"}' ``` Response: @@ -153,32 +197,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_f7e8d9c0b1a2", + "object": "classification", + "intent": ["praise"], "sentiment": "positive", - "sentiment_confidence": 0.98, + "language": "ar", "dialect": null, "dialect_confidence": null, - "toxicity": "none", - "toxicity_confidence": 0.99, - "categories": ["engagement"], - "language": "en", - "model": "claude-v1", - "cached": false + "requires_response": false, + "priority": "medium", + "suggested_reply": { + "text": "", + "direction": "rtl" + }, + "provider": "claude", + "model": "arabizi-rubric", + "fallback_used": false, + "tokens_used": null, + "cost_usd": 0.006, + "credits_used": 6 }, "errors": [], - "request_id": "req_en_praise_001" + "request_id": "req_nw_b2c3d4e5f67890ab" } ``` - + ```bash curl -X POST https://api.trynawa.com/v1/classify \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ + -H "Authorization: Bearer nawa_live_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"}' + -d '{"text": "This is hands down the best review I have seen on this phone. Subscribed!", "platform": "youtube"}' ``` Response: @@ -186,32 +236,38 @@ result = nawa.classify( { "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, + "id": "cls_nw_c3d4e5f6a7b8", + "object": "classification", + "intent": ["praise"], + "sentiment": "positive", + "language": "en", "dialect": null, "dialect_confidence": null, - "toxicity": "none", - "toxicity_confidence": 0.97, - "categories": ["feedback"], - "language": "en", - "model": "claude-v1", - "cached": false + "requires_response": false, + "priority": "medium", + "suggested_reply": { + "text": "", + "direction": "ltr" + }, + "provider": "claude", + "model": "claude-sonnet-4-5-20250929", + "fallback_used": false, + "tokens_used": null, + "cost_usd": 0.006, + "credits_used": 6 }, "errors": [], - "request_id": "req_en_complaint_001" + "request_id": "req_nw_en_praise_001abc" } ``` - + ```bash curl -X POST https://api.trynawa.com/v1/classify \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ + -H "Authorization: Bearer nawa_live_sk_xxx" \ -H "Content-Type: application/json" \ - -d '{"text": "What camera and lens setup are you using for these shots?", "platform": "youtube"}' + -d '{"text": "The audio quality is terrible in this one. Can barely hear anything after the 5 minute mark.", "platform": "youtube"}' ``` Response: @@ -219,30 +275,36 @@ result = nawa.classify( { "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, + "id": "cls_nw_d4e5f6a7b8c9", + "object": "classification", + "intent": ["complaint"], + "sentiment": "negative", + "language": "en", "dialect": null, "dialect_confidence": null, - "toxicity": "none", - "toxicity_confidence": 0.99, - "categories": ["engagement"], - "language": "en", - "model": "claude-v1", - "cached": false + "requires_response": true, + "priority": "high", + "suggested_reply": { + "text": "", + "direction": "ltr" + }, + "provider": "claude", + "model": "claude-sonnet-4-5-20250929", + "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_complaint_001" } ``` - + ```bash curl -X POST https://api.trynawa.com/v1/classify \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ + -H "Authorization: Bearer nawa_live_sk_xxx" \ -H "Content-Type: application/json" \ -d '{"text": "ما شاء الله عليك، محتوى رهيب!", "platform": "youtube"}' ``` @@ -252,30 +314,36 @@ result = nawa.classify( { "success": true, "result": { - "text": "ما شاء الله عليك، محتوى رهيب!", - "intent": "praise", - "intent_confidence": 0.98, + "id": "cls_nw_e5f6a7b8c9d0", + "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": "medium", + "suggested_reply": { + "text": "", + "direction": "rtl" + }, + "provider": "allam", + "model": "allam-13b", + "fallback_used": false, + "tokens_used": null, + "cost_usd": 0.006, + "credits_used": 6 }, "errors": [], - "request_id": "req_ghi789jkl012" + "request_id": "req_nw_ghi789jkl012ab" } ``` - + ```bash curl -X POST https://api.trynawa.com/v1/classify \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ + -H "Authorization: Bearer nawa_live_sk_xxx" \ -H "Content-Type: application/json" \ -d '{"text": "الصوت وحش أوي في الفيديو ده", "platform": "youtube"}' ``` @@ -285,30 +353,36 @@ result = nawa.classify( { "success": true, "result": { - "text": "الصوت وحش أوي في الفيديو ده", - "intent": "complaint", - "intent_confidence": 0.94, + "id": "cls_nw_f6a7b8c9d0e1", + "object": "classification", + "intent": ["complaint"], "sentiment": "negative", - "sentiment_confidence": 0.96, + "language": "ar", "dialect": "egyptian", "dialect_confidence": 0.97, - "toxicity": "none", - "toxicity_confidence": 0.95, - "categories": ["feedback"], - "language": "ar", - "model": "nagl-v1", - "cached": false + "requires_response": true, + "priority": "high", + "suggested_reply": { + "text": "", + "direction": "rtl" + }, + "provider": "allam", + "model": "allam-13b", + "fallback_used": false, + "tokens_used": null, + "cost_usd": 0.006, + "credits_used": 6 }, "errors": [], - "request_id": "req_mno345pqr678" + "request_id": "req_nw_mno345pqr678ab" } ``` - + ```bash curl -X POST https://api.trynawa.com/v1/classify \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ + -H "Authorization: Bearer nawa_live_sk_xxx" \ -H "Content-Type: application/json" \ -d '{"text": "لو سمحت حاول تحكي عن المطاعم بلبنان", "platform": "instagram"}' ``` @@ -318,22 +392,28 @@ result = nawa.classify( { "success": true, "result": { - "text": "لو سمحت حاول تحكي عن المطاعم بلبنان", - "intent": "suggestion", - "intent_confidence": 0.92, + "id": "cls_nw_a7b8c9d0e1f2", + "object": "classification", + "intent": ["suggestion"], "sentiment": "neutral", - "sentiment_confidence": 0.88, + "language": "ar", "dialect": "levantine", "dialect_confidence": 0.96, - "toxicity": "none", - "toxicity_confidence": 0.99, - "categories": ["engagement"], - "language": "ar", - "model": "nagl-v1", - "cached": false + "requires_response": true, + "priority": "medium", + "suggested_reply": { + "text": "", + "direction": "rtl" + }, + "provider": "allam", + "model": "allam-13b", + "fallback_used": false, + "tokens_used": null, + "cost_usd": 0.006, + "credits_used": 6 }, "errors": [], - "request_id": "req_stu901vwx234" + "request_id": "req_nw_stu901vwx234ab" } ``` diff --git a/api-reference/comments-reply.mdx b/api-reference/comments-reply.mdx index d6321d7..352dc9c 100644 --- a/api-reference/comments-reply.mdx +++ b/api-reference/comments-reply.mdx @@ -1,62 +1,82 @@ --- -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 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 context-aware reply in a single call. Replies match the commenter's language, dialect, and script style. + +For **Arabizi input** (Latin-script Arabic like `7abibi`, `shu ra2yak`), NAWA automatically detects the script and forces Claude as the reply provider. This ensures replies come back in matching Latin-script Arabizi rather than Arabic script. + +For **Arabic-script input**, replies are generated in the same dialect and script. For **English input**, replies are natural and platform-appropriate. - Cost: **$0.008** per request (8 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`). + Cost: **$0.008** per request (8 credits). This includes both classification and reply generation. ## Request -### Path parameters +### Headers + +| Header | Required | Description | +|--------|----------|-------------| +| `Authorization` | Yes | `Bearer nawa_live_sk_xxx` or `Bearer nawa_test_sk_xxx` | +| `Content-Type` | Yes | `application/json` | + +### Query parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `id` | string | Yes | The comment ID to reply to. | +| `provider` | string | No | Force a specific AI provider for classification: `claude`, `gemini`, `allam`. For Arabizi input, reply generation always uses Claude regardless of this parameter. | ### 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 | Comment text to classify and reply to. Supports Arabic script, Arabizi, and English. | +| `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 \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ +curl -X POST https://api.trynawa.com/v1/comments/reply \ + -H "Authorization: Bearer nawa_live_sk_xxx" \ -H "Content-Type: application/json" \ -d '{ + "text": "shu ra2yak fi el beat? ana b7eb el vibe kter", "tone": "friendly", - "context": "Tech review channel focused on smartphones" + "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="nawa_live_sk_xxx") + result = nawa.comments.reply( - comment_id="cmt_abc123", + text="shu ra2yak fi el beat? ana b7eb el vibe kter", tone="friendly", - context="Tech review channel focused on smartphones" + 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: 'shu ra2yak fi el beat? ana b7eb el vibe kter', + tone: 'friendly', + maxLength: 500 +}) +``` + ## Response @@ -67,25 +87,197 @@ 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": ["question"], + "sentiment": "positive", + "priority": "medium", + "requires_response": true + }, + "reply": { + "text": "ahlan! el beat 7elo kter, glad you like the vibe", + "direction": "ltr", + "tone": "friendly" + }, + "provider": "claude", + "model": "claude-sonnet-4-5-20250929", + "cost_usd": 0.008, + "credits_used": 8 }, "errors": [], - "request_id": "req_rep789xyz012" + "request_id": "req_nw_rpl1a2b3c4d5e6f7" } ``` +### Response headers + +| Header | Description | +|--------|-------------| +| `X-Request-Id` | Unique request identifier for debugging | +| `X-NAWA-Provider` | AI provider that generated the reply | +| `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) | + ### Result fields | 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.intent` | string[] | Detected intents of the original comment | +| `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.text` | string | Generated reply text. Empty string if `requires_response` is `false`. | +| `reply.direction` | string | `rtl` if the reply contains Arabic script, `ltr` otherwise | +| `reply.tone` | string | The tone used for the reply | +| `provider` | string | AI provider that generated the reply | +| `model` | string | Model identifier | +| `cost_usd` | number | Cost in USD | +| `credits_used` | integer | Credits consumed | + +### Error responses + +| Status | Type | When | +|--------|------|------| +| 400 | `invalid_request_error` | Missing `text`, invalid `tone`, `max_length` out of range, invalid `provider` | +| 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 | + +## Arabizi reply behavior + +When NAWA detects Arabizi input, reply generation is handled differently: + +1. **Classification** proceeds normally through the language-based router (typically ALLaM for Arabic content) +2. **Reply generation** forces Claude regardless of classification provider, because only Claude follows the Arabizi script mirror hint +3. The reply comes back in **Latin-script Arabizi** matching the commenter's style (using number substitutions like `7` for ح and `3` for ع) + +If the comment is classified as not requiring a response (e.g., simple praise), `reply.text` is an empty string and `requires_response` is `false`. + +### More examples + + + + ```bash + curl -X POST https://api.trynawa.com/v1/comments/reply \ + -H "Authorization: Bearer nawa_live_sk_xxx" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "هذا المنتج سيء جداً ولا أنصح به", + "tone": "professional" + }' + ``` + + Response: + ```json + { + "success": true, + "result": { + "id": "rpl_nw_b2c3d4e5f6a7", + "object": "comment_reply", + "classification": { + "intent": ["complaint"], + "sentiment": "negative", + "priority": "high", + "requires_response": true + }, + "reply": { + "text": "نعتذر عن هذه التجربة. نود مساعدتك في حل المشكلة.", + "direction": "rtl", + "tone": "professional" + }, + "provider": "allam", + "model": "allam-13b", + "cost_usd": 0.008, + "credits_used": 8 + }, + "errors": [], + "request_id": "req_nw_rpl2b3c4d5e6f7a8" + } + ``` + + + + ```bash + curl -X POST https://api.trynawa.com/v1/comments/reply \ + -H "Authorization: Bearer nawa_live_sk_xxx" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "wallah a7san video, y3afech 3la hal content", + "tone": "friendly" + }' + ``` + + Response: + ```json + { + "success": true, + "result": { + "id": "rpl_nw_c3d4e5f6a7b8", + "object": "comment_reply", + "classification": { + "intent": ["praise"], + "sentiment": "positive", + "priority": "medium", + "requires_response": false + }, + "reply": { + "text": "", + "direction": "ltr", + "tone": "friendly" + }, + "provider": "allam", + "model": "allam-13b", + "cost_usd": 0.008, + "credits_used": 8 + }, + "errors": [], + "request_id": "req_nw_rpl3c4d5e6f7a8b9" + } + ``` + + + + ```bash + curl -X POST https://api.trynawa.com/v1/comments/reply \ + -H "Authorization: Bearer nawa_live_sk_xxx" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "What camera setup are you using for these shots?", + "tone": "casual" + }' + ``` + + Response: + ```json + { + "success": true, + "result": { + "id": "rpl_nw_d4e5f6a7b8c9", + "object": "comment_reply", + "classification": { + "intent": ["question"], + "sentiment": "neutral", + "priority": "medium", + "requires_response": true + }, + "reply": { + "text": "Hey! I'm using a Sony A7IV with a 24-70mm f/2.8 lens for most shots.", + "direction": "ltr", + "tone": "casual" + }, + "provider": "claude", + "model": "claude-sonnet-4-5-20250929", + "cost_usd": 0.008, + "credits_used": 8 + }, + "errors": [], + "request_id": "req_nw_rpl4d5e6f7a8b9c0" + } + ``` + + diff --git a/api-reference/rubric-classify.mdx b/api-reference/rubric-classify.mdx index 4489175..445b59a 100644 --- a/api-reference/rubric-classify.mdx +++ b/api-reference/rubric-classify.mdx @@ -1,83 +1,104 @@ --- -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 scoring criteria." +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. Define your own categories with optional descriptions for domain-specific classification. Supports multi-label classification 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). ## Request +### Headers + +| Header | Required | Description | +|--------|----------|-------------| +| `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: `claude`, `gemini`, `allam`. Bypasses automatic language-based routing. | + ### 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. | +| `rubric` | object | Yes | The rubric definition with categories and scoring criteria. | +| `rubric.categories` | array | Yes | Array of category objects (max 20). Each object requires a `name` field and accepts an optional `description` field. | +| `rubric.multi_label` | boolean | No | When `true`, returns all categories above the confidence threshold. Default: `false` (single best match). | +| `rubric.confidence_threshold` | number | No | Minimum confidence (0-1) for a category to appear in results. Default: `0.5`. | ### Example request ```bash cURL -curl -X POST https://api.trynawa.com/v1/rubric/classify \ - -H "Authorization: Bearer nawa_test_sk_xxx" \ +curl -X POST https://api.trynawa.com/v1/rubric-classify \ + -H "Authorization: Bearer nawa_live_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", "description": "Comments about the content quality"}, + {"name": "off_topic", "description": "Not related to the video"} + ], + "multi_label": false, + "confidence_threshold": 0.5 } - }, - platform: 'youtube' -}) + }' ``` ```python Python +from nawa import Nawa + +nawa = Nawa(api_key="nawa_live_sk_xxx") + 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", - }, + "categories": [ + {"name": "translation_request", "description": "User is asking for subtitles or translation"}, + {"name": "technical_issue", "description": "Audio, video, or playback problems"}, + {"name": "content_feedback", "description": "Comments about the content quality"}, + {"name": "off_topic", "description": "Not related to the video"}, + ], + "multi_label": False, + "confidence_threshold": 0.5, }, - platform="youtube", ) ``` +```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', description: 'Comments about the content quality' }, + { name: 'off_topic', description: 'Not related to the video' } + ], + multiLabel: false, + confidenceThreshold: 0.5 + } +}) +``` + ## Response @@ -88,34 +109,103 @@ 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} + ], + "multi_label": false, "language": "ar", - "model": "nagl-v1", - "cached": false + "provider": "allam", + "model": "allam-13b", + "fallback_used": false, + "cost_usd": 0.003, + "credits_used": 3 }, "errors": [], - "request_id": "req_rub123abc456" + "request_id": "req_nw_rub123abc456def7" } ``` +### Response headers + +| Header | Description | +|--------|-------------| +| `X-Request-Id` | Unique request identifier for debugging | +| `X-NAWA-Provider` | AI provider used (`allam`, `claude`, or `gemini`) | +| `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) | + ### Result fields | 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 | Rubric classification ID in `rcl_nw_xxx` format | +| `object` | string | Always `"rubric_classification"` | +| `categories` | array | Matched categories with confidence scores, filtered by the threshold | +| `categories[].name` | string | Category name from your rubric | +| `categories[].confidence` | number | Confidence score (0-1, rounded to 2 decimal places) | +| `multi_label` | boolean | Whether multi-label mode was used | +| `language` | string | Detected language: `ar`, `en`, or `mixed` | +| `provider` | string | AI provider used: `allam`, `claude`, or `gemini` | +| `model` | string | Model identifier | +| `fallback_used` | boolean | Whether a fallback provider was used | +| `cost_usd` | number | Cost in USD | +| `credits_used` | integer | Credits consumed | + +### Error responses + +| Status | Type | When | +|--------|------|------| +| 400 | `invalid_request_error` | Missing `text` or `rubric`, empty categories, categories exceed 20, missing category `name`, invalid `provider` | +| 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 | + +### Multi-label example + +When `multi_label` is `true`, all categories that meet the confidence threshold are returned: + +```bash +curl -X POST https://api.trynawa.com/v1/rubric-classify \ + -H "Authorization: Bearer nawa_live_sk_xxx" \ + -H "Content-Type: application/json" \ + -d '{ + "text": "الصوت مو واضح وأبي ترجمة عربية لو سمحت", + "rubric": { + "categories": [ + {"name": "translation_request"}, + {"name": "technical_issue"}, + {"name": "content_feedback"} + ], + "multi_label": true, + "confidence_threshold": 0.3 + } + }' +``` + +Response: +```json +{ + "success": true, + "result": { + "id": "rcl_nw_b2c3d4e5f6a7", + "object": "rubric_classification", + "categories": [ + {"name": "translation_request", "confidence": 0.82}, + {"name": "technical_issue", "confidence": 0.71} + ], + "multi_label": true, + "language": "ar", + "provider": "allam", + "model": "allam-13b", + "fallback_used": false, + "cost_usd": 0.003, + "credits_used": 3 + }, + "errors": [], + "request_id": "req_nw_rub789ghi012jkl3" +} +``` diff --git a/openapi.yaml b/openapi.yaml index 17706f2..7ca77c1 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -53,12 +53,23 @@ 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 and per-user calibration. + + Arabic text (including Arabizi) is routed to ALLaM. English text is routed to Claude. + Use the `provider` query parameter to override routing for A/B testing. 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. Bypasses language-based routing. + schema: + type: string + enum: [claude, gemini, allam] requestBody: required: true content: @@ -71,10 +82,11 @@ paths: type: string minLength: 1 maxLength: 5000 - description: The comment text to classify + description: The comment text to classify. Supports Arabic script, Arabizi (Latin-script Arabic), and English. examples: - "متى الجزء الثاني؟" - "ما شاء الله عليك، محتوى رهيب!" + - "9ba7oooo ya 7abibi el video 7elo kter" platform: type: string enum: [youtube, instagram, twitter, facebook] @@ -117,21 +129,51 @@ paths: 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 + language: ar dialect: gulf dialect_confidence: 0.95 - toxicity: none - toxicity_confidence: 0.99 - categories: ["engagement"] - language: ar - model: nagl-v1 - cached: false + requires_response: true + priority: medium + suggested_reply: + text: "" + direction: rtl + provider: allam + model: allam-13b + fallback_used: false + tokens_used: null + cost_usd: 0.006 + credits_used: 6 errors: [] request_id: req_nw_a1b2c3d4e5f67890 + arabizi_praise: + summary: Arabizi praise (Latin-script Arabic) + value: + success: true + result: + id: cls_nw_f7e8d9c0b1a2 + object: classification + intent: ["praise"] + sentiment: positive + language: ar + dialect: gulf + dialect_confidence: 0.88 + requires_response: false + priority: medium + suggested_reply: + text: "" + direction: rtl + provider: claude + model: arabizi-rubric + fallback_used: false + tokens_used: null + cost_usd: 0.006 + credits_used: 6 + errors: [] + request_id: req_nw_b2c3d4e5f67890ab "400": $ref: "#/components/responses/BadRequest" "401": @@ -457,17 +499,26 @@ 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. Define your own categories with + optional descriptions and scoring criteria for domain-specific classification. + 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 + description: Force a specific AI provider for A/B testing. Bypasses language-based routing. + schema: + type: string + enum: [claude, gemini, allam] requestBody: required: true content: @@ -492,27 +543,40 @@ paths: 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 + type: object + required: [name] + properties: + name: + type: string + description: Category name + description: + type: string + description: Category description to guide the model + description: List of categories to classify against (max 20) + multi_label: + type: boolean + default: false + description: "When `true`, returns all categories above the confidence threshold. When `false`, returns only the single best match." + confidence_threshold: + type: number + minimum: 0 + maximum: 1 + default: 0.5 + description: Minimum confidence score for a category to be included in results 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" + description: "Comments about the content quality" + - name: "off_topic" + description: "Not related to the video" + multi_label: false + confidence_threshold: 0.5 responses: "200": description: Rubric classification result @@ -521,10 +585,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: @@ -541,21 +601,22 @@ paths: 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: allam-13b + fallback_used: false + cost_usd: 0.003 + credits_used: 3 errors: [] - request_id: req_rub123abc456 + request_id: req_nw_rub123abc456def7 "400": $ref: "#/components/responses/BadRequest" "401": @@ -567,37 +628,48 @@ 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. + + For Arabizi (Latin-script Arabic like "7abibi", "shu ra2yak"), NAWA automatically + detects the script style and forces Claude as the reply provider to produce + matching Latin-script replies. Arabic-script comments are routed normally. 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 + description: Force a specific AI provider for classification. For Arabizi input, reply generation always uses Claude regardless of this parameter. 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. Supports Arabic script, Arabizi, and English. + examples: + - "هذا المنتج سيء جداً ولا أنصح به" + - "shu ra2yak fi el beat? ana b7eb el vibe kter" tone: type: string enum: [friendly, professional, casual, formal] - default: friendly + default: professional description: Reply tone max_length: type: integer @@ -605,29 +677,18 @@ paths: 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: + text: "shu ra2yak fi el beat? ana b7eb el vibe kter" tone: "friendly" - context: "Tech review channel focused on smartphones" + 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 +700,50 @@ paths: schema: $ref: "#/components/schemas/ReplySuccessResponse" examples: - gulf_reply: - summary: Friendly reply to Gulf Arabic question + arabizi_reply: + summary: Arabizi comment with Latin-script reply 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: ["question"] + sentiment: positive + priority: medium + requires_response: true + reply: + text: "ahlan! el beat 7elo kter, glad you like the vibe" + direction: ltr + tone: friendly + provider: claude + model: claude-sonnet-4-5-20250929 + cost_usd: 0.008 + credits_used: 8 errors: [] - request_id: req_rep789xyz012 + request_id: req_nw_rpl1a2b3c4d5e6f7 + arabic_reply: + summary: Arabic comment with Arabic reply + value: + success: true + result: + id: rpl_nw_b2c3d4e5f6a7 + object: comment_reply + classification: + intent: ["complaint"] + sentiment: negative + priority: high + requires_response: true + reply: + text: "نعتذر عن هذه التجربة. نود مساعدتك في حل المشكلة." + direction: rtl + tone: professional + provider: allam + model: allam-13b + cost_usd: 0.008 + credits_used: 8 + errors: [] + request_id: req_nw_rpl2b3c4d5e6f7a8 "400": $ref: "#/components/responses/BadRequest" "401": @@ -1170,7 +1262,7 @@ components: 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: @@ -1424,51 +1516,72 @@ 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 (e.g. `["praise"]`, `["question", "complaint"]`) 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 - enum: [gulf, egyptian, levantine, msa] + nullable: true + description: Arabic dialect (`gulf`, `egyptian`, `levantine`, `maghrebi`, `msa`). Null for English text. dialect_confidence: type: number + nullable: true minimum: 0 maximum: 1 - toxicity: + description: Dialect confidence (0-1). Null for English text. + requires_response: + type: boolean + description: Whether this comment needs a reply + 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 + description: Reply direction summary (may be empty) + direction: + type: string + enum: [ltr, rtl] + description: Text direction based on detected language + provider: type: string - enum: [ar, en, mixed] + enum: [claude, gemini, allam] + description: AI provider used for classification model: type: string - cached: + description: "Model identifier (e.g. `allam-13b`, `claude-sonnet-4-5-20250929`, `arabizi-rubric`)" + fallback_used: type: boolean + description: Whether a fallback provider was used + tokens_used: + type: integer + nullable: true + description: Token count (currently always null) + cost_usd: + type: number + description: Cost in USD (always 0.006) + credits_used: + type: integer + description: Credits consumed (always 6) TranslateResult: type: object @@ -1550,8 +1663,8 @@ components: description: Confidence score for dialect detection. Null if not Arabic. script: type: string - enum: [arabic, latin, mixed] - description: Script type + enum: [arabic, latin, arabizi, mixed] + description: "Script type. `arabizi` indicates Latin-script Arabic (e.g. `7abibi`, `shu`)." text_direction: type: string enum: [rtl, ltr] @@ -1687,62 +1800,110 @@ 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: + description: Rubric classification ID (`rcl_nw_xxx` format) + 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 + description: Category name from the rubric + confidence: + type: number + minimum: 0 + maximum: 1 + description: Confidence score (rounded to 2 decimal places) + description: Matched categories with confidence scores, filtered by the confidence threshold + multi_label: + type: boolean + description: Whether multi-label classification was used language: type: string enum: [ar, en, mixed] + provider: + type: string + enum: [claude, gemini, allam] + description: AI provider used model: type: string - cached: + description: Model identifier + fallback_used: type: boolean + description: Whether a fallback provider was used + cost_usd: + type: number + description: Cost in USD (always 0.003) + credits_used: + type: integer + description: Credits consumed (always 3) 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: + description: Reply ID (`rpl_nw_xxx` format) + 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 + description: Classification of the input comment + properties: + intent: + type: array + items: + type: string + description: Detected intents + sentiment: + type: string + enum: [positive, negative, neutral, mixed] + priority: + type: string + enum: [high, medium, low] + requires_response: + type: boolean + description: Whether the comment warrants a reply + reply: + type: object + description: The generated reply + properties: + text: + type: string + description: "Generated reply text. Empty string if `requires_response` is `false`." + direction: + type: string + enum: [ltr, rtl] + description: Text direction based on the reply content + tone: + type: string + enum: [friendly, professional, casual, formal] + description: Tone used for the reply + provider: type: string - enum: [question, complaint, praise, suggestion, spam, other] - description: Detected intent of the original comment - original_dialect: + enum: [claude, gemini, allam] + description: AI provider that generated the reply + model: type: string - enum: [gulf, egyptian, levantine, msa] - description: Detected dialect of the original comment + description: Model identifier + cost_usd: + type: number + description: Cost in USD (always 0.008) + credits_used: + type: integer + description: Credits consumed (always 8) FeedbackInput: type: object