From 559f7b64e8e4e92a5365a0b806ac45b469aa6733 Mon Sep 17 00:00:00 2001
From: Oussama Mahjoub To ensure fair usage and optimal performance for all users, our API has rate limits in place. When you exceed a rate limit, your request will typically receive a The limits detailed below are our default settings for all models. We understand that your needs may vary. If your application requires higher throughput or more tailored rate limits for specific models, please contact us: To ensure fair usage and optimal performance for all users, our API has rate limits in place. When you exceed a rate limit, your request will typically receive a The limits detailed below are our default settings for all models. We understand that your needs may vary. If your application requires higher throughput or more tailored rate limits for specific models, please contact us: To ensure fair usage and optimal performance for all users, our API has
+ rate limits in place. When you exceed a rate limit, your request will
+ typically receive a The limits detailed below are our default settings for all models. We
+ understand that your needs may vary. If your application requires higher
+ throughput or more tailored rate limits for specific models, please contact
+ us: Typical cause: the caller cancelled or timed out the request mid-flight (for example, a
+ * subscriber cancelling a stream). The request was abandoned deliberately, so it is not
+ * retryable. The audio domain has multiple concerns surfaced as flat methods on this single client: Subsequent sub-milestones add {@code speech(...)} (TTS — binary audio out) and
- * {@code transcribe(...)} (STT — multipart audio in) to this same interface. Implementations must be thread-safe — one {@code AudioClient} instance backs every call on
* a given {@code FanarClient}. The returned publisher supports a single subscriber, honours back-pressure, and emits
+ * opaque {@code byte[]} chunks whose boundaries follow transport reads — concatenate them in
+ * emission order to reconstruct the full clip. Cancelling the subscription closes the
+ * underlying connection. The initial request (headers, interceptors, error mapping) behaves
+ * exactly like {@link #speech(TextToSpeechRequest)}; mid-stream failures surface via
+ * {@code Subscriber.onError}. Mirrors the OpenAPI {@code Voice} schema. Only {@code name} and {@code type} are guaranteed
+ * by the spec; the descriptive fields are nullable and the {@code languages} list may be empty
+ * (typical for personalized voices). {@link #name()} is the identifier accepted by
+ * {@code TextToSpeechRequest.voice()} — convert with {@code Voice.of(availableVoice.name())}. Named {@code AvailableVoice} (mirroring {@code models.AvailableModel}) because {@link Voice}
+ * is already the request-side voice identifier.
Base URL: https://api.fanar.qa
Request API Access: https://api.fanar.qa/requestRate Limits
\n429 Too Many Requests HTTP status code.\n \n
\n",
+ "description": "You can interact with FanarAPI for seamless chat completion and text processing using Fanar.\n \n \n Model \n Rate Limit \n \n \n Fanar \n 50 requests/minute \n \n \n Fanar-S-1-7B \n 50 requests/minute \n \n \n Fanar-C-1-8.7B \n 50 requests/minute \n \n \n Fanar-C-2-27B \n 50 requests/minute \n \n \n Fanar-Sadiq \n 50 requests/minute \n \n \n Fanar-Sadiq-TTS-1 \n 20 requests/day \n \n \n Fanar-Oryx-IVU-2 \n 20 requests/day \n \n \n Fanar-Aura-TTS-2 \n 20 requests/day \n \n \n Fanar-Aura-STT-1 \n 20 requests/day \n \n \n Fanar-Aura-STT-LF-1 \n 10 requests/day \n \n \n Fanar-Oryx-IG-2 \n 20 requests/day \n \n \n Fanar-Guard-2 \n 50 requests/minute \n \n \n Fanar-Shaheen-MT-1 \n 20 requests/day \n \n \n \nFanar-Diwan \n 50 requests/minute \n
Base URL: https://api.fanar.qa
Request API Access: https://api.fanar.qa/requestRate Limits
\n429 Too Many Requests HTTP status code.\n \n
\n",
"termsOfService": "https://fanar.qa/terms-of-services",
"contact": {
"name": "Fanar Support",
@@ -224,6 +224,23 @@
}
}
},
+ "499": {
+ "description": "Client closed request before completion",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "example": {
+ "error": {
+ "code": "client_closed_request",
+ "message": "Client closed request before completion",
+ "status": 499
+ }
+ }
+ }
+ }
+ },
"500": {
"description": "Internal server error",
"content": {
@@ -300,7 +317,17 @@
{
"lang": "Python - Fanar-Sadiq",
"label": "Python - Fanar-Sadiq",
- "source": "import requests\n\ndef model_api(messages, model):\n headers = {\n \"Authorization\": \"Bearer YOUR_API_KEY_HERE\",\n \"Content-Type\": \"application/json\",\n }\n\n payload = {\n \"model\": model,\n \"messages\": messages,\n \"max_tokens\": 750,\n }\n\n response = requests.post(\n \"https://api.fanar.qa/v1/chat/completions\", \n json=payload, \n headers=headers\n )\n return response.json()\n\nmodel = \"Fanar-Sadiq\"\n\nprompt = \"What are the Islamic values?\"\n\nmessages = [\n {\"role\": \"user\", \"content\": prompt}\n]\n\nresponse = model_api(messages=messages, model=model)\n\n# Extract the assistant's answer\ncontent = response[\"choices\"][0][\"message\"][\"content\"]\nprint(\"Assistant Response:\\n\")\nprint(content)\n\n# Print reference sources if present\nreferences = response[\"choices\"][0][\"message\"].get(\"references\", [])\nif references:\n print(\"\\nReferences:\")\n for ref in references:\n number = ref.get(\"number\", \"-\")\n source = ref.get(\"source\", \"Unknown source\")\n ref_content = ref.get(\"content\", \"\")\n print(f\"\\n[{number}] {source}\\n{ref_content}\")\nelse:\n print(\"\\nNo references returned.\")\n \n"
+ "source": "import requests\n\ndef model_api(messages, model):\n headers = {\n \"Authorization\": \"Bearer YOUR_API_KEY_HERE\",\n \"Content-Type\": \"application/json\",\n }\n\n payload = {\n \"model\": model,\n \"messages\": messages,\n \"max_tokens\": 750,\n }\n\n response = requests.post(\n \"https://api.fanar.qa/v1/chat/completions\", \n json=payload, \n headers=headers\n )\n return response.json()\n\nmodel = \"Fanar-Sadiq\"\n\nprompt = \"What are the Islamic values?\"\n\nmessages = [\n {\"role\": \"user\", \"content\": prompt}\n]\n\nresponse = model_api(messages=messages, model=model)\n\n# Extract the assistant's answer\ncontent = response[\"choices\"][0][\"message\"][\"content\"]\nprint(\"Assistant Response:\\n\")\nprint(content)\n\n# Print reference sources if present\nreferences = response[\"choices\"][0][\"message\"].get(\"references\", [])\nif references:\n print(\"\\nReferences:\")\n for ref in references:\n number = ref.get(\"number\", \"-\")\n source = ref.get(\"source\", \"Unknown source\")\n ref_content = ref.get(\"content\", \"\")\n print(f\"\\n[{number}] {source}\\n{ref_content}\")\nelse:\n print(\"\\nNo references returned.\")\n"
+ },
+ {
+ "lang": "Python - Fanar-Sadiq with persona",
+ "label": "Python - Fanar-Sadiq with persona",
+ "source": "# The \"persona\" parameter controls the assistant's voice and identity.\n# It is only supported for the Fanar-Sadiq model.\n\nimport requests\n\ndef model_api(messages, persona=None):\n headers = {\n \"Authorization\": \"Bearer YOUR_API_KEY_HERE\",\n \"Content-Type\": \"application/json\",\n }\n\n payload = {\n \"model\": \"Fanar-Sadiq\",\n \"messages\": messages,\n \"max_tokens\": 750,\n }\n if persona:\n payload[\"persona\"] = persona\n\n response = requests.post(\n \"https://api.fanar.qa/v1/chat/completions\",\n json=payload,\n headers=headers,\n )\n return response.json()\n\nmessages = [\n {\"role\": \"user\", \"content\": \"What are the Islamic values?\"}\n]\n\n# Customize the assistant's voice/identity for this call.\npersona = \"You are a warm, patient teacher who explains concepts simply for young students.\"\n\nresponse = model_api(messages=messages, persona=persona)\n\ncontent = response[\"choices\"][0][\"message\"][\"content\"]\nprint(\"Assistant Response:\\n\")\nprint(content)\n\n# Print reference sources if present\nreferences = response[\"choices\"][0][\"message\"].get(\"references\", [])\nif references:\n print(\"\\nReferences:\")\n for ref in references:\n number = ref.get(\"number\", \"-\")\n source = ref.get(\"source\", \"Unknown source\")\n ref_content = ref.get(\"content\", \"\")\n print(f\"\\n[{number}] {source}\\n{ref_content}\")\nelse:\n print(\"\\nNo references returned.\")\n"
+ },
+ {
+ "lang": "Python - Fanar-Sadiq-2",
+ "label": "Python - Fanar-Sadiq-2",
+ "source": "# Fanar-Sadiq-2 requires additional authorization and is not allowed by default.\n\nimport requests\n\ndef model_api(messages, madhab=None):\n headers = {\n \"Authorization\": \"Bearer YOUR_API_KEY_HERE\",\n \"Content-Type\": \"application/json\",\n }\n\n payload = {\n \"model\": \"Fanar-Sadiq-2\",\n \"messages\": messages,\n }\n if madhab:\n payload[\"madhab\"] = madhab\n\n response = requests.post(\n \"https://api.fanar.qa/v1/chat/completions\",\n json=payload,\n headers=headers,\n )\n return response.json()\n\nmessages = [\n {\"role\": \"user\", \"content\": \"What are the conditions for Zakat on gold according to the Hanafi school?\"}\n]\n\nresponse = model_api(messages=messages, madhab=[\"hanafi\"])\n\ncontent = response[\"choices\"][0][\"message\"][\"content\"]\nprint(\"Assistant Response:\\n\")\nprint(content)\n\n# Print reference sources if present\nreferences = response[\"choices\"][0][\"message\"].get(\"references\", [])\nif references:\n print(\"\\nReferences:\")\n for ref in references:\n number = ref.get(\"number\", \"-\")\n source = ref.get(\"source\", \"Unknown source\")\n ref_content = ref.get(\"content\", \"\")\n print(f\"\\n[{number}] {source}\\n{ref_content}\")\n"
},
{
"lang": "Python - Thinking mode (Fanar-C-1-8.7B)",
@@ -326,7 +353,7 @@
"Audio"
],
"summary": "Create Speech",
- "description": "This endpoint is compatible with the OpenAI library.\n \n \n Model \n Rate Limit \n \n \n Fanar \n 50 requests/minute \n \n \n Fanar-S-1-7B \n 50 requests/minute \n \n \n Fanar-C-1-8.7B \n 50 requests/minute \n \n \n Fanar-C-2-27B \n 50 requests/minute \n \n \n Fanar-Sadiq \n 50 requests/minute \n \n \n Fanar-Sadiq-2 \n 50 requests/minute \n \n \n Fanar-Sadiq-TTS-1 \n 20 requests/day \n \n \n Fanar-Oryx-IVU-2 \n 20 requests/day \n \n \n Fanar-Aura-TTS-2 \n 20 requests/day \n \n \n Fanar-Aura-STT-1 \n 20 requests/day \n \n \n Fanar-Aura-STT-LF-1 \n 10 requests/day \n \n \n Fanar-Oryx-IG-2 \n 20 requests/day \n \n \n Fanar-Guard-2 \n 50 requests/minute \n \n \n Fanar-Shaheen-MT-1 \n 20 requests/day \n \n \n \nFanar-Diwan \n 50 requests/minute \n
Generates audio from the input text.
This endpoint requires additional authorization and is not allowed by default. Please contact support@fanar.qa.",
+ "description": "This endpoint is compatible with the OpenAI library.
Generates audio from the input text.",
"operationId": "create_speech_v1_audio_speech_post",
"requestBody": {
"content": {
@@ -529,6 +556,23 @@
}
}
},
+ "499": {
+ "description": "Client closed request before completion",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "example": {
+ "error": {
+ "code": "client_closed_request",
+ "message": "Client closed request before completion",
+ "status": 499
+ }
+ }
+ }
+ }
+ },
"500": {
"description": "Internal server error",
"content": {
@@ -605,7 +649,22 @@
{
"lang": "Python - requests for Quranic text",
"label": "Python - requests for Quranic text",
- "source": "# Text-to-Speech requires additional authorization and is not allowed by default.\n\nimport requests\n\nurl = \"https://api.fanar.qa/v1/audio/speech\"\nheaders = {\n \"Authorization\": \"Bearer YOUR_API_KEY\",\n \"Content-Type\": \"application/json\"\n}\ndata = {\n \"model\": \"Fanar-Sadiq-TTS-1\",\n \"input\": \"Quranic text goes here\",\n \"voice\": \"Amelia\",\n \"quran_reciter\": \"abdul-basit\",\n \"response_format\": \"mp3\"\n}\n\nresponse = requests.post(url, headers=headers, json=data)\n\nrevised_input = response.headers.get('X-Revised-Input')\nif revised_input:\n from urllib.parse import unquote\n print(\"Revised Input:\", unquote(revised_input))\n\nwith open(\"quranic_speech.mp3\", \"wb\") as f:\n f.write(response.content)"
+ "source": "# Text-to-Speech requires additional authorization and is not allowed by default.\n\nimport requests\n\nurl = \"https://api.fanar.qa/v1/audio/speech\"\nheaders = {\n \"Authorization\": \"Bearer YOUR_API_KEY\",\n \"Content-Type\": \"application/json\"\n}\ndata = {\n \"model\": \"Fanar-Sadiq-TTS-1\",\n \"input\": \"Quranic text goes here\",\n \"voice\": \"Amelia\",\n \"quran_reciter\": \"abdul-basit\",\n \"response_format\": \"mp3\"\n}\n\nresponse = requests.post(url, headers=headers, json=data)\n\nrevised_input = response.headers.get('X-Revised-Input')\nif revised_input:\n from urllib.parse import unquote\n print(\"Revised Input:\", unquote(revised_input))\n\nwith open(\"quranic_speech.mp3\", \"wb\") as f:\n f.write(response.content)\n"
+ },
+ {
+ "lang": "cURL - streaming",
+ "label": "cURL - streaming",
+ "source": "# Pass \"stream\": true to receive audio bytes progressively as they are\n# synthesized. The response Content-Type is identical to the non-streaming\n# case (audio/wav or audio/mpeg) but uses chunked transfer encoding, so\n# `--output greeting.mp3` keeps working. The first byte arrives much\n# sooner for longer inputs.\n\ncurl -X POST \"https://api.fanar.qa/v1/audio/speech\" \\\n-H \"Content-Type: application/json\" \\\n-H \"Authorization: Bearer YOUR_API_KEY\" \\\n--no-buffer \\\n--output greeting.mp3 \\\n-d '{\n \"model\": \"Fanar-Aura-TTS-2\",\n \"input\": \"Hello! I hope you are having a wonderful day.\",\n \"voice\": \"Amelia\",\n \"response_format\": \"mp3\",\n \"stream\": true\n}'\n"
+ },
+ {
+ "lang": "Python - OpenAI streaming",
+ "label": "Python - OpenAI streaming",
+ "source": "# Text-to-Speech requires additional authorization and is not allowed by default.\n# Use the OpenAI SDK's with_streaming_response helper to play audio as it\n# arrives. `stream: true` is passed via `extra_body` since it's a\n# Fanar-specific extension to the OpenAI-compatible schema.\n\nfrom openai import OpenAI\n\nclient = OpenAI(\n base_url=\"https://api.fanar.qa/v1\",\n api_key=\"YOUR_API_KEY\"\n)\n\nwith client.audio.speech.with_streaming_response.create(\n model=\"Fanar-Aura-TTS-2\",\n input=\"Hello! I hope you are having a wonderful day.\",\n voice=\"Amelia\",\n response_format=\"wav\",\n extra_body={\"stream\": True},\n) as response:\n response.stream_to_file(\"greeting.wav\")\n"
+ },
+ {
+ "lang": "Python - requests streaming",
+ "label": "Python - requests streaming",
+ "source": "# Text-to-Speech requires additional authorization and is not allowed by default.\n# Pass stream=True to requests so it doesn't pre-buffer the body, then\n# iterate over chunks. The first chunk arrives within ~1 s even for long\n# inputs that would otherwise wait many seconds for the full synthesis.\n\nimport requests\n\nurl = \"https://api.fanar.qa/v1/audio/speech\"\nheaders = {\n \"Authorization\": \"Bearer YOUR_API_KEY\",\n \"Content-Type\": \"application/json\"\n}\ndata = {\n \"model\": \"Fanar-Aura-TTS-2\",\n \"input\": \"Hello! I hope you are having a wonderful day.\",\n \"voice\": \"Amelia\",\n \"response_format\": \"wav\",\n \"stream\": True\n}\n\nwith requests.post(url, headers=headers, json=data, stream=True) as r:\n r.raise_for_status()\n with open(\"greeting.wav\", \"wb\") as f:\n for chunk in r.iter_content(chunk_size=8192):\n if chunk:\n f.write(chunk)"
}
]
}
@@ -616,7 +675,7 @@
"Audio"
],
"summary": "Create Transcription",
- "description": "This endpoint is compatible with the OpenAI library.
Transcribes audio into the input language.
This endpoint requires additional authorization and is not allowed by default. Please contact support@fanar.qa.",
+ "description": "This endpoint is compatible with the OpenAI library.
Transcribes audio into the input language.",
"operationId": "create_transcription_v1_audio_transcriptions_post",
"requestBody": {
"content": {
@@ -805,6 +864,23 @@
}
}
},
+ "499": {
+ "description": "Client closed request before completion",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "example": {
+ "error": {
+ "code": "client_closed_request",
+ "message": "Client closed request before completion",
+ "status": 499
+ }
+ }
+ }
+ }
+ },
"500": {
"description": "Internal server error",
"content": {
@@ -892,11 +968,11 @@
"Audio"
],
"summary": "List Voices",
- "description": "Lists the personalized voices.
This endpoint requires additional authorization and is not allowed by default. Please contact support@fanar.qa.",
+ "description": "Lists all available text-to-speech voices.
The list will include all built-in (public) voices by default. If your API key is authorized to create personalized voices, they will be included in the list and labeled as type: \"personal\".",
"operationId": "list_voices_v1_audio_voices_get",
"responses": {
"200": {
- "description": "A list of personalized voices.",
+ "description": "A list of available voices.",
"content": {
"application/json": {
"schema": {
@@ -904,8 +980,34 @@
},
"example": {
"voices": [
- "Jenny",
- "Dora"
+ {
+ "name": "Amelia",
+ "name_ar": "أميليا",
+ "gender": "Female",
+ "accent": "British",
+ "languages": [
+ "en"
+ ],
+ "type": "public",
+ "emotion": false
+ },
+ {
+ "name": "Hamad",
+ "name_ar": "حمد",
+ "gender": "Male",
+ "accent": "Gulf",
+ "languages": [
+ "ar"
+ ],
+ "type": "public",
+ "emotion": false
+ },
+ {
+ "name": "MyVoice",
+ "languages": [],
+ "type": "personal",
+ "emotion": false
+ }
]
}
}
@@ -1066,6 +1168,23 @@
}
}
},
+ "499": {
+ "description": "Client closed request before completion",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "example": {
+ "error": {
+ "code": "client_closed_request",
+ "message": "Client closed request before completion",
+ "status": 499
+ }
+ }
+ }
+ }
+ },
"500": {
"description": "Internal server error",
"content": {
@@ -1141,7 +1260,7 @@
"Audio"
],
"summary": "Create Voice",
- "description": "Create a personalized voice that can be used to generate speech.
This endpoint requires additional authorization and is not allowed by default. Please contact support@fanar.qa.",
+ "description": "Creates a personalized voice that can be used to generate speech.
The voice name must be unique among your own personalized voices. Names that match a built-in (public) voice are allowed — public voices and your personalized voices live in separate namespaces. When you request a voice name that exists in both, your personalized voice takes precedence at synthesis time in POST /v1/audio/speech.
This endpoint requires additional authorization and is not allowed by default. Please contact support@fanar.qa.",
"operationId": "create_voice_v1_audio_voices_post",
"requestBody": {
"content": {
@@ -1317,6 +1436,23 @@
}
}
},
+ "499": {
+ "description": "Client closed request before completion",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "example": {
+ "error": {
+ "code": "client_closed_request",
+ "message": "Client closed request before completion",
+ "status": 499
+ }
+ }
+ }
+ }
+ },
"500": {
"description": "Internal server error",
"content": {
@@ -1576,6 +1712,23 @@
}
}
},
+ "499": {
+ "description": "Client closed request before completion",
+ "content": {
+ "application/json": {
+ "example": {
+ "error": {
+ "code": "client_closed_request",
+ "message": "Client closed request before completion",
+ "status": 499
+ }
+ },
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ }
+ }
+ }
+ },
"500": {
"description": "Internal server error",
"content": {
@@ -1648,7 +1801,7 @@
"Images"
],
"summary": "Create Image",
- "description": "This endpoint is compatible with the OpenAI library.
Creates an image given a prompt.
This endpoint requires additional authorization and is not allowed by default. Please contact support@fanar.qa.",
+ "description": "This endpoint is compatible with the OpenAI library.
Creates an image given a prompt.",
"operationId": "create_image_v1_images_generations_post",
"requestBody": {
"content": {
@@ -1826,6 +1979,23 @@
}
}
},
+ "499": {
+ "description": "Client closed request before completion",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "example": {
+ "error": {
+ "code": "client_closed_request",
+ "message": "Client closed request before completion",
+ "status": 499
+ }
+ }
+ }
+ }
+ },
"500": {
"description": "Internal server error",
"content": {
@@ -1908,7 +2078,7 @@
"Translations"
],
"summary": "Translate",
- "description": "Translate the given text into the specified language.
This endpoint requires additional authorization and is not allowed by default. Please contact support@fanar.qa.",
+ "description": "Translate the given text into the specified language.",
"operationId": "translate_v1_translations_post",
"requestBody": {
"content": {
@@ -2086,6 +2256,23 @@
}
}
},
+ "499": {
+ "description": "Client closed request before completion",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "example": {
+ "error": {
+ "code": "client_closed_request",
+ "message": "Client closed request before completion",
+ "status": 499
+ }
+ }
+ }
+ }
+ },
"500": {
"description": "Internal server error",
"content": {
@@ -2163,7 +2350,7 @@
"Poems"
],
"summary": "Create Poem",
- "description": "This endpoint is compatible with the OpenAI library.
Creates a poem given a prompt.
This endpoint requires additional authorization and is not allowed by default. Please contact support@fanar.qa.",
+ "description": "This endpoint is compatible with the OpenAI library.
Creates a poem given a prompt.",
"operationId": "create_poem_v1_poems_generations_post",
"requestBody": {
"content": {
@@ -2341,6 +2528,23 @@
}
}
},
+ "499": {
+ "description": "Client closed request before completion",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "example": {
+ "error": {
+ "code": "client_closed_request",
+ "message": "Client closed request before completion",
+ "status": 499
+ }
+ }
+ }
+ }
+ },
"500": {
"description": "Internal server error",
"content": {
@@ -2577,6 +2781,23 @@
}
}
},
+ "499": {
+ "description": "Client closed request before completion",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "example": {
+ "error": {
+ "code": "client_closed_request",
+ "message": "Client closed request before completion",
+ "status": 499
+ }
+ }
+ }
+ }
+ },
"500": {
"description": "Internal server error",
"content": {
@@ -2812,6 +3033,23 @@
}
}
},
+ "499": {
+ "description": "Client closed request before completion",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "example": {
+ "error": {
+ "code": "client_closed_request",
+ "message": "Client closed request before completion",
+ "status": 499
+ }
+ }
+ }
+ }
+ },
"500": {
"description": "Internal server error",
"content": {
@@ -3057,6 +3295,23 @@
}
}
},
+ "499": {
+ "description": "Client closed request before completion",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Error"
+ },
+ "example": {
+ "error": {
+ "code": "client_closed_request",
+ "message": "Client closed request before completion",
+ "status": 499
+ }
+ }
+ }
+ }
+ },
"500": {
"description": "Internal server error",
"content": {
@@ -4239,6 +4494,7 @@
"Fanar-C-1-8.7B",
"Fanar-C-2-27B",
"Fanar-Sadiq",
+ "Fanar-Sadiq-2",
"Fanar-Oryx-IVU-2"
],
"title": "ChatCompletionLLM"
@@ -4270,7 +4526,7 @@
},
"model": {
"$ref": "#/components/schemas/ChatCompletionLLM",
- "description": "The model to use for the completion. For the Fanar-Sadiq model, the following LLM parameters are not used.
Islamic-RAG is replaced with Fanar-Sadiq and will be removed soon. Please use Fanar-Sadiq instead."
+ "description": "The model to use for the completion. For the Fanar-Sadiq and Fanar-Sadiq-2 models, the following LLM parameters are not used."
},
"enable_thinking": {
"anyOf": [
@@ -4670,6 +4926,21 @@
"title": "Filter Sources",
"description": "List of sources to filter from the Fanar-Sadiq model. Accepts predefined source names or values starting with 'digital_seerah'."
},
+ "madhab": {
+ "anyOf": [
+ {
+ "items": {
+ "$ref": "#/components/schemas/MadhabEnum"
+ },
+ "type": "array"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Madhab",
+ "description": "List of madhab (Islamic school of thought) to filter by for the Fanar-Sadiq-2 model."
+ },
"restrict_to_islamic": {
"anyOf": [
{
@@ -4682,6 +4953,19 @@
"title": "Restrict To Islamic",
"description": "When enabled for Fanar-Sadiq model, only Islamic content prompts will be accepted. Non-Islamic content will be rejected.",
"default": false
+ },
+ "persona": {
+ "anyOf": [
+ {
+ "type": "string",
+ "maxLength": 2000
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Persona",
+ "description": "Custom persona that controls the assistant's voice and identity for the Fanar-Sadiq model. Free-form text; only supported for Fanar-Sadiq."
}
},
"type": "object",
@@ -5229,7 +5513,8 @@
"unprocessable",
"conflict",
"Not found",
- "no_longer_supported"
+ "no_longer_supported",
+ "client_closed_request"
],
"title": "ErrorCode"
},
@@ -5257,7 +5542,8 @@
422,
409,
404,
- 410
+ 410,
+ 499
],
"title": "ErrorStatus"
},
@@ -5274,11 +5560,13 @@
},
"revised": {
"type": "boolean",
- "title": "Revised"
+ "title": "Revised",
+ "description": "Indicates whether the prompt was revised before generation."
},
"revised_prompt": {
"type": "string",
- "title": "Revised Prompt"
+ "title": "Revised Prompt",
+ "description": "The prompt used for generation, which may be revised from the original prompt."
}
},
"type": "object",
@@ -5296,11 +5584,23 @@
"type": "string",
"title": "B64 Json",
"description": "The base64-encoded JSON of the generated image."
+ },
+ "revised": {
+ "type": "boolean",
+ "title": "Revised",
+ "description": "Indicates whether the prompt was revised before generation."
+ },
+ "revised_prompt": {
+ "type": "string",
+ "title": "Revised Prompt",
+ "description": "The prompt used for generation, which may be revised from the original prompt."
}
},
"type": "object",
"required": [
- "b64_json"
+ "b64_json",
+ "revised",
+ "revised_prompt"
],
"title": "ImageGenerationItem"
},
@@ -5321,6 +5621,12 @@
"type": "string",
"title": "Prompt",
"description": "A text description of the desired image."
+ },
+ "revise": {
+ "type": "boolean",
+ "title": "Revise",
+ "description": "Whether to automatically revise the prompt to enhance style, quality, and cultural alignment for improved generation results.",
+ "default": true
}
},
"type": "object",
@@ -5394,6 +5700,17 @@
],
"title": "LLM"
},
+ "MadhabEnum": {
+ "type": "string",
+ "enum": [
+ "all",
+ "hanafi",
+ "maliki",
+ "shafii",
+ "hanbali"
+ ],
+ "title": "MadhabEnum"
+ },
"ModelObject": {
"type": "string",
"enum": [
@@ -5769,8 +6086,9 @@
"voice": {
"type": "string",
"title": "Voice",
- "description": "The voice to use for the text-to-speech. Details are below:\n| **Voice** | Gender | Accent | Supported Languages |\n|----------------------|--------|----------|---------------------|\n| Amelia | Female | British | [English](https://chat.fanar.qa/api/sample-voices/en/Amelia) |\n| Emily | Female | American | [English](https://chat.fanar.qa/api/sample-voices/en/Emily) |\n| Hamad | Male | Gulf | [Arabic](https://chat.fanar.qa/api/sample-voices/ar/Hamad) |\n| Harry | Male | British | [English](https://chat.fanar.qa/api/sample-voices/en/Harry) |\n| Huda | Female | Standard | [Arabic](https://chat.fanar.qa/api/sample-voices/ar/Huda) |\n| Jake | Male | American | [English](https://chat.fanar.qa/api/sample-voices/en/Jake) |\n| Jasim | Male | Gulf | [Arabic](https://chat.fanar.qa/api/sample-voices/ar/Jasim) |\n| Noor | Female | Standard | [Arabic](https://chat.fanar.qa/api/sample-voices/ar/Noor) |\n",
+ "description": "The voice to use for the text-to-speech. Details are below:\n
",
"enum": [
+ "Abdulrahman",
"Amelia",
"Emily",
"Hamad",
@@ -5778,7 +6096,8 @@
"Huda",
"Jake",
"Jasim",
- "Noor"
+ "Noor",
+ "Radwa"
]
},
"response_format": {
@@ -5790,6 +6109,18 @@
"$ref": "#/components/schemas/QuranReciters",
"description": "The Quran reciter to use when using Fanar-Sadiq-TTS-1 model for Quranic text.",
"default": "abdul-basit"
+ },
+ "with_emotion": {
+ "type": "boolean",
+ "title": "With Emotion",
+ "description": "Enable emotional speech synthesis. **Only applicable to `Fanar-Aura-TTS-2` and to voices where `emotion: true` in GET /v1/voices.** When the selected voice does not support emotion, or when used with `Fanar-Sadiq-TTS-1`, the request is rejected with a 422 error. Defaults to false.",
+ "default": false
+ },
+ "stream": {
+ "type": "boolean",
+ "title": "Stream",
+ "description": "Stream the audio as it is generated. Supported for both `wav` and `mp3`.",
+ "default": false
}
},
"type": "object",
@@ -5995,22 +6326,162 @@
],
"title": "VideoURL"
},
+ "Voice": {
+ "properties": {
+ "name": {
+ "type": "string",
+ "title": "Name",
+ "description": "The English name of the voice. This is the identifier passed to the TTS endpoint.",
+ "examples": [
+ "Amelia"
+ ]
+ },
+ "name_ar": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Name Ar",
+ "description": "The Arabic display name of the voice, when available.",
+ "examples": [
+ "أميليا"
+ ]
+ },
+ "gender": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Gender",
+ "description": "Gender label of the voice (e.g., 'Male', 'Female').",
+ "examples": [
+ "Female"
+ ]
+ },
+ "accent": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "title": "Accent",
+ "description": "Accent label of the voice (e.g., 'British', 'Gulf', 'American', 'Standard').",
+ "examples": [
+ "British"
+ ]
+ },
+ "languages": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "title": "Languages",
+ "description": "Supported language codes (e.g., 'en', 'ar').",
+ "examples": [
+ [
+ "en"
+ ]
+ ]
+ },
+ "type": {
+ "type": "string",
+ "enum": [
+ "public",
+ "personal"
+ ],
+ "title": "Type",
+ "description": "Whether this is a built-in public voice or a personalized voice registered for this API key.",
+ "examples": [
+ "public"
+ ]
+ },
+ "emotion": {
+ "type": "boolean",
+ "title": "Emotion",
+ "description": "Whether this voice supports emotional speech synthesis. When true, you may set `with_emotion: true` on POST /v1/audio/speech to enable emotional rendering.",
+ "default": false,
+ "examples": [
+ true
+ ]
+ }
+ },
+ "type": "object",
+ "required": [
+ "name",
+ "type"
+ ],
+ "title": "Voice",
+ "example": {
+ "accent": "British",
+ "emotion": false,
+ "gender": "Female",
+ "languages": [
+ "en"
+ ],
+ "name": "Amelia",
+ "name_ar": "أميليا",
+ "type": "public"
+ }
+ },
"VoiceResponse": {
"properties": {
"voices": {
"items": {
- "type": "string"
+ "$ref": "#/components/schemas/Voice"
},
"type": "array",
"title": "Voices",
- "description": "A list of personalized voices."
+ "description": "Available voices. Always includes the built-in public voices. Includes personalized voices registered for this API key when voice personalization is authorized."
}
},
"type": "object",
"required": [
"voices"
],
- "title": "VoiceResponse"
+ "title": "VoiceResponse",
+ "example": {
+ "voices": [
+ {
+ "accent": "British",
+ "emotion": false,
+ "gender": "Female",
+ "languages": [
+ "en"
+ ],
+ "name": "Amelia",
+ "name_ar": "أميليا",
+ "type": "public"
+ },
+ {
+ "accent": "Gulf",
+ "emotion": false,
+ "gender": "Male",
+ "languages": [
+ "ar"
+ ],
+ "name": "Hamad",
+ "name_ar": "حمد",
+ "type": "public"
+ },
+ {
+ "emotion": false,
+ "languages": [],
+ "name": "MyVoice",
+ "type": "personal"
+ }
+ ]
+ }
},
"TokenChunk": {
"properties": {
@@ -6654,4 +7125,4 @@
"description": "All API endpoints require a **Bearer** token. Include it in the `Authorization` header:\n\n```\nAuthorization: Bearer YOUR_API_KEY\n```"
}
]
-}
\ No newline at end of file
+}
diff --git a/api-spec/openapi.yaml b/api-spec/openapi.yaml
new file mode 100644
index 0000000..ac66e99
--- /dev/null
+++ b/api-spec/openapi.yaml
@@ -0,0 +1,6043 @@
+openapi: 3.1.0
+info:
+ title: Fanar API
+ description: >
+ You can interact with FanarAPI for seamless chat completion and text
+ processing using Fanar.Voice Gender Supported Languages Accent Emotion Abdulrahman Male Arabic Standard ✓ Amelia Female English British — Emily Female English American — Hamad Male Arabic Standard — Harry Male English British — Huda Female Arabic Standard — Jake Male English American — Jasim Male Arabic Standard — Noor Female Arabic Standard — Radwa Female Arabic Standard ✓
Base URL:
+ https://api.fanar.qa
Request API Access: https://api.fanar.qa/requestRate Limits
+
+ 429 Too Many Requests HTTP status code.
+
+
+ termsOfService: https://fanar.qa/terms-of-services
+ contact:
+ name: Fanar Support
+ url: https://fanar.qa/
+ email: support@fanar.qa
+ version: 1.0.0
+ x-logo:
+ url: /static/white-logo.svg
+ alt: logo
+paths:
+ /v1/chat/completions:
+ post:
+ tags:
+ - Chat
+ summary: Create Chat Completion
+ description: This endpoint is compatible with the OpenAI library. If certain
+ parameters are not supported by the OpenAI library, they can be provided
+ in the `extra_body` field of the OpenAI request.
+
+
+ Model
+ Rate Limit
+
+
+ Fanar
+ 50 requests/minute
+
+
+ Fanar-S-1-7B
+ 50 requests/minute
+
+
+ Fanar-C-1-8.7B
+ 50 requests/minute
+
+
+ Fanar-C-2-27B
+ 50 requests/minute
+
+
+ Fanar-Sadiq
+ 50 requests/minute
+
+
+ Fanar-Sadiq-2
+ 50 requests/minute
+
+
+ Fanar-Sadiq-TTS-1
+ 20 requests/day
+
+
+ Fanar-Oryx-IVU-2
+ 20 requests/day
+
+
+ Fanar-Aura-TTS-2
+ 20 requests/day
+
+
+ Fanar-Aura-STT-1
+ 20 requests/day
+
+
+ Fanar-Aura-STT-LF-1
+ 10 requests/day
+
+
+ Fanar-Oryx-IG-2
+ 20 requests/day
+
+
+ Fanar-Guard-2
+ 50 requests/minute
+
+
+ Fanar-Shaheen-MT-1
+ 20 requests/day
+
+
+
+ Fanar-Diwan
+ 50 requests/minute
+
When creating the
+ OpenAI object, set the `base_url` as the API domain followed by `/v1`.
+ operationId: create_chat_completion_v1_chat_completions_post
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ChatCompletionRequest"
+ required: true
+ responses:
+ "200":
+ description: Chat completion response
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ChatCompletionResponse"
+ text/event-stream:
+ schema:
+ oneOf:
+ - $ref: "#/components/schemas/TokenChunk"
+ - $ref: "#/components/schemas/ToolCallChunk"
+ - $ref: "#/components/schemas/ToolResultChunk"
+ - $ref: "#/components/schemas/ProgressChunk"
+ - $ref: "#/components/schemas/DoneChunk"
+ - $ref: "#/components/schemas/ErrorChunk"
+ "400":
+ description: The content was filtered
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: content_filter
+ message: The content was filtered
+ status: 400
+ param: prompt
+ type: safety
+ "401":
+ description: Invalid authentication
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authentication
+ message: Invalid authentication
+ status: 401
+ "403":
+ description: Invalid authorization
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authorization
+ message: Invalid authorization
+ status: 403
+ "404":
+ description: Not found
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: Not found
+ message: Not found
+ status: 404
+ "409":
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: conflict
+ message: Conflict
+ status: 409
+ "410":
+ description: No longer supported
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: no_longer_supported
+ message: No longer supported
+ status: 410
+ "413":
+ description: Request entity too large
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: too_large
+ message: Request entity too large
+ status: 413
+ "422":
+ description: Unprocessable
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: unprocessable
+ message: Unprocessable
+ status: 422
+ "429":
+ description: Rate limit reached or Exceeded quota
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: exceeded_quota
+ message: Exceeded quota
+ status: 429
+ "499":
+ description: Client closed request before completion
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: client_closed_request
+ message: Client closed request before completion
+ status: 499
+ "500":
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: internal_server_error
+ message: Internal server error
+ status: 500
+ "503":
+ description: Service overloaded
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: overloaded
+ message: Service overloaded
+ status: 503
+ "504":
+ description: Request timed out
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: timeout
+ message: Request timed out
+ status: 504
+ security:
+ - Bearer: []
+ x-codeSamples:
+ - lang: Curl
+ label: cURL
+ source: |
+ curl -X POST "https://api.fanar.qa/v1/chat/completions" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer YOUR_API_KEY_HERE" \
+ -d '{
+ "model": "Fanar",
+ "messages": [
+ {
+ "role": "user",
+ "content": "Your message here"
+ }
+ ]
+ }'
+ - lang: Python
+ label: Python - OpenAI
+ source: |
+ from openai import OpenAI
+
+ client = OpenAI(
+ base_url="https://api.fanar.qa/v1",
+ api_key="YOUR_API_KEY_HERE",
+ )
+
+ model_name = "Fanar"
+ messages = [
+ {"role": "user", "content": "Your message here"}
+ ]
+
+ response = client.chat.completions.create(
+ model=model_name,
+ messages=messages,
+ )
+
+ print("Assistant Response:\n")
+ print(response.choices[0].message.content)
+ - lang: Python - OpenAI Stream
+ label: Python - OpenAI Stream
+ source: |
+ from openai import AsyncOpenAI
+
+ client = AsyncOpenAI(
+ base_url="https://api.fanar.qa/v1",
+ api_key="YOUR_API_KEY_HERE",
+ )
+
+ messages = [
+ {"role": "user", "content": "Your message here"}
+ ]
+
+ stream = await client.chat.completions.create(
+ model="Fanar",
+ messages=messages,
+ stream=True
+ )
+
+ print("Assistant Response:\n")
+
+ content = ""
+ references = None
+
+ async for chunk in stream:
+ if chunk.choices and chunk.choices[0].delta:
+ delta = chunk.choices[0].delta
+
+ # Capture references from the first chunk
+ if hasattr(delta, 'references') and delta.references:
+ references = delta.references
+
+ # Stream content as it arrives
+ if delta.content:
+ content += delta.content
+ print(delta.content, end="", flush=True)
+
+ print("\n")
+
+ # Print references if present
+ if references:
+ print("\nReferences:")
+ for ref in references:
+ number = ref.get("number", "-")
+ source = ref.get("source", "Unknown source")
+ ref_content = ref.get("content", "")
+ print(f"\n[{number}] {source}\n{ref_content}")
+ - lang: Python - Fanar-Sadiq
+ label: Python - Fanar-Sadiq
+ source: |
+ import requests
+
+ def model_api(messages, model):
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY_HERE",
+ "Content-Type": "application/json",
+ }
+
+ payload = {
+ "model": model,
+ "messages": messages,
+ "max_tokens": 750,
+ }
+
+ response = requests.post(
+ "https://api.fanar.qa/v1/chat/completions",
+ json=payload,
+ headers=headers
+ )
+ return response.json()
+
+ model = "Fanar-Sadiq"
+
+ prompt = "What are the Islamic values?"
+
+ messages = [
+ {"role": "user", "content": prompt}
+ ]
+
+ response = model_api(messages=messages, model=model)
+
+ # Extract the assistant's answer
+ content = response["choices"][0]["message"]["content"]
+ print("Assistant Response:\n")
+ print(content)
+
+ # Print reference sources if present
+ references = response["choices"][0]["message"].get("references", [])
+ if references:
+ print("\nReferences:")
+ for ref in references:
+ number = ref.get("number", "-")
+ source = ref.get("source", "Unknown source")
+ ref_content = ref.get("content", "")
+ print(f"\n[{number}] {source}\n{ref_content}")
+ else:
+ print("\nNo references returned.")
+ - lang: Python - Fanar-Sadiq with persona
+ label: Python - Fanar-Sadiq with persona
+ source: >
+ # The "persona" parameter controls the assistant's voice and
+ identity.
+
+ # It is only supported for the Fanar-Sadiq model.
+
+
+ import requests
+
+
+ def model_api(messages, persona=None):
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY_HERE",
+ "Content-Type": "application/json",
+ }
+
+ payload = {
+ "model": "Fanar-Sadiq",
+ "messages": messages,
+ "max_tokens": 750,
+ }
+ if persona:
+ payload["persona"] = persona
+
+ response = requests.post(
+ "https://api.fanar.qa/v1/chat/completions",
+ json=payload,
+ headers=headers,
+ )
+ return response.json()
+
+ messages = [
+ {"role": "user", "content": "What are the Islamic values?"}
+ ]
+
+
+ # Customize the assistant's voice/identity for this call.
+
+ persona = "You are a warm, patient teacher who explains concepts
+ simply for young students."
+
+
+ response = model_api(messages=messages, persona=persona)
+
+
+ content = response["choices"][0]["message"]["content"]
+
+ print("Assistant Response:\n")
+
+ print(content)
+
+
+ # Print reference sources if present
+
+ references = response["choices"][0]["message"].get("references", [])
+
+ if references:
+ print("\nReferences:")
+ for ref in references:
+ number = ref.get("number", "-")
+ source = ref.get("source", "Unknown source")
+ ref_content = ref.get("content", "")
+ print(f"\n[{number}] {source}\n{ref_content}")
+ else:
+ print("\nNo references returned.")
+ - lang: Python - Fanar-Sadiq-2
+ label: Python - Fanar-Sadiq-2
+ source: >
+ # Fanar-Sadiq-2 requires additional authorization and is not allowed
+ by default.
+
+
+ import requests
+
+
+ def model_api(messages, madhab=None):
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY_HERE",
+ "Content-Type": "application/json",
+ }
+
+ payload = {
+ "model": "Fanar-Sadiq-2",
+ "messages": messages,
+ }
+ if madhab:
+ payload["madhab"] = madhab
+
+ response = requests.post(
+ "https://api.fanar.qa/v1/chat/completions",
+ json=payload,
+ headers=headers,
+ )
+ return response.json()
+
+ messages = [
+ {"role": "user", "content": "What are the conditions for Zakat on gold according to the Hanafi school?"}
+ ]
+
+
+ response = model_api(messages=messages, madhab=["hanafi"])
+
+
+ content = response["choices"][0]["message"]["content"]
+
+ print("Assistant Response:\n")
+
+ print(content)
+
+
+ # Print reference sources if present
+
+ references = response["choices"][0]["message"].get("references", [])
+
+ if references:
+ print("\nReferences:")
+ for ref in references:
+ number = ref.get("number", "-")
+ source = ref.get("source", "Unknown source")
+ ref_content = ref.get("content", "")
+ print(f"\n[{number}] {source}\n{ref_content}")
+ - lang: Python - Thinking mode (Fanar-C-1-8.7B)
+ label: Python - Thinking mode (Fanar-C-1-8.7B)
+ source: >
+ # Thinking mode requires additional authorization and is not allowed
+ by default.
+
+
+ import requests
+
+
+ def model_api(messages, max_tokens=500):
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY_HERE",
+ "Content-Type": "application/json",
+ }
+
+ payload = {
+ "model": "Fanar-C-1-8.7B",
+ "messages": messages,
+ "max_tokens": max_tokens,
+ }
+
+ response = requests.post(
+ "https://api.fanar.qa/v1/chat/completions",
+ json=payload,
+ headers=headers
+ )
+ return response.json()
+
+ # Step 1: User message with "thinking_user" role
+
+ messages = [
+ {"role": "thinking_user", "content": user_input}
+ ]
+
+
+ # Step 2: Send request to model with extended max tokens
+
+ response = model_api(
+ messages=messages,
+ max_tokens=2000,
+ )
+
+
+ output = response["choices"][0]["message"]["content"]
+
+ finish_reason = response["choices"][0]["finish_reason"]
+
+
+ # Step 3: Check if thinking mode continuation is needed
+
+ has_think_tag = "" in output
+
+ hit_length_limit = finish_reason == "length"
+
+
+ if has_think_tag or hit_length_limit:
+ # Extract thinking output
+ thinking_output = output.split("")[0] if has_think_tag else output
+
+ # Modify the last "thinking_user" role message to "user"
+ for msg in reversed(messages):
+ if msg["role"] == "thinking_user":
+ msg["role"] = "user"
+ break
+
+ # Add new "thinking" role message with extracted output
+ messages.append({"role": "thinking", "content": thinking_output})
+
+ # Re-run model with updated messages, shorter max tokens
+ final_response = model_api(
+ messages=messages,
+ max_tokens=1000,
+ )
+
+ final_output = final_response["choices"][0]["message"]["content"]
+ else:
+ final_output = output
+
+ # final_output now contains the full response after handling
+ thinking mode
+
+ print("Assistant Response:\n")
+
+ print(final_output)
+ - lang: Python - Thinking mode (Fanar-C-2-27B)
+ label: Python - Thinking mode (Fanar-C-2-27B)
+ source: >
+ # Thinking mode requires additional authorization and is not allowed
+ by default.
+
+
+ import requests
+
+
+ def model_api(messages, max_tokens=4000):
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY_HERE",
+ "Content-Type": "application/json",
+ }
+
+ payload = {
+ "model": "Fanar-C-2-27B",
+ "messages": messages,
+ "max_tokens": max_tokens,
+ "enable_thinking": True
+ }
+
+ response = requests.post(
+ "https://api.fanar.qa/v1/chat/completions",
+ json=payload,
+ headers=headers
+ )
+ return response.json()
+
+ messages = [
+ {"role": "user", "content": user_input}
+ ]
+
+
+ response = model_api(
+ messages=messages
+ )
+
+
+ print("Assistant Response:\n")
+
+ print(response)
+ - lang: Python - Image understanding
+ label: Python - Image understanding
+ source: >
+ # Image understanding requires additional authorization and is not
+ allowed by default.
+
+
+ import requests
+
+ import base64
+
+
+ def model_api(messages):
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY_HERE",
+ "Content-Type": "application/json",
+ }
+
+ payload = {
+ "model": "Fanar-Oryx-IVU-2",
+ "messages": messages,
+ "max_tokens": 750,
+ }
+
+ response = requests.post("https://api.fanar.qa/v1/chat/completions", json=payload, headers=headers)
+ return response.json()
+
+ with open("path/to/image.jpg", "rb") as image_file:
+ raw_b64 = base64.b64encode(image_file.read()).decode("utf-8")
+
+ image_b64_url = f"data:image/jpeg;base64,{raw_b64}"
+
+
+ prompt = "Tell me about this image."
+
+
+ messages = [
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "text",
+ "text": prompt
+ },
+ {
+ "type": "image_url",
+ "image_url": {
+ "url": image_b64_url
+ }
+ }
+ ]
+ }
+ ]
+
+
+ response = model_api(messages=messages)
+
+
+ content = response["choices"][0]["message"]["content"]
+
+ print("Assistant Response:\n")
+
+ print(content)
+ /v1/audio/speech:
+ post:
+ tags:
+ - Audio
+ summary: Create Speech
+ description: This endpoint is compatible with the OpenAI library.
Generates
+ audio from the input text.
+ operationId: create_speech_v1_audio_speech_post
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/TextToSpeechRequest"
+ required: true
+ responses:
+ "200":
+ description: The audio file content or error details.
+ headers:
+ X-Id:
+ description: A unique identifier for the text-to-speech.
+ schema:
+ type: string
+ format: uuid
+ X-Revised-Input:
+ description: The processed input text after Quran validation and tagging. This
+ header is only present when using the Fanar-Sadiq-TTS model. The
+ validator identifies Quranic verses in the input and wraps them
+ with XML-style tags (e.g., `
Transcribes audio into the input language.
+ operationId: create_transcription_v1_audio_transcriptions_post
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ $ref: "#/components/schemas/Body_create_transcription_v1_audio_transcriptions_p\
+ ost"
+ required: true
+ responses:
+ "200":
+ description: Successful Response
+ content:
+ application/json:
+ schema:
+ anyOf:
+ - $ref: "#/components/schemas/SpeechToTextResponseWithText"
+ - $ref: "#/components/schemas/SpeechToTextResponseWithSRT"
+ - $ref: "#/components/schemas/SpeechToTextResponseWithJson-Output"
+ title: Response Create Transcription V1 Audio Transcriptions Post
+ "400":
+ description: The content was filtered
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: content_filter
+ message: The content was filtered
+ status: 400
+ param: prompt
+ type: safety
+ "401":
+ description: Invalid authentication
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authentication
+ message: Invalid authentication
+ status: 401
+ "403":
+ description: Invalid authorization
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authorization
+ message: Invalid authorization
+ status: 403
+ "404":
+ description: Not found
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: Not found
+ message: Not found
+ status: 404
+ "409":
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: conflict
+ message: Conflict
+ status: 409
+ "410":
+ description: No longer supported
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: no_longer_supported
+ message: No longer supported
+ status: 410
+ "413":
+ description: Request entity too large
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: too_large
+ message: Request entity too large
+ status: 413
+ "422":
+ description: Unprocessable
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: unprocessable
+ message: Unprocessable
+ status: 422
+ "429":
+ description: Rate limit reached or Exceeded quota
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: exceeded_quota
+ message: Exceeded quota
+ status: 429
+ "499":
+ description: Client closed request before completion
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: client_closed_request
+ message: Client closed request before completion
+ status: 499
+ "500":
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: internal_server_error
+ message: Internal server error
+ status: 500
+ "503":
+ description: Service overloaded
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: overloaded
+ message: Service overloaded
+ status: 503
+ "504":
+ description: Request timed out
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: timeout
+ message: Request timed out
+ status: 504
+ security:
+ - Bearer: []
+ x-codeSamples:
+ - lang: Curl
+ label: cURL Example
+ source: |
+ curl -X POST "https://api.fanar.qa/v1/audio/transcriptions" \
+ -H "Authorization: Bearer YOUR_API_KEY" \
+ -H "Content-Type: multipart/form-data" \
+ -F "file=@sample.wav" \
+ -F "model=Fanar-Aura-STT-1"
+ - lang: Python
+ label: Python - OpenAI
+ source: >
+ # Audio Transcriptions requires additional authorization and is not
+ allowed by default.
+
+
+ from openai import OpenAI
+
+
+ client = OpenAI(
+ base_url="https://api.fanar.qa/v1",
+ api_key="YOUR_API_KEY"
+ )
+
+
+ with open("sample.wav", "rb") as f:
+ response = client.audio.transcriptions.create(
+ file=f,
+ model="Fanar-Aura-STT-1"
+ )
+
+ print(response.text)
+ - lang: Python - requests
+ label: Python - requests
+ source: >
+ # Audio Transcriptions requires additional authorization and is not
+ allowed by default.
+
+
+ import requests
+
+
+ url = "https://api.fanar.qa/v1/audio/transcriptions"
+
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY"
+ }
+
+ files = {
+ "file": open("sample.wav", "rb")
+ }
+
+ data = {
+ "model": "Fanar-Aura-STT-1"
+ }
+
+
+ response = requests.post(url, headers=headers, files=files,
+ data=data)
+
+
+ print(response.json().get("text"))
+ - lang: Python - requests with longform audio (JSON format)
+ label: Python - requests with longform audio (JSON format)
+ source: >-
+ # Audio Transcriptions requires additional authorization and is not
+ allowed by default.
+
+
+ import requests
+
+
+ url = "https://api.fanar.qa/v1/audio/transcriptions"
+
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY"
+ }
+
+ files = {
+ "file": open("sample.wav", "rb")
+ }
+
+ data = {
+ "model": "Fanar-Aura-STT-LF-1",
+ "format": "json"
+ }
+
+
+ response = requests.post(url, headers=headers, files=files,
+ data=data)
+
+
+ print(response.json().get("json"))
+ /v1/audio/voices:
+ get:
+ tags:
+ - Audio
+ summary: List Voices
+ description: 'Lists all available text-to-speech voices.
The list will
+ include all built-in (public) voices by default. If your API key is
+ authorized to create
+ personalized voices, they will be included in the list and labeled
+ as type: "personal".'
+ operationId: list_voices_v1_audio_voices_get
+ responses:
+ "200":
+ description: A list of available voices.
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/VoiceResponse"
+ example:
+ voices:
+ - name: Amelia
+ name_ar: أميليا
+ gender: Female
+ accent: British
+ languages:
+ - en
+ type: public
+ emotion: false
+ - name: Hamad
+ name_ar: حمد
+ gender: Male
+ accent: Gulf
+ languages:
+ - ar
+ type: public
+ emotion: false
+ - name: MyVoice
+ languages: []
+ type: personal
+ emotion: false
+ "400":
+ description: The content was filtered
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: content_filter
+ message: The content was filtered
+ status: 400
+ param: prompt
+ type: safety
+ "401":
+ description: Invalid authentication
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authentication
+ message: Invalid authentication
+ status: 401
+ "403":
+ description: Invalid authorization
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authorization
+ message: Invalid authorization
+ status: 403
+ "404":
+ description: Not found
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: Not found
+ message: Not found
+ status: 404
+ "409":
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: conflict
+ message: Conflict
+ status: 409
+ "410":
+ description: No longer supported
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: no_longer_supported
+ message: No longer supported
+ status: 410
+ "413":
+ description: Request entity too large
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: too_large
+ message: Request entity too large
+ status: 413
+ "422":
+ description: Unprocessable
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: unprocessable
+ message: Unprocessable
+ status: 422
+ "429":
+ description: Rate limit reached or Exceeded quota
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: exceeded_quota
+ message: Exceeded quota
+ status: 429
+ "499":
+ description: Client closed request before completion
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: client_closed_request
+ message: Client closed request before completion
+ status: 499
+ "500":
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: internal_server_error
+ message: Internal server error
+ status: 500
+ "503":
+ description: Service overloaded
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: overloaded
+ message: Service overloaded
+ status: 503
+ "504":
+ description: Request timed out
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: timeout
+ message: Request timed out
+ status: 504
+ security:
+ - Bearer: []
+ x-codeSamples:
+ - lang: Curl
+ label: cURL
+ source: |
+ curl -X GET "https://api.fanar.qa/v1/audio/voices" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer YOUR_API_KEY"
+ - lang: Python
+ label: Python - requests
+ source: >-
+ # Voice personalization requires additional authorization and is not
+ allowed by default.
+
+
+ import requests
+
+
+ url = "https://api.fanar.qa/v1/audio/voices"
+
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY",
+ "Content-Type": "application/json"
+ }
+
+ response = requests.get(url, headers=headers)
+
+ voices = response.json()
+
+ print(voices)
+ post:
+ tags:
+ - Audio
+ summary: Create Voice
+ description: Creates a personalized voice that can be used to generate
+ speech.
The voice name must be unique among your own personalized
+ voices. Names that match a built-in (public) voice are allowed — public
+ voices and your personalized voices live in separate namespaces. When
+ you request a voice name that exists in both, your personalized voice
+ takes precedence at synthesis time in POST
+ /v1/audio/speech.
This endpoint requires additional
+ authorization and is not allowed by default. Please contact support@fanar.qa.
+ operationId: create_voice_v1_audio_voices_post
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ $ref: "#/components/schemas/Body_create_voice_v1_audio_voices_post"
+ required: true
+ responses:
+ "200":
+ description: Successful Response
+ content:
+ application/json:
+ schema: {}
+ "400":
+ description: The content was filtered
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: content_filter
+ message: The content was filtered
+ status: 400
+ param: prompt
+ type: safety
+ "401":
+ description: Invalid authentication
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authentication
+ message: Invalid authentication
+ status: 401
+ "403":
+ description: Invalid authorization
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authorization
+ message: Invalid authorization
+ status: 403
+ "404":
+ description: Not found
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: Not found
+ message: Not found
+ status: 404
+ "409":
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: conflict
+ message: Conflict
+ status: 409
+ "410":
+ description: No longer supported
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: no_longer_supported
+ message: No longer supported
+ status: 410
+ "413":
+ description: Request entity too large
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: too_large
+ message: Request entity too large
+ status: 413
+ "422":
+ description: Unprocessable
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: unprocessable
+ message: Unprocessable
+ status: 422
+ "429":
+ description: Rate limit reached or Exceeded quota
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: exceeded_quota
+ message: Exceeded quota
+ status: 429
+ "499":
+ description: Client closed request before completion
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: client_closed_request
+ message: Client closed request before completion
+ status: 499
+ "500":
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: internal_server_error
+ message: Internal server error
+ status: 500
+ "503":
+ description: Service overloaded
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: overloaded
+ message: Service overloaded
+ status: 503
+ "504":
+ description: Request timed out
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: timeout
+ message: Request timed out
+ status: 504
+ security:
+ - Bearer: []
+ x-codeSamples:
+ - lang: Curl
+ label: cURL
+ source: |
+ curl -X POST "https://api.fanar.qa/v1/audio/voices" \
+ -H "Authorization: Bearer YOUR_API_KEY" \
+ -F "audio=@custom-voice-sample.wav;type=audio/wav" \
+ -F "name=CustomVoice1" \
+ -F "transcript=This is a sample transcription."
+ - lang: Python
+ label: Python - requests
+ source: >
+ # Voice personalization requires additional authorization and is not
+ allowed by default.
+
+
+ import requests
+
+ from pydub import AudioSegment
+
+ from io import BytesIO
+
+
+ url = "https://api.fanar.qa/v1/audio/voices"
+
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY"
+ }
+
+
+ sample_audio_path = "custom-voice-sample.wav"
+
+ audio = AudioSegment.from_wav(sample_audio_path)
+
+ if audio.frame_rate != 24000:
+ audio = audio.set_frame_rate(24000)
+
+ audio_buffer = BytesIO()
+
+ audio.export(audio_buffer, format='wav')
+
+ audio_buffer.seek(0) # Reset buffer position to start
+
+
+ files = {'audio': (os.path.basename(sample_audio_path),
+ audio_buffer, 'audio/wav')}
+
+
+ data = {
+ "name": "CustomVoice1",
+ "transcript": "This is a sample transcription."
+ }
+
+
+ requests.post(url, headers=headers, files=files, data=data)
+ /v1/audio/voices/{name}:
+ delete:
+ tags:
+ - Audio
+ summary: Delete Voice
+ description: Deletes a personalized voice by name.
This endpoint requires
+ additional authorization and is not allowed by default. Please contact
+ support@fanar.qa.
+ operationId: delete_voice_v1_audio_voices__name__delete
+ security:
+ - Bearer: []
+ parameters:
+ - name: name
+ in: path
+ required: true
+ schema:
+ type: string
+ title: Name
+ responses:
+ "200":
+ description: Successful Response
+ content:
+ application/json:
+ schema: {}
+ "400":
+ description: The content was filtered
+ content:
+ application/json:
+ example:
+ error:
+ code: content_filter
+ message: The content was filtered
+ status: 400
+ param: prompt
+ type: safety
+ schema:
+ $ref: "#/components/schemas/Error"
+ "401":
+ description: Invalid authentication
+ content:
+ application/json:
+ example:
+ error:
+ code: invalid_authentication
+ message: Invalid authentication
+ status: 401
+ schema:
+ $ref: "#/components/schemas/Error"
+ "403":
+ description: Invalid authorization
+ content:
+ application/json:
+ example:
+ error:
+ code: invalid_authorization
+ message: Invalid authorization
+ status: 403
+ schema:
+ $ref: "#/components/schemas/Error"
+ "404":
+ description: Not found
+ content:
+ application/json:
+ example:
+ error:
+ code: Not found
+ message: Not found
+ status: 404
+ schema:
+ $ref: "#/components/schemas/Error"
+ "409":
+ description: Conflict
+ content:
+ application/json:
+ example:
+ error:
+ code: conflict
+ message: Conflict
+ status: 409
+ schema:
+ $ref: "#/components/schemas/Error"
+ "410":
+ description: No longer supported
+ content:
+ application/json:
+ example:
+ error:
+ code: no_longer_supported
+ message: No longer supported
+ status: 410
+ schema:
+ $ref: "#/components/schemas/Error"
+ "413":
+ description: Request entity too large
+ content:
+ application/json:
+ example:
+ error:
+ code: too_large
+ message: Request entity too large
+ status: 413
+ schema:
+ $ref: "#/components/schemas/Error"
+ "422":
+ description: Unprocessable
+ content:
+ application/json:
+ example:
+ error:
+ code: unprocessable
+ message: Unprocessable
+ status: 422
+ schema:
+ $ref: "#/components/schemas/Error"
+ "429":
+ description: Rate limit reached or Exceeded quota
+ content:
+ application/json:
+ example:
+ error:
+ code: exceeded_quota
+ message: Exceeded quota
+ status: 429
+ schema:
+ $ref: "#/components/schemas/Error"
+ "499":
+ description: Client closed request before completion
+ content:
+ application/json:
+ example:
+ error:
+ code: client_closed_request
+ message: Client closed request before completion
+ status: 499
+ schema:
+ $ref: "#/components/schemas/Error"
+ "500":
+ description: Internal server error
+ content:
+ application/json:
+ example:
+ error:
+ code: internal_server_error
+ message: Internal server error
+ status: 500
+ schema:
+ $ref: "#/components/schemas/Error"
+ "503":
+ description: Service overloaded
+ content:
+ application/json:
+ example:
+ error:
+ code: overloaded
+ message: Service overloaded
+ status: 503
+ schema:
+ $ref: "#/components/schemas/Error"
+ "504":
+ description: Request timed out
+ content:
+ application/json:
+ example:
+ error:
+ code: timeout
+ message: Request timed out
+ status: 504
+ schema:
+ $ref: "#/components/schemas/Error"
+ x-codeSamples:
+ - lang: Curl
+ label: cURL
+ source: |
+ curl -X DELETE "https://api.fanar.qa/v1/audio/voices/{name}" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer YOUR_API_KEY"
+ - lang: Python
+ label: Python - requests
+ source: >
+ # Voice personalization requires additional authorization and is not
+ allowed by default.
+
+
+ import requests
+
+
+ url = "https://api.fanar.qa/v1/audio/voices/{name}"
+
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY",
+ "Content-Type": "application/json"
+ }
+
+ requests.delete(url, headers=headers)
+ /v1/images/generations:
+ post:
+ tags:
+ - Images
+ summary: Create Image
+ description: This endpoint is compatible with the OpenAI library.
Creates an
+ image given a prompt.
+ operationId: create_image_v1_images_generations_post
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ImageGenerationRequest"
+ required: true
+ responses:
+ "200":
+ description: Successful Response
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ImageGenerationResponse"
+ "400":
+ description: The content was filtered
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: content_filter
+ message: The content was filtered
+ status: 400
+ param: prompt
+ type: safety
+ "401":
+ description: Invalid authentication
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authentication
+ message: Invalid authentication
+ status: 401
+ "403":
+ description: Invalid authorization
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authorization
+ message: Invalid authorization
+ status: 403
+ "404":
+ description: Not found
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: Not found
+ message: Not found
+ status: 404
+ "409":
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: conflict
+ message: Conflict
+ status: 409
+ "410":
+ description: No longer supported
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: no_longer_supported
+ message: No longer supported
+ status: 410
+ "413":
+ description: Request entity too large
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: too_large
+ message: Request entity too large
+ status: 413
+ "422":
+ description: Unprocessable
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: unprocessable
+ message: Unprocessable
+ status: 422
+ "429":
+ description: Rate limit reached or Exceeded quota
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: exceeded_quota
+ message: Exceeded quota
+ status: 429
+ "499":
+ description: Client closed request before completion
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: client_closed_request
+ message: Client closed request before completion
+ status: 499
+ "500":
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: internal_server_error
+ message: Internal server error
+ status: 500
+ "503":
+ description: Service overloaded
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: overloaded
+ message: Service overloaded
+ status: 503
+ "504":
+ description: Request timed out
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: timeout
+ message: Request timed out
+ status: 504
+ security:
+ - Bearer: []
+ x-codeSamples:
+ - lang: Curl
+ label: cURL
+ source: >
+ curl -X POST "https://api.fanar.qa/v1/images/generations" \
+
+ -H "Content-Type: application/json" \
+
+ -H "Authorization: Bearer YOUR_API_KEY" \
+
+ -d '{
+ "model": "Fanar-Oryx-IG-2",
+ "prompt": "A serene sunset over a mountain lake with reflections of colorful clouds and pine trees"
+ }'
+ - lang: Python
+ label: Python - OpenAI
+ source: >
+ # Image Generation requires additional authorization and is not
+ allowed by default.
+
+
+ import base64
+
+ from openai import OpenAI
+
+
+ client = OpenAI(
+ base_url="https://api.fanar.qa/v1",
+ api_key="YOUR_API_KEY",
+ )
+
+
+ response = client.images.generate(
+ model="Fanar-Oryx-IG-2",
+ prompt="A serene sunset over a mountain lake with reflections of colorful clouds and pine trees"
+ )
+
+
+ image_b64 = response.data[0].b64_json
+
+
+ image_bytes = base64.b64decode(image_b64)
+
+
+ with open("generated_image.png", "wb") as f:
+ f.write(image_bytes)
+ - lang: Python - requests
+ label: Python - requests
+ source: >-
+ # Image Generation requires additional authorization and is not
+ allowed by default.
+
+
+ import requests
+
+ import base64
+
+
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY",
+ "Content-Type": "application/json",
+ }
+
+
+ json_data = {
+ "model": "Fanar-Oryx-IG-2",
+ "prompt": "A serene sunset over a mountain lake with reflections of colorful clouds and pine trees"
+ }
+
+
+ response = requests.post(
+ "https://api.fanar.qa/v1/images/generations",
+ headers=headers,
+ json=json_data
+ )
+
+
+ data = response.json()
+
+
+ image_b64 = data["data"][0]["b64_json"]
+
+
+ image_bytes = base64.b64decode(image_b64)
+
+
+ with open("generated_image.png", "wb") as f:
+ f.write(image_bytes)
+ /v1/translations:
+ post:
+ tags:
+ - Translations
+ summary: Translate
+ description: Translate the given text into the specified language.
+ operationId: translate_v1_translations_post
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/TranslationRequest"
+ required: true
+ responses:
+ "200":
+ description: Successful Response
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/TranslationResponse"
+ "400":
+ description: The content was filtered
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: content_filter
+ message: The content was filtered
+ status: 400
+ param: prompt
+ type: safety
+ "401":
+ description: Invalid authentication
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authentication
+ message: Invalid authentication
+ status: 401
+ "403":
+ description: Invalid authorization
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authorization
+ message: Invalid authorization
+ status: 403
+ "404":
+ description: Not found
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: Not found
+ message: Not found
+ status: 404
+ "409":
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: conflict
+ message: Conflict
+ status: 409
+ "410":
+ description: No longer supported
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: no_longer_supported
+ message: No longer supported
+ status: 410
+ "413":
+ description: Request entity too large
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: too_large
+ message: Request entity too large
+ status: 413
+ "422":
+ description: Unprocessable
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: unprocessable
+ message: Unprocessable
+ status: 422
+ "429":
+ description: Rate limit reached or Exceeded quota
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: exceeded_quota
+ message: Exceeded quota
+ status: 429
+ "499":
+ description: Client closed request before completion
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: client_closed_request
+ message: Client closed request before completion
+ status: 499
+ "500":
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: internal_server_error
+ message: Internal server error
+ status: 500
+ "503":
+ description: Service overloaded
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: overloaded
+ message: Service overloaded
+ status: 503
+ "504":
+ description: Request timed out
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: timeout
+ message: Request timed out
+ status: 504
+ security:
+ - Bearer: []
+ x-codeSamples:
+ - lang: Curl
+ label: cURL
+ source: |
+ curl -X POST "https://api.fanar.qa/v1/translations" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer YOUR_API_KEY" \
+ -d '{
+ "model": "Fanar-Shaheen-MT-1",
+ "text": "Your text here",
+ "langpair": "ar-en",
+ "preprocessing": "default"
+ }'
+ - lang: Python
+ label: Python - requests
+ source: >-
+ # Translation requires additional authorization and is not allowed
+ by default.
+
+
+ import requests
+
+
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY",
+ "Content-Type": "application/json",
+ }
+
+
+ json_data = {
+ "model": "Fanar-Shaheen-MT-1",
+ "text": "مرحبا بك في عالم الذكاء الاصطناعي!",
+ "langpair": "ar-en",
+ "preprocessing": "default",
+ }
+
+
+ response = requests.post("https://api.fanar.qa/v1/translations",
+ headers=headers, json=json_data)
+
+
+ print(response.json())
+ /v1/poems/generations:
+ post:
+ tags:
+ - Poems
+ summary: Create Poem
+ description: This endpoint is compatible with the OpenAI library.
Creates a
+ poem given a prompt.
+ operationId: create_poem_v1_poems_generations_post
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/PoemGenerationRequest"
+ required: true
+ responses:
+ "200":
+ description: Successful Response
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/PoemGenerationResponse"
+ "400":
+ description: The content was filtered
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: content_filter
+ message: The content was filtered
+ status: 400
+ param: prompt
+ type: safety
+ "401":
+ description: Invalid authentication
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authentication
+ message: Invalid authentication
+ status: 401
+ "403":
+ description: Invalid authorization
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authorization
+ message: Invalid authorization
+ status: 403
+ "404":
+ description: Not found
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: Not found
+ message: Not found
+ status: 404
+ "409":
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: conflict
+ message: Conflict
+ status: 409
+ "410":
+ description: No longer supported
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: no_longer_supported
+ message: No longer supported
+ status: 410
+ "413":
+ description: Request entity too large
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: too_large
+ message: Request entity too large
+ status: 413
+ "422":
+ description: Unprocessable
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: unprocessable
+ message: Unprocessable
+ status: 422
+ "429":
+ description: Rate limit reached or Exceeded quota
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: exceeded_quota
+ message: Exceeded quota
+ status: 429
+ "499":
+ description: Client closed request before completion
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: client_closed_request
+ message: Client closed request before completion
+ status: 499
+ "500":
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: internal_server_error
+ message: Internal server error
+ status: 500
+ "503":
+ description: Service overloaded
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: overloaded
+ message: Service overloaded
+ status: 503
+ "504":
+ description: Request timed out
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: timeout
+ message: Request timed out
+ status: 504
+ security:
+ - Bearer: []
+ x-codeSamples:
+ - lang: Curl
+ label: cURL
+ source: |
+ curl -X POST "https://api.fanar.qa/v1/poems/generations" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer YOUR_API_KEY" \
+ -d '{
+ "model": "Fanar-Diwan",
+ "prompt": "Your text here"
+ }'
+ - lang: Python
+ label: Python - requests
+ source: >-
+ # Poem generation requires additional authorization and is not
+ allowed by default.
+
+
+ import requests
+
+
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY",
+ "Content-Type": "application/json",
+ }
+
+
+ json_data = {
+ "model": "Fanar-Diwan",
+ "prompt": "Your text here",
+ }
+
+
+ response =
+ requests.post("https://api.fanar.qa/v1/poems/generations",
+ headers=headers, json=json_data)
+
+
+ print(response.json())
+ /v1/moderations:
+ post:
+ tags:
+ - Moderations
+ summary: Identify Safety
+ description: FanarGuard gives each prompt–response pair safety and
+ cultural-awareness scores, allowing moderation thresholds to be tailored
+ to the deployment.
For our definition of cultural awareness, refer
+ to [https://arxiv.org/abs/2511.18852](https://arxiv.org/abs/2511.18852).
+ operationId: identify_safety_v1_moderations_post
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/SafetyFilterRequest"
+ required: true
+ responses:
+ "200":
+ description: Successful Response
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/SafetyFilterResponse"
+ "401":
+ description: Invalid authentication
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authentication
+ message: Invalid authentication
+ status: 401
+ "403":
+ description: Invalid authorization
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authorization
+ message: Invalid authorization
+ status: 403
+ "404":
+ description: Not found
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: Not found
+ message: Not found
+ status: 404
+ "409":
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: conflict
+ message: Conflict
+ status: 409
+ "410":
+ description: No longer supported
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: no_longer_supported
+ message: No longer supported
+ status: 410
+ "413":
+ description: Request entity too large
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: too_large
+ message: Request entity too large
+ status: 413
+ "422":
+ description: Unprocessable
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: unprocessable
+ message: Unprocessable
+ status: 422
+ "429":
+ description: Rate limit reached or Exceeded quota
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: exceeded_quota
+ message: Exceeded quota
+ status: 429
+ "499":
+ description: Client closed request before completion
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: client_closed_request
+ message: Client closed request before completion
+ status: 499
+ "500":
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: internal_server_error
+ message: Internal server error
+ status: 500
+ "503":
+ description: Service overloaded
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: overloaded
+ message: Service overloaded
+ status: 503
+ "504":
+ description: Request timed out
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: timeout
+ message: Request timed out
+ status: 504
+ security:
+ - Bearer: []
+ x-codeSamples:
+ - lang: Curl
+ label: cURL
+ source: |
+ curl -X POST "https://api.fanar.qa/v1/moderations" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer YOUR_API_KEY" \
+ -d '{
+ "model": "Fanar-Guard-2",
+ "prompt": "Your prompt here",
+ "response": "Response from the model here"
+ }'
+ - lang: Python
+ label: Python - requests
+ source: >-
+ import requests
+
+
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY",
+ "Content-Type": "application/json",
+ }
+
+
+ json_data = {
+ "model": "Fanar-Guard-2",
+ "prompt": "Your prompt here",
+ "response": "Response from the model here",
+ }
+
+
+ response = requests.post("https://api.fanar.qa/v1/moderations",
+ headers=headers, json=json_data)
+
+
+ print(response.json()) # Print the safety and cultural awareness
+ scores
+ /v1/tokens:
+ post:
+ tags:
+ - Tokens
+ summary: Get Tokens
+ operationId: get_tokens_v1_tokens_post
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/TokenizationRequest"
+ required: true
+ responses:
+ "200":
+ description: Successful Response
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/TokenizationResponse"
+ "401":
+ description: Invalid authentication
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authentication
+ message: Invalid authentication
+ status: 401
+ "403":
+ description: Invalid authorization
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authorization
+ message: Invalid authorization
+ status: 403
+ "404":
+ description: Not found
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: Not found
+ message: Not found
+ status: 404
+ "409":
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: conflict
+ message: Conflict
+ status: 409
+ "410":
+ description: No longer supported
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: no_longer_supported
+ message: No longer supported
+ status: 410
+ "413":
+ description: Request entity too large
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: too_large
+ message: Request entity too large
+ status: 413
+ "422":
+ description: Unprocessable
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: unprocessable
+ message: Unprocessable
+ status: 422
+ "429":
+ description: Rate limit reached or Exceeded quota
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: exceeded_quota
+ message: Exceeded quota
+ status: 429
+ "499":
+ description: Client closed request before completion
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: client_closed_request
+ message: Client closed request before completion
+ status: 499
+ "500":
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: internal_server_error
+ message: Internal server error
+ status: 500
+ "503":
+ description: Service overloaded
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: overloaded
+ message: Service overloaded
+ status: 503
+ "504":
+ description: Request timed out
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: timeout
+ message: Request timed out
+ status: 504
+ security:
+ - Bearer: []
+ x-codeSamples:
+ - lang: Curl
+ label: cURL
+ source: |
+ curl -X POST "https://api.fanar.qa/v1/tokens" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer YOUR_API_KEY" \
+ -d '{
+ "content": "Your text content here",
+ "model": "Fanar-C-1-8.7B"
+ }'
+ - lang: Python
+ label: Python - requests
+ source: >-
+ import requests
+
+
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY",
+ "Content-Type": "application/json",
+ }
+
+
+ json_data = {
+ "content": "Your text content here",
+ "model": "Fanar-C-1-8.7B"
+ }
+
+
+ response = requests.post("https://api.fanar.qa/v1/tokens",
+ headers=headers, json=json_data)
+
+
+ print(response.json())
+ /v1/models:
+ get:
+ tags:
+ - Models
+ summary: List Models
+ description: Lists the currently available models.
+ operationId: list_models_v1_models_get
+ responses:
+ "200":
+ description: Successful Response
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ModelsResponse"
+ "400":
+ description: The content was filtered
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: content_filter
+ message: The content was filtered
+ status: 400
+ param: prompt
+ type: safety
+ "401":
+ description: Invalid authentication
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authentication
+ message: Invalid authentication
+ status: 401
+ "403":
+ description: Invalid authorization
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: invalid_authorization
+ message: Invalid authorization
+ status: 403
+ "404":
+ description: Not found
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: Not found
+ message: Not found
+ status: 404
+ "409":
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: conflict
+ message: Conflict
+ status: 409
+ "410":
+ description: No longer supported
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: no_longer_supported
+ message: No longer supported
+ status: 410
+ "413":
+ description: Request entity too large
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: too_large
+ message: Request entity too large
+ status: 413
+ "422":
+ description: Unprocessable
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: unprocessable
+ message: Unprocessable
+ status: 422
+ "429":
+ description: Rate limit reached or Exceeded quota
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: exceeded_quota
+ message: Exceeded quota
+ status: 429
+ "499":
+ description: Client closed request before completion
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: client_closed_request
+ message: Client closed request before completion
+ status: 499
+ "500":
+ description: Internal server error
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: internal_server_error
+ message: Internal server error
+ status: 500
+ "503":
+ description: Service overloaded
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: overloaded
+ message: Service overloaded
+ status: 503
+ "504":
+ description: Request timed out
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Error"
+ example:
+ error:
+ code: timeout
+ message: Request timed out
+ status: 504
+ security:
+ - Bearer: []
+ x-codeSamples:
+ - lang: Curl
+ label: cURL
+ source: |
+ curl -X GET "https://api.fanar.qa/v1/models" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer YOUR_API_KEY"
+ - lang: Python
+ label: Python - requests
+ source: >-
+ import requests
+
+
+ headers = {
+ "Authorization": "Bearer YOUR_API_KEY",
+ "Content-Type": "application/json",
+ }
+
+
+ response = requests.get("https://api.fanar.qa/v1/models",
+ headers=headers)
+
+
+ print(response.json())
+components:
+ schemas:
+ AvailableModel:
+ properties:
+ id:
+ type: string
+ title: Id
+ description: A unique identifier for the model.
+ object:
+ $ref: "#/components/schemas/ModelObject"
+ description: The type of object.
+ created:
+ type: integer
+ title: Created
+ description: The creation timestamp of the model.
+ owned_by:
+ type: string
+ title: Owned By
+ description: The owner of the model.
+ type: object
+ required:
+ - id
+ - object
+ - created
+ - owned_by
+ title: AvailableModel
+ Body_create_transcription_v1_audio_transcriptions_post:
+ properties:
+ file:
+ type: string
+ format: binary
+ title: File
+ description: The audio blob to transcribe.
+ model:
+ $ref: "#/components/schemas/STTModels"
+ description: >-
+ The model to use for the speech-to-text.
+
+ - `Fanar-Aura-STT-1`: For short audio clips (up to 20–30 seconds).
+
+ - `Fanar-Aura-STT-LF-1`: For long-form transcription of longer audio
+ files.
+ format:
+ $ref: "#/components/schemas/STTFormat"
+ description: >-
+ The format of the transcribed text. `Fanar-Aura-STT-1` only supports
+ `text` format.
+
+ - `text`: Plain text format.
+
+ - `srt`: SubRip Subtitle format.
+
+ - `json`: JSON format with detailed transcription data.
+ default: text
+ type: object
+ required:
+ - file
+ - model
+ title: Body_create_transcription_v1_audio_transcriptions_post
+ Body_create_voice_v1_audio_voices_post:
+ properties:
+ name:
+ type: string
+ title: Name
+ description: The name of the personalized voice to be created.
+ audio:
+ type: string
+ format: binary
+ title: Audio
+ description: The audio sample to create the personalized voice. Only WAV format
+ is accepted.
+ transcript:
+ type: string
+ title: Transcript
+ description: The transcript of the audio sample.
+ type: object
+ required:
+ - name
+ - audio
+ - transcript
+ title: Body_create_voice_v1_audio_voices_post
+ BookNamesEnum:
+ type: string
+ enum:
+ - أصل الزراري شرح صحيح البخاري - مخطوط
+ - جمهرة تراجم الفقهاء المالكية
+ - مختصر تحفة المحتاج بشرح المنهاج
+ - شرح رياض الصالحين - حطيبة
+ - "تفسير العثيمين: الزمر"
+ - الموسوعة في صحيح السيرة النبوية - العهد المكي
+ - تيسير التفسير للقطان
+ - "تفسير العثيمين: السجدة"
+ - شرح زاد المستقنع - الشنقيطي - التفريغ
+ - تقويم طرق تعليم القرآن الكريم في مراحل التعليم العام والتعليم الجامعي
+ - شرح الورقات في أصول الفقه - الددو
+ - السراج الوهاج
+ - معلم التجويد
+ - فتح القدير للكمال بن الهمام - ط الحلبي
+ - دلائل الإعجاز بين أبي سعيد السيرافي والجرجاني
+ - معلمة الفقه المالكي
+ - الفرائض
+ - غزوات النبي
+ - فقه السيرة النبوية لمنير الغضبان
+ - شرح صحيح مسلم - حسن أبو الأشبال
+ - رسالة ابن القيم إلى أحد إخوانه - ط الشرق الأوسط
+ - مجموع فتاوى ورسائل العثيمين
+ - حاشية البجيرمي على الخطيب = تحفة الحبيب على شرح الخطيب
+ - جواهر البلاغة في المعاني والبيان والبديع
+ - أعلام السيرة النبوية في القرن الثاني للهجرة
+ - شرح سنن النسائي - الراجحي
+ - فتاوى واستشارات الإسلام اليوم
+ - مذكرة أصول الفقه - الجامعة الإسلامية
+ - الوفيات والأحداث
+ - صحيح كنوز السنة النبوية
+ - التفسير الميسر
+ - فتح السلام شرح عمدة الأحكام من فتح الباري
+ - المنتخب في ذكر نسب قبائل العرب
+ - المنهاج الواضح للبلاغة
+ - البيان والتعريف في أسباب ورود الحديث الشريف
+ - متون طالب العلم - المستوى الخامس - 3
+ - تفسير أسماء الله الحسنى للسعدي
+ - شرح رياض الصالحين لابن عثيمين
+ - يسألونك عن رمضان
+ - تكملة المعاجم العربية
+ - التفسير الوسيط - الزحيلي
+ - التفسير القيم = تفسير القرآن الكريم لابن القيم
+ - قاموس الإملاء
+ - علوم البلاغة
+ - السيرة النبوية لأبي الحسن الندوي
+ - مدارج السالكين - ط عطاءات العلم
+ - نونية ابن القيم الكافية الشافية - ط عطاءات العلم
+ - المختصر في تفسير القرآن الكريم
+ - نيل الأوطار شرح منتقى الأخبار - ط الحديث
+ - التنوير شرح الجامع الصغير
+ - قواعد التجويد على رواية حفص عن عاصم بن أبي النجود
+ - شرح صحيح البخاري - عبد الكريم الخضير
+ - منهج الإمام أحمد في إعلال الأحاديث
+ - السيرة النبوية كما جاءت في الأحاديث الصحيحة
+ - تقريب فتاوى ابن تيمية
+ - ترجمة القرآن الكريم
+ - "تفسير العثيمين: الحجرات - الحديد"
+ - تحرير تقريب التهذيب
+ - المعتصر من شرح مختصر الأصول من علم الأصول
+ - علوم البلاغة «البديع والبيان والمعاني»
+ - الفقه المنهجي على مذهب الإمام الشافعي
+ - الإعراب المفصل لكتاب الله المرتل
+ - المختصر في المنطق
+ - مفهوم التفسير والتأويل والاستنباط والتدبر والمفسر
+ - الجرح والتعديل - اللاحم
+ - فتاوى مهمة تتعلق بالحج والعمرة
+ - الرسائل الحربية في عصر الدولة الأيوبية
+ - حاشية ابن عابدين = رد المحتار ط الحلبي
+ - عشائر العراق
+ - مدخل إلى التفسير وعلوم القرآن
+ - الإمام البخاري وكتابه الجامع الصحيح
+ - فائدة جليلة في قواعد الأسماء الحسنى
+ - التفسير الواضح
+ - المعجم الوسيط
+ - التسهيل في فقه الإمام أحمد - وزارة الأوقاف الكويتية
+ - أيسر التفاسير للجزائري
+ - أصول النحو 2 - جامعة المدينة
+ - بغية المقتصد شرح بداية المجتهد
+ - موجز التاريخ الإسلامي من عهد آدم إلى عصرنا الحاضر
+ - السيرة النبوية الصحيحة محاولة لتطبيق قواعد المحدثين في نقد روايات
+ السيرة النبوية
+ - نضرة النعيم في مكارم أخلاق الرسول الكريم
+ - قواعد الإملاء
+ - الرحيق المختوم
+ - البر والصلة لابن الجوزي
+ - مقاصد الشريعة الإسلامية
+ - دراسات في تاريخ العرب القديم
+ - إتحاف الأريب بشرح الغاية والتقريب
+ - سبل السلام شرح بلوغ المرام - ط الحديث
+ - اللؤلؤ المكنون في سيرة النبي المأمون
+ - نظرية المقاصد عند الإمام الشاطبي
+ - سير أعلام النبلاء - ط الرسالة
+ - القاموس المحيط
+ - البدع والمخالفات في الحج
+ - أساليب بلاغية
+ - غاية المرام في تخريج أحاديث الحلال والحرام
+ - الروح - ابن القيم - ط عطاءات العلم
+ - هداية الحيارى في أجوبة اليهود والنصارى - ط عطاءات العلم
+ - موسوعة المفاهيم الإسلامية العامة
+ - شرح الأربعين النووية - العباد
+ - "تفسير العثيمين: النساء"
+ - أصول الإيمان لمحمد بن عبد الوهاب - ت الجوابرة
+ - عون المعبود وحاشية ابن القيم
+ - تيسير اللطيف المنان في خلاصة تفسير القرآن - ط الأوقاف السعودية
+ - شرح عمدة الأحكام - عبد الكريم الخضير
+ - الوجيز في إيضاح قواعد الفقة الكلية
+ - حاشية السندي على سنن ابن ماجه
+ - "تفسير العثيمين: النمل"
+ - السيرة النبوية والدعوة في العهد المكي
+ - بغية الإيضاح لتلخيص المفتاح في علوم البلاغة
+ - قصة الحضارة
+ - فيض الباري على صحيح البخاري
+ - مناظرة بين الإسلام والنصرانية
+ - النظم البلاغي بين النظرية والتطبيق
+ - بحوث في تاريخ السنة المشرفة
+ - الوجيز في حكم تجويد الكتاب العزيز
+ - التخريج عند الفقهاء والأصوليين
+ - "تفسير العثيمين: الشعراء"
+ - الأسلوب
+ - المسند الجامع
+ - حاشية الصاوي على الشرح الصغير = بلغة السالك لأقرب المسالك
+ - تلخيص فقة الفرائض
+ - فقه السيرة للغزالي
+ - فقه المعاملات
+ - مختصر في قواعد التفسير
+ - سلسلة الآداب - المنجد
+ - الدرر البهية من الفتاوى الكويتية
+ - شرح الأربعين النووية للعثيمين
+ - التحرير والتنوير
+ - فتح القدير للشوكاني
+ - العقود الدرية في تنقيح الفتاوى الحامدية
+ - سلسلة الأحاديث الصحيحة وشيء من فقهها وفوائدها
+ - تفسير القرطبي = الجامع لأحكام القرآن
+ - فقه العبادات على المذهب الشافعي
+ - التحفة الندية شرح العقيدة الواسطية - عبد الرحمن العقل
+ - سلسلة الفوائد الحديثية والفقهية
+ - شرح المحرر في الحديث - عبد الكريم الخضير
+ - معجم تصحيح لغة الإعلام العربي
+ - آداب البحث والمناظرة
+ - شرح سنن الترمذي - عبد الكريم الخضير
+ - المجتبى من مشكل إعراب القرآن
+ - المختصر المفيد في أحكام التجويد - بآخر مصحف القراءات والتجويد
+ - مواهب الجليل من أدلة خليل
+ - التذهيب في أدلة متن الغاية والتقريب
+ - السيرة النبوية - راغب السرجاني
+ - معالم مكة التأريخية والأثرية
+ - "تفسير العثيمين: الأنعام"
+ - حياة محمد صلى الله عليه وآله وسلم
+ - متون طالب العلم - الإضافية - 2
+ - التجريد لبغية المريد في القراءات السبع - ت ضاري
+ - الأحاديث الواردة في فضائل الصحابة
+ - لطائف قرآنية
+ - المتشابه
+ - تفسير البيضاوي = أنوار التنزيل وأسرار التأويل
+ - خصائص التراكيب دارسة تحليلية لمسائل علم المعاني
+ - "تفسير العثيمين: فصلت"
+ - فقه النوازل في العبادات
+ - الوابل الصيب - ط دار الحديث
+ - السيرة النبوية بين الآثار المروية والآيات القرآنية
+ - فتاوى دار الإفتاء المصرية
+ - زوائد الأحاديث الواردة في فضائل الصحابة
+ - شرح المدائح النبوية
+ - جامع تفاسير الأحلام = تنبيه الأفهام بتأويل الأحلام
+ - الطبقات للنسائي
+ - أصول الإيمان لابن باز
+ - صحيح السيرة النبوية للألباني
+ - "تفسير العثيمين: لقمان"
+ - شرح جامع الترمذي - الراجحي
+ - مدونة أحكام الوقف الفقهية
+ - معجم المعالم الجغرافية في السيرة النبوية
+ - الأديان والمذاهب - جامعة المدينة
+ - الفتح المبين بشرح الأربعين
+ - تحفة المودود بأحكام المولود - ط عطاءات العلم
+ - لا تحزن
+ - موسوعة الفرق المنتسبة للإسلام
+ - مائة من عظماء أمة الإسلام غيروا مجرى التاريخ
+ - منهج الإمام الطاهر بن عاشور في التفسير
+ - حاشية السيوطي على سنن النسائي
+ - السيرة النبوية منهجية دراستها واستعراض أحداثها
+ - "تفسير العثيمين: القصص"
+ - "تفسير العثيمين: النور"
+ - تذكرة الأريب في تفسير الغريب
+ - أثر العقيدة الإسلامية في تضامن ووحدة الأمة الإسلامية
+ - السياسة الشرعية - جامعة المدينة
+ - فتاوى إسلامية
+ - فقه السيرة النبوية مع موجز لتاريخ الخلافة الراشدة
+ - معجم الشعراء العرب
+ - القواعد الفقهية وتطبيقاتها في المذاهب الأربعة
+ - منهاج أهل السنة والجماعة في العقيدة والعمل
+ - الجنايات في الفقه الإسلامي دراسة مقارنة بين الفقه الإسلامي والقانون
+ - تسهيل الفرائض
+ - "تفسير العثيمين: الفاتحة والبقرة"
+ - شرح كتاب الإيمان - يوسف الغفيص
+ - صيد الخاطر
+ - المنيحة بسلسلة الأحاديث الصحيحة
+ - شرح صحيح البخاري - أسامة سليمان
+ - تلخيص الأصول
+ - الشامل في زكاة الأسهم واستثمار أموال الزكاة
+ - حاشية السندي على سنن الترمذي
+ - موسوعة أحكام الطهارة - الدبيان - ط 3
+ - الموسوعة القرآنية
+ - المدخل في تاريخ السنة
+ - تطور كتابة المصحف الشريف وطباعته
+ - الأشباه والنظائر - ابن نجيم
+ - معجم قبائل المملكة العربية السعودية
+ - الدر المنثور في التفسير بالمأثور
+ - طبائع الاستبداد ومصارع الاستعباد
+ - الفوائد لابن القيم - ط عطاءات العلم
+ - موسوعة الأخلاق الإسلامية
+ - المدخل إلي جامع الترمذي
+ - العلاج والرقى
+ - تاريخ القرآن الكريم
+ - فتاوى الشبكة الإسلامية
+ - دولة الإسلام في الأندلس
+ - فتح الباري بشرح البخاري - ط السلفية
+ - شرح سنن أبي داود للعباد
+ - من شرح بلوغ المرام للطريفي
+ - التفسير الموضوعي 2 - جامعة المدينة
+ - المفصل فى تاريخ العرب قبل الإسلام
+ - الإعلام بأحكام المال الحرام
+ - إعانة الطالب في بداية علم الفرائض
+ - أسواق العرب في الجاهلية والإسلام
+ - منة المنعم في شرح صحيح مسلم
+ - شرح الأجرومية للأسمري
+ - يسألونك عن الزكاة
+ - شرح صحيح ابن خزيمة - الراجحي
+ - الآداب الإسلامية
+ - الأندلس من الفتح إلى السقوط
+ - تهذيب التهذيب - ط دبي
+ - شرح بلوغ المرام - اللهيميد
+ - العذب النمير من مجالس الشنقيطي في التفسير
+ - كيف تربي ولدك
+ - القطارة النحوية على المقدمة الآجرومية
+ - درر الحكام في شرح مجلة الأحكام
+ - مجموعة الوثائق السياسية للعهد النبوي والخلافة الراشدة
+ - الأحكام الشرعية المتعلقة بالوباء والطاعون مع دراسة فقهية للأحكام
+ المتعلقة بفيروس كورونا
+ - مقالات موقع الدرر السنية
+ - التفسير الوسيط - مجمع البحوث
+ - تاريخ العرب وحضارتهم في الأندلس
+ - متون طالب العلم - المستوى التمهيدي
+ - الشفعة بين الجمع العثماني والأحرف السبعة
+ - الشورى في الشريعة الإسلامية
+ - تحفة الأريب بما في القرآن من الغريب
+ - التعريف بالقرآن الكريم
+ - موسوعة الملل والأديان
+ - شرح الآجرومية - حسن حفظي
+ - الميسر في القراءات الأربع عشرة
+ - آيات متشابهات الألفاظ في القرآن الكريم وكيف التمييز بينها
+ - موجز دائرة المعارف الإسلامية
+ - التعريف بالإسلام
+ - معجم الصواب اللغوي
+ - مختصر تفسير البغوي المسمى بمعالم التنزيل
+ - "تفسير العثيمين: من جزء قد سمع وتبارك - ط مكتبة الطبري"
+ - شرح تفسير ابن كثير - الراجحي
+ - الداء والدواء = الجواب الكافي - ط دار المعرفة
+ - الفتاوى الاقتصادية
+ - القواعد الأصولية والفقهية المتعلقة بالمسلم غير المجتهد
+ - فقه الأسرة
+ - ورد اليوم والليلة
+ - معجم الدخيل في اللغة العربية الحديثة ولهجاتها
+ - الدرة السنية منظومة في علم الفرائض
+ - المال والحكم في الإسلام
+ - صحيح الكتب التسعة وزوائده
+ - "تفسير العثيمين: الأحزاب"
+ - فتح الكريم المنان في آداب حملة القرآن
+ - موسوعة الإعجاز العلمي في القرآن والسنة
+ - تيسير أحكام التجويد - المستوى الأول
+ - زاد المسير في علم التفسير
+ - سبيل المهتدين إلى شرح الأربعين النووية
+ - صحيح السيرة النبوية للعلي
+ - المعالم الأثيرة في السنة والسيرة
+ - "تفسير العثيمين: فاطر"
+ - مرويات السيرة لأكرم العمري
+ - عدة الصابرين وذخيرة الشاكرين - ط عطاءات العلم
+ - المعاجم المفهرسة لألفاظ القرآن الكريم
+ - شرح صحيح البخاري للحويني
+ - "تفسير العثيمين: الشورى"
+ - مناسبات الآيات والسور
+ - "تفسير العثيمين: العنكبوت"
+ - "تفسير العثيمين: الزخرف"
+ - دراسات في أصول اللغات العربية
+ - تاريخ العرب القديم
+ - معجزات القرآن العلمية
+ - شرح المعتمد في أصول الفقه
+ - الوجيز في علم التجويد
+ - مسائل مهمات تتعلق بفقه الصوم والتراويح والقراءة على الأموات
+ - الأزهر وأثره في النهضة الأدبية الحديثة
+ - تفسير أسماء الله الحسنى للزجاج
+ - تراجم منتخبة من «التهذيب» و «الميزان» - ضمن «آثار المعلمي»
+ - لسان العرب
+ - قواعد الفقه
+ - الهادي شرح طيبة النشر في القراءات العشر
+ - خاتم النبيين صلى الله عليه وآله وسلم
+ - البلاغة 2 - المعاني - جامعة المدينة
+ - صفوة التفاسير
+ - رسائل وفتاوى عبد العزيز آل الشيخ
+ - موسوعة القواعد الفقهية
+ - غريب الحديث - ابن الجوزي
+ - الإمام مسلم وصحيحه
+ - الكوكب الدري على جامع الترمذي
+ - معجم وتفسير لغوى لكلمات القرآن
+ - دراسات في فقه اللغة
+ - الفقه الميسر في ضوء الكتاب والسنة
+ - فقه العبادات على المذهب الحنفي
+ - تيسير العلام شرح عمدة الأحكام
+ - المنتخب في تفسير القرآن الكريم
+ - المدخل إلى دراسة المدارس والمذاهب الفقهية
+ - الموسوعة العقدية
+ - أثر اختلاف الأسانيد والمتون في اختلاف الفقهاء
+ - الروضة الندية شرح متن الجزرية
+ - الطبقات السنية في تراجم الحنفية
+ - اللغة وعلم اللغة
+ - المسائل الفقهية التي عليها الفتوى عند متأخري الحنفية - جمعا ودراسة-
+ - أصول الفقه الذي لا يسع الفقيه جهله
+ - التذييل على تهذيب التهذيب
+ - الرسول القائد
+ - العقد الثمين في شرح منظومة الشيخ ابن عثيمين
+ - متون طالب العلم - المستوى الثاني
+ - موسوعة المذاهب الفكرية المعاصرة
+ - الخلافة
+ - إعلام الموقعين عن رب العالمين - ط العلمية
+ - الخلاصة الفقهية على مذهب السادة المالكية
+ - الإسلام وأوضاعنا القانونية
+ - "تفسير العثيمين: سبأ"
+ - "تفسير العثيمين: المائدة"
+ - الأديان الوضعية - جامعة المدينة
+ - فتح العلام في دراسة أحاديث بلوغ المرام ط 4
+ - القواعد والضوابط الفقهية المتضمنة للتيسير
+ - معجم الجرح والتعديل لرجال السنن الكبرى
+ - شرح مسند أبي حنيفة
+ - البحر المحيط الثجاج في شرح صحيح الإمام مسلم بن الحجاج
+ - شرح سنن النسائي المسمى شروق أنوار المنن الكبرى الإلهية بكشف أسرار
+ السنن الصغرى النسائية
+ - التوجيه والإرشاد النفسي
+ - فقه العبادات على المذهب المالكي
+ - الفتاوى الكبرى لابن تيمية
+ - متون طالب العلم - الإضافية - 1
+ - جمع القرآن الكريم في عهد الخلفاء الراشدين - عبد القيوم السندي
+ - محاضرات في علوم الحديث
+ - علم الجرح والتعديل
+ - موسوعة تفسير الأحلام
+ - لمسات بيانية في نصوص من التنزيل - كتاب
+ - شرح المقدمة الحضرمية المسمى بشرى الكريم بشرح مسائل التعليم
+ - شرح القواعد الفقهية
+ - "تفسير العثيمين: يس"
+ - الرقية الشرعية
+ - مدرسة الحديث في مصر
+ - شرح الأربعين النووية - عبد الكريم الخضير
+ - شرح العقيدة الواسطية - الغنيمان
+ - التفسير القرآني للقرآن
+ - القاموس الفقهي
+ - المغني لابن قدامة - ت التركي
+ - تفسير ابن رجب الحنبلي
+ - صفة الصفوة
+ - متون طالب العلم - المستوى الخامس - 2
+ - موسوعة مرآة الحرمين الشريفين وجزيرة العرب
+ - فتاوى اللجنة الدائمة - المجموعة الأولى
+ - جوامع الدعاء
+ - تاريخ نزول القرآن
+ - تاريخ شبه الجزيرة العربية في عصورها القديمة
+ - حياة الصحابة
+ - مدخل في علوم القراءات
+ - بينات الرسول صلى الله عليه وآله وسلم ومعجزاته
+ - موسوعة التفسير المأثور
+ - نحو معجم تاريخي للمصطلحات القرآنية المعرفة
+ - توفيق الرب المنعم بشرح صحيح الإمام مسلم
+ - مجموعة رسائل وفتاوى في مسائل مهمة تمس إليها حاجة العصر
+ - شرح مسند الدارمي
+ - "تفسير العثيمين: جزء عم"
+ - الإيمان حقيقته، خوارمه، نواقضه عند أهل السنة والجماعة
+ - اللغة العربية معناها ومبناها
+ - "تفسير العثيمين: غافر"
+ - السرايا والبعوث النبوية حول المدينة ومكة
+ - جمهرة الأجزاء الحديثية
+ - الإسلام وأوضاعنا السياسية
+ - القرآن وإعجازه العلمي
+ - متون طالب العلم - الإضافية - 4
+ - الكواشف الجلية في حكم قراءة القرآن بالمقامات الموسيقية
+ - معالم أصول الفقه عند أهل السنة والجماعة
+ - صفحات في علوم القراءات
+ - المدخل إلى صحيح البخاري
+ - المجالس الفقهية
+ - زاد المعاد في هدي خير العباد - ط عطاءات العلم
+ - الموسوعة التاريخية
+ - شرح سنن أبي داود - الراجحي
+ - البديع عند الحريري
+ - تقريب التهذيب
+ - صحيح الأثر وجميل العبر من سيرة خير البشر (صلى الله عليه وسلم)
+ - فقه الدعوة الإسلامية في الغرب ووجوب تجديدها على الحكمة والوسطية
+ والاعتدال
+ - متون طالب العلم - الإضافية - 10
+ - اللباب في قواعد اللغة وآلات الأدب النحو والصرف والبلاغة والعروض واللغة
+ والمثل
+ - شذرات الذهب دراسة في البلاغة القرآنية
+ - علم اللغة مقدمة للقارئ العربي
+ - شرح التدمرية - محمد بن خليفة التميمي
+ - المنهج الحركي للسيرة النبوية
+ - شرح عمدة الأحكام لابن جبرين
+ - السيرة النبوية - دروس وعبر
+ - شرح مقدمة في أصول التفسير لابن تيمية
+ - الجامع لكتب الضعفاء والمتروكين والكذابين
+ - شرح كتاب الفتن من صحيح البخاري - عبد الكريم الخضير
+ - تفسير ابن كثير - ط ابن الجوزي
+ - تفسير القرآن الكريم - المقدم
+ - البلاغة 1 - البيان والبديع - جامعة المدينة
+ - شرح مائة المعاني والبيان
+ - الخلاصة البهية في ترتيب أحداث السيرة النبوية
+ - الدرر الثرية من الفتاوى البازية
+ - فتح الودود في شرح سنن أبي داود
+ - علم البديع
+ - طريق الهجرتين وباب السعادتين - ط عطاءات العلم
+ - المنهل العذب المورود شرح سنن أبي داود
+ - الموسوعة القرآنية المتخصصة
+ - متون طالب العلم - الإضافية - 8
+ - بحوث ومقالات في اللغة
+ - إغاثة اللهفان في مصايد الشيطان - ت الفقي
+ - منتقى الأذكار
+ - الفقه الميسر
+ - سلسلة القصص - المنجد
+ - الصحاح تاج اللغة وصحاح العربية
+ - إكمال تهذيب الكمال - ط العلمية
+ - البدهيات في القرآن الكريم
+ - القيم الإسلامية
+ - المسلمون في بلاد الغربة
+ - موسوعة سفير للتاريخ الإسلامي
+ - مهمات في أحكام المواريث
+ - الفتاوى اليومية من المسائل الفقهية
+ - "تفسير العثيمين: الروم"
+ - كيف تحفظ القرآن الكريم
+ - مقدمات في علم القراءات
+ - فتح المنعم شرح صحيح مسلم
+ - الإتقان في ضوابط تسجيل القرآن
+ - نزول القران الكريم وتاريخه وما يتعلق به
+ - شرح الموطأ - عبد الكريم الخضير
+ - موجز عن الفتوحات الإسلامية
+ - الاستقصا لأخبار دول المغرب الأقصى
+ - الرسالة الندية في القواعد الفقهية
+ - فقه الهندسة المالية الإسلامية
+ - التربية الإسلامية أصولها ومنهجها ومعلمها
+ - موسوعة القبائل العربية
+ - تفسير السعدي = تيسير الكريم الرحمن
+ - شرح سنن ابن ماجه للهرري = مرشد ذوي الحجا والحاجة إلى سنن ابن ماجه
+ - متون طالب العلم - الإضافية - 3
+ - المدخل إلى دراسة المذاهب الفقهية
+ - التفسير الموضوعي للقرآن الكريم ونماذج منه
+ - معجم قبائل العرب القديمة والحديثة
+ - محمد صلى الله عليه وسلم
+ - فقه الأدعية والأذكار
+ - يسألونك عن المعاملات المالية المعاصرة
+ - شرح العقيدة الأصفهانية
+ - الطبقات الكبرى - ط الخانجي
+ - الفقه والشريعة
+ - شرح سنن ابن ماجة - الراجحي
+ - الجامع في أمثال القرآن
+ - سبل السلام من صحيح سيرة خير الأنام عليه الصلاة والسلام
+ - موسوعة صناعة الحلال
+ - تفسير غريب ما في الصحيحين البخاري ومسلم
+ - "تفسير العثيمين: الفرقان"
+ - شرح الترغيب والترهيب للمنذرى - حطيبة
+ - خطب مختارة
+ - روضة المحبين ونزهة المشتاقين - ط عطاءات العلم
+ - الحديث الموضوعي المنهج والتأصيل والتمثيل
+ - مدخل إلى علوم الشريعة
+ - الإسلام والحكم
+ - "تفسير العثيمين: ص"
+ - المعجم الجامع في تراجم المعاصرين
+ - البلاغة العربية
+ - متون طالب العلم - المستوى الأول
+ - الأخلاق في الإسلام
+ - الموسوعة الفقهية
+ - البحر المديد في تفسير القرآن المجيد
+ - علم البيان
+ - التشريع الجنائي الإسلامي مقارنا بالقانون الوضعي
+ - "تفسير العثيمين: آل عمران"
+ - الأساس في السنة وفقهها - السيرة النبوية
+ - التوضيح والبيان لشجرة الإيمان
+ - معرفة القراء الكبار على الطبقات والأعصار
+ - تفسير البغوي - طيبة
+ - فتح القوي المتين في شرح الأربعين وتتمة الخمسين للنووي وابن رجب رحمهما
+ الله
+ - علم الفرائض والمواريث في الشريعة الإسلامية والقانون السوري
+ - نزول القرآن الكريم والعناية به في عهد الرسول صلى الله عليه وسلم
+ - بذل المجهود في حل سنن أبي داود
+ - مناهج البحث في العلوم السياسية
+ - فتاوى الطب والمرضى
+ - متون طالب العلم - الإضافية - 5
+ - تفسير الشعراوي
+ - برنامجك في رمضان
+ - شرح بلوغ المرام - عبد الكريم الخضير
+ - علم المعاني
+ - المنتخب من وصايا الآباء للأبناء
+ - مختصر سيرة الرسول صلى الله عليه وسلم لمحمد بن عبد الوهاب
+ - آل الجرباء في التاريخ والأدب
+ - البلاغة الصافية في المعاني والبيان والبديع
+ - شرح رسالة لطيفة جامعة في أصول الفقه المهمة
+ - "تفسير العثيمين: الصافات"
+ - التعريفات الفقهية
+ - متون طالب العلم - الإضافية - 6
+ - شرح العقيدة السفارينية
+ - تاريخ الدولة العلية العثمانية
+ - الجامع الصحيح للسيرة النبوية
+ - إسعاف الأعيان في أنساب أهل عمان
+ - أسرار البيان في التعبير القرآني - كتاب
+ - النظام القضائي في الفقه الإسلامي
+ - علم اللغة
+ - الفتاوى العالمكيرية = الفتاوى الهندية
+ - روح البيان
+ - اصطلاح المذهب عند المالكية
+ - التفسير الوسيط لطنطاوي
+ - مجموعة الرسائل والمسائل النجدية (الجزء الرابع، القسم الثاني)
+ - فقه عمل اليوم والليلة
+ - أصول الإيمان في ضوء الكتاب والسنة
+ - الإيمان لابن تيمية
+ - شرح العقيدة الطحاوية - عبد العزيز الراجحي
+ - "تفسير العثيمين: الكهف"
+ - أصول النحو 1 - جامعة المدينة
+ - وجوب تطبيق الشريعة الإسلامية في كل عصر
+ - الموسوعة الفقهية الكويتية
+ - موسوعة الإجماع في الفقه الإسلامي - ط الفضيلة
+ - صحيح سنن أبي داود ط غراس
+ - إعراب القرآن الكريم - ط دار الصحابة
+ - التفسير النبوي
+ - شرح العقيدة الواسطية - العثيمين
+ - المغني في ضبط الأسماء لرواة الأنباء
+ - كتاب سيرة النبي صلى الله عليه وسلم
+ - نور اليقين في سيرة سيد المرسلين
+ - لمحات مهمة في الوصية
+ - مختصر زاد المعاد
+ - العقيدة الصحيحة وما يضادها ونواقض الإسلام
+ - المعاجم العربية مع اعتناء خاص بمعجم العين للخليل بن أحمد
+ - مقتطفات من السيرة
+ - معجم اللغة العربية المعاصرة
+ - مدخل إلى تفسير القرآن وعلومه
+ - فقه النكاح والفرائض
+ - الحافظ العراقي وكتابه تكملة شرح الترمذي - مجلة الهند
+ - شرح العقيدة الطحاوية - صالح آل الشيخ = إتحاف السائل بما في الطحاوية من
+ مسائل
+ - تفسير الألوسي = روح المعاني
+ - سلسلة الآثار الصحيحة أو الصحيح المسند من أقوال الصحابة والتابعين
+ - حاشية السندي على سنن النسائي
+ - معجم لغة الفقهاء
+ - متون طالب العلم - المستوى الخامس - 1
+ - مختصر تفسير ابن كثير
+ - أوضح التفاسير
+ - شرح صحيح ابن حبان - الراجحي
+ - التحفة المكية في توضيح أهم القواعد الفقهية
+ - تاريخ الخلفاء الراشدين الفتوحات والإنجازات السياسية
+ - شرح متن أبي شجاع - محمد حسن عبد الغفار
+ - "تفسير العثيمين: من سورة محمد - ط مكتبة الطبري"
+ - تجريد القواعد والفوائد الأصولية
+ - شمائل الرسول صلى الله عليه وآله وسلم
+ - المنهج التأصيلي لدراسة التفسير التحليلي
+ - شرح مقدمة سنن ابن ماجه
+ - نظام الإثبات في الفقه الإسلامي
+ - الرسالة التبوكية زاد المهاجر إلى ربه - ت غازي
+ - شرح كتاب الإيمان الأوسط لابن تيمية - الراجحي
+ - موسوعة الأعلام - الأوقاف المصرية
+ - الكوكب الوهاج شرح صحيح مسلم بن الحجاج
+ - صحيح سنن النسائي
+ - شرح اختصار علوم الحديث - اللاحم
+ - مختار الصحاح
+ - أضواء البيان في إيضاح القرآن بالقرآن - ط عطاءات العلم
+ - مجلة الأحكام العدلية
+ - أسرار المحبين في رمضان
+ - السياسة الشرعية في الشئون الدستورية والخارجية والمالية
+ - نزول القرآن على سبعة أحرف
+ - دليل الحاج والمعتمر وزائر مسجد الرسول صلى الله عليه وسلم
+ - فتاوى عاجلة لمنسوبي الصحة
+ - المغالطات المنطقية
+ - فقه السنة
+ - من قضايا البلاغة والنقد عند عبد القادر الجرجاني
+ - مجموعة الرسائل والمسائل النجدية (الجزء الأول)
+ - متون طالب العلم - المستوى الثالث
+ - متون طالب العلم - المستوى الرابع
+ - تفسير القرآن الكريم وإعرابه وبيانه - الدرة
+ - فتاوى اللجنة الدائمة - المجموعة الثانية
+ - الموسوعة الميسرة في الأديان والمذاهب والأحزاب المعاصرة
+ - التحالف السياسي في الإسلام
+ title: BookNamesEnum
+ ChatCompletionAssistantMessageParam:
+ properties:
+ role:
+ type: string
+ const: assistant
+ title: Role
+ content:
+ anyOf:
+ - type: string
+ - items:
+ anyOf:
+ - $ref: "#/components/schemas/ChatCompletionContentPartTextParam"
+ - $ref: "#/components/schemas/ChatCompletionContentPartRefusalParam"
+ type: array
+ - type: "null"
+ title: Content
+ name:
+ type: string
+ title: Name
+ tool_calls:
+ anyOf:
+ - items:
+ $ref: "#/components/schemas/ChatCompletionToolCall"
+ type: array
+ - type: "null"
+ title: Tool Calls
+ type: object
+ required:
+ - role
+ title: ChatCompletionAssistantMessageParam
+ ChatCompletionChoice-Input:
+ properties:
+ finish_reason:
+ type: string
+ enum:
+ - stop
+ - length
+ - tool_calls
+ - content_filter
+ - function_call
+ title: Finish Reason
+ index:
+ type: integer
+ title: Index
+ logprobs:
+ anyOf:
+ - $ref: "#/components/schemas/ChoiceLogprobs-Input"
+ - type: "null"
+ message:
+ $ref: "#/components/schemas/ChatCompletionChoiceMessage-Input"
+ additionalProperties: true
+ type: object
+ required:
+ - finish_reason
+ - index
+ - message
+ title: ChatCompletionChoice
+ ChatCompletionChoice-Output:
+ properties:
+ finish_reason:
+ type: string
+ enum:
+ - stop
+ - length
+ - tool_calls
+ - content_filter
+ - function_call
+ title: Finish Reason
+ index:
+ type: integer
+ title: Index
+ logprobs:
+ anyOf:
+ - $ref: "#/components/schemas/ChoiceLogprobs-Output"
+ - type: "null"
+ message:
+ $ref: "#/components/schemas/ChatCompletionChoiceMessage-Output"
+ additionalProperties: true
+ type: object
+ required:
+ - finish_reason
+ - index
+ - message
+ title: ChatCompletionChoice
+ ChatCompletionChoiceMessage-Input:
+ properties:
+ content:
+ anyOf:
+ - type: string
+ - items:
+ anyOf:
+ - $ref: "#/components/schemas/ChatCompletionContentText"
+ - $ref: "#/components/schemas/ChatCompletionContentImage"
+ - $ref: "#/components/schemas/ChatCompletionContentAudio"
+ type: array
+ - type: "null"
+ title: Content
+ role:
+ type: string
+ const: assistant
+ title: Role
+ references:
+ anyOf:
+ - items:
+ $ref: "#/components/schemas/ChatCompletionChoiceMessageReference"
+ type: array
+ - type: "null"
+ title: references
+ description: The list of references used in the response
+ tool_calls:
+ anyOf:
+ - items:
+ $ref: "#/components/schemas/ChatCompletionToolCall"
+ type: array
+ - type: "null"
+ title: tool_calls
+ description: The list of tool calls made by the assistant.
+ type: object
+ required:
+ - role
+ title: ChatCompletionChoiceMessage
+ ChatCompletionChoiceMessage-Output:
+ properties:
+ content:
+ anyOf:
+ - type: string
+ - items:
+ anyOf:
+ - $ref: "#/components/schemas/ChatCompletionContentText"
+ - $ref: "#/components/schemas/ChatCompletionContentImage"
+ - $ref: "#/components/schemas/ChatCompletionContentAudio"
+ type: array
+ - type: "null"
+ title: Content
+ role:
+ type: string
+ const: assistant
+ title: Role
+ references:
+ anyOf:
+ - items:
+ $ref: "#/components/schemas/ChatCompletionChoiceMessageReference"
+ type: array
+ - type: "null"
+ title: references
+ description: The list of references used in the response
+ tool_calls:
+ anyOf:
+ - items:
+ $ref: "#/components/schemas/ChatCompletionToolCall"
+ type: array
+ - type: "null"
+ title: tool_calls
+ description: The list of tool calls made by the assistant.
+ type: object
+ required:
+ - role
+ title: ChatCompletionChoiceMessage
+ ChatCompletionChoiceMessageReference:
+ properties:
+ index:
+ description: The location of the reference in the response
+ title: index
+ type: integer
+ number:
+ description: The reference number in the response
+ title: number
+ type: integer
+ source:
+ description: The source of the reference
+ title: source
+ type: string
+ content:
+ description: The content of the reference
+ title: content
+ type: string
+ required:
+ - index
+ - number
+ - source
+ - content
+ title: ChatCompletionChoiceMessageReference
+ type: object
+ ChatCompletionContentAudio:
+ properties:
+ type:
+ type: string
+ const: audio_url
+ title: Type
+ audio_url:
+ $ref: "#/components/schemas/URL"
+ type: object
+ required:
+ - type
+ - audio_url
+ title: ChatCompletionContentAudio
+ ChatCompletionContentImage:
+ properties:
+ type:
+ type: string
+ const: image_url
+ title: Type
+ image_url:
+ $ref: "#/components/schemas/URL"
+ type: object
+ required:
+ - type
+ - image_url
+ title: ChatCompletionContentImage
+ ChatCompletionContentPartImageParam:
+ properties:
+ image_url:
+ $ref: "#/components/schemas/ImageURL"
+ type:
+ type: string
+ const: image_url
+ title: Type
+ type: object
+ required:
+ - image_url
+ - type
+ title: ChatCompletionContentPartImageParam
+ ChatCompletionContentPartRefusalParam:
+ properties:
+ refusal:
+ type: string
+ title: Refusal
+ type:
+ type: string
+ const: refusal
+ title: Type
+ type: object
+ required:
+ - refusal
+ - type
+ title: ChatCompletionContentPartRefusalParam
+ ChatCompletionContentPartTextParam:
+ properties:
+ text:
+ type: string
+ title: Text
+ type:
+ type: string
+ const: text
+ title: Type
+ type: object
+ required:
+ - text
+ - type
+ title: ChatCompletionContentPartTextParam
+ ChatCompletionContentPartVideoParam:
+ properties:
+ video_url:
+ $ref: "#/components/schemas/VideoURL"
+ type:
+ type: string
+ const: video_url
+ title: Type
+ type: object
+ required:
+ - video_url
+ - type
+ title: ChatCompletionContentPartVideoParam
+ ChatCompletionContentText:
+ properties:
+ type:
+ type: string
+ const: text
+ title: Type
+ text:
+ type: string
+ title: Text
+ type: object
+ required:
+ - type
+ - text
+ title: ChatCompletionContentText
+ ChatCompletionLLM:
+ type: string
+ enum:
+ - Fanar
+ - Fanar-S-1-7B
+ - Fanar-C-1-8.7B
+ - Fanar-C-2-27B
+ - Fanar-Sadiq
+ - Fanar-Sadiq-2
+ - Fanar-Oryx-IVU-2
+ title: ChatCompletionLLM
+ ChatCompletionRequest:
+ properties:
+ messages:
+ items:
+ anyOf:
+ - $ref: "#/components/schemas/ChatCompletionSystemMessageParam"
+ - $ref: "#/components/schemas/ChatCompletionUserMessageParam"
+ - $ref: "#/components/schemas/ChatCompletionAssistantMessageParam"
+ - $ref: "#/components/schemas/ChatCompletionThinkingUserMessageParam"
+ - $ref: "#/components/schemas/ChatCompletionThinkingMessageParam"
+ type: array
+ title: Messages
+ model:
+ $ref: "#/components/schemas/ChatCompletionLLM"
+ description: The model to use for the completion. For the Fanar-Sadiq and
+ Fanar-Sadiq-2 models, the following LLM parameters are not used.
+ enable_thinking:
+ anyOf:
+ - type: boolean
+ - type: "null"
+ title: Enable Thinking
+ description: Whether to enable the thinking role in the conversation. This only
+ applies if the model supports it. Currently, only the Fanar-C-2-27B
+ model supports this parameter with additional authorization.
+ default: false
+ frequency_penalty:
+ anyOf:
+ - type: number
+ - type: "null"
+ title: Frequency Penalty
+ description: A penalty for how much new tokens should avoid repeating existing
+ ones.
+ default: 0
+ logit_bias:
+ anyOf:
+ - additionalProperties:
+ type: number
+ type: object
+ - type: "null"
+ title: Logit Bias
+ description: Modify the likelihood of specified tokens appearing in the
+ completion.
+ logprobs:
+ anyOf:
+ - type: boolean
+ - type: "null"
+ title: Logprobs
+ description: Whether to return log probabilities of the output tokens or not.
+ top_logprobs:
+ anyOf:
+ - type: integer
+ - type: "null"
+ title: Top Logprobs
+ description: An integer between 0 and 20 specifying the number of most likely
+ tokens toreturn at each token position, each with an associated log
+ probability.
+ max_tokens:
+ anyOf:
+ - type: integer
+ - type: "null"
+ title: Max Tokens
+ description: The maximum number of tokens that can be generated in the chat
+ completion.
+ n:
+ anyOf:
+ - type: integer
+ - type: "null"
+ title: N
+ description: How many chat completion choices to generate for each input message.
+ presence_penalty:
+ anyOf:
+ - type: number
+ - type: "null"
+ title: Presence Penalty
+ description: Number between -2.0 and 2.0. Positive values penalize new tokens
+ based onwhether they appear in the text so far, increasing the
+ model's likelihood totalk about new topics.
+ stop:
+ anyOf:
+ - type: string
+ - items:
+ type: string
+ type: array
+ - type: "null"
+ title: Stop
+ description: Up to 4 sequences where the API will stop generating further tokens.
+ stream:
+ anyOf:
+ - type: boolean
+ - type: "null"
+ title: Stream
+ description: Whether to stream back partial progress as tokens are generated.
+ default: false
+ temperature:
+ anyOf:
+ - type: number
+ - type: "null"
+ title: Temperature
+ description: The sampling temperature, where 0.0 means deterministic output.
+ default: 0
+ top_p:
+ anyOf:
+ - type: number
+ - type: "null"
+ title: Top P
+ description: Controls the cumulative probability of the top tokens to consider.
+ best_of:
+ anyOf:
+ - type: integer
+ - type: "null"
+ title: Best Of
+ description: Number of output sequences that are generated from the prompt.
+ top_k:
+ anyOf:
+ - type: integer
+ - type: "null"
+ title: Top K
+ description: Controls the number of top tokens to consider.
+ min_p:
+ anyOf:
+ - type: number
+ - type: "null"
+ title: Min P
+ description: Represents the minimum probability for a token to be considered,
+ relative to the probability of the most likely token.
+ repetition_penalty:
+ anyOf:
+ - type: number
+ - type: "null"
+ title: Repetition Penalty
+ description: A penalty for repeating the same tokens in the generated output.
+ default: 1.1
+ length_penalty:
+ anyOf:
+ - type: number
+ - type: "null"
+ title: Length Penalty
+ description: Penalizes sequences based on their length. Used in beam search.
+ early_stopping:
+ anyOf:
+ - type: boolean
+ - type: "null"
+ title: Early Stopping
+ description: Controls the stopping condition for beam-based methods.
+ stop_token_ids:
+ anyOf:
+ - items:
+ type: integer
+ type: array
+ - type: "null"
+ title: Stop Token Ids
+ description: List of tokens that stop the generation when they are generated.
+ ignore_eos:
+ anyOf:
+ - type: boolean
+ - type: "null"
+ title: Ignore Eos
+ description: Whether to ignore the EOS token and continue generating tokens
+ after the EOS token is generated.
+ min_tokens:
+ anyOf:
+ - type: integer
+ - type: "null"
+ title: Min Tokens
+ description: Minimum number of tokens to generate per output sequence before EOS
+ or stop_token_ids can be generated.
+ skip_special_tokens:
+ anyOf:
+ - type: boolean
+ - type: "null"
+ title: Skip Special Tokens
+ description: Whether to skip special tokens in the output.
+ spaces_between_special_tokens:
+ anyOf:
+ - type: boolean
+ - type: "null"
+ title: Spaces Between Special Tokens
+ description: Whether to add spaces between special tokens in the output.
+ truncate_prompt_tokens:
+ anyOf:
+ - type: integer
+ - type: "null"
+ title: Truncate Prompt Tokens
+ description: If set to an integer k, will use only the last k tokens from the
+ prompt
+ prompt_logprobs:
+ anyOf:
+ - type: integer
+ - type: "null"
+ title: Prompt Logprobs
+ description: Number of log probabilities to return per prompt token.
+ book_names:
+ anyOf:
+ - items:
+ $ref: "#/components/schemas/BookNamesEnum"
+ type: array
+ - type: "null"
+ title: Book Names
+ description: List of book names to use for the Fanar-Sadiq model.
+ preferred_sources:
+ anyOf:
+ - items:
+ anyOf:
+ - $ref: "#/components/schemas/SourcesEnum"
+ - type: string
+ type: array
+ - type: "null"
+ title: Preferred Sources
+ description: List the preferred sources to use for the Fanar-Sadiq model, in
+ order of priority, with fallback sources used if needed. Accepts
+ predefined source names or values starting with 'digital_seerah'.
+ exclude_sources:
+ anyOf:
+ - items:
+ anyOf:
+ - $ref: "#/components/schemas/SourcesEnum"
+ - type: string
+ type: array
+ - type: "null"
+ title: Exclude Sources
+ description: List of sources to exclude from the Fanar-Sadiq model. Accepts
+ predefined source names or values starting with 'digital_seerah'.
+ filter_sources:
+ anyOf:
+ - items:
+ anyOf:
+ - $ref: "#/components/schemas/SourcesEnum"
+ - type: string
+ type: array
+ - type: "null"
+ title: Filter Sources
+ description: List of sources to filter from the Fanar-Sadiq model. Accepts
+ predefined source names or values starting with 'digital_seerah'.
+ madhab:
+ anyOf:
+ - items:
+ $ref: "#/components/schemas/MadhabEnum"
+ type: array
+ - type: "null"
+ title: Madhab
+ description: List of madhab (Islamic school of thought) to filter by for the
+ Fanar-Sadiq-2 model.
+ restrict_to_islamic:
+ anyOf:
+ - type: boolean
+ - type: "null"
+ title: Restrict To Islamic
+ description: When enabled for Fanar-Sadiq model, only Islamic content prompts
+ will be accepted. Non-Islamic content will be rejected.
+ default: false
+ persona:
+ anyOf:
+ - type: string
+ maxLength: 2000
+ - type: "null"
+ title: Persona
+ description: Custom persona that controls the assistant's voice and identity for
+ the Fanar-Sadiq model. Free-form text; only supported for
+ Fanar-Sadiq.
+ type: object
+ required:
+ - messages
+ - model
+ title: ChatCompletionRequest
+ example:
+ model: Fanar
+ messages:
+ - role: user
+ content: Hello
+ ChatCompletionRequestVoiceLangaugePair:
+ properties:
+ voice:
+ anyOf:
+ - type: string
+ - type: "null"
+ title: Voice
+ language:
+ type: string
+ title: Language
+ type: object
+ required:
+ - language
+ title: ChatCompletionRequestVoiceLangaugePair
+ ChatCompletionResponse:
+ properties:
+ id:
+ type: string
+ title: Id
+ description: A unique identifier for the chat completion
+ choices:
+ items:
+ $ref: "#/components/schemas/ChatCompletionChoice-Output"
+ type: array
+ title: Choices
+ description: A list of chat completion choices.Can be more than one if `n` is
+ greater than 1.
+ created:
+ type: integer
+ title: Created
+ description: The Unix timestamp (in seconds) of when the chat completion was
+ created.
+ model:
+ type: string
+ title: Model
+ description: The model used for the chat completion.
+ object:
+ type: string
+ const: chat.completion
+ title: Object
+ description: The object type
+ default: chat.completion
+ usage:
+ anyOf:
+ - $ref: "#/components/schemas/CompletionUsage"
+ - type: "null"
+ description: Usage statistics for the completion request.
+ metadata:
+ anyOf:
+ - additionalProperties: true
+ type: object
+ - type: "null"
+ title: Metadata
+ description: Additional metadata associated with the completion.
+ type: object
+ required:
+ - id
+ - choices
+ - created
+ - model
+ title: ChatCompletionResponse
+ ChatCompletionSystemMessageParam:
+ properties:
+ content:
+ anyOf:
+ - type: string
+ - items:
+ $ref: "#/components/schemas/ChatCompletionContentPartTextParam"
+ type: array
+ title: Content
+ role:
+ type: string
+ const: system
+ title: Role
+ name:
+ type: string
+ title: Name
+ type: object
+ required:
+ - content
+ - role
+ title: ChatCompletionSystemMessageParam
+ ChatCompletionThinkingMessageParam:
+ properties:
+ role:
+ type: string
+ const: thinking
+ title: Role
+ content:
+ type: string
+ title: Content
+ type: object
+ required:
+ - role
+ - content
+ title: ChatCompletionThinkingMessageParam
+ ChatCompletionThinkingUserMessageParam:
+ properties:
+ role:
+ type: string
+ const: thinking_user
+ title: Role
+ content:
+ type: string
+ title: Content
+ type: object
+ required:
+ - role
+ - content
+ title: ChatCompletionThinkingUserMessageParam
+ ChatCompletionTokenLogprob:
+ properties:
+ token:
+ type: string
+ title: Token
+ bytes:
+ anyOf:
+ - items:
+ type: integer
+ type: array
+ - type: "null"
+ title: Bytes
+ logprob:
+ type: number
+ title: Logprob
+ top_logprobs:
+ items:
+ $ref: "#/components/schemas/TopLogprob"
+ type: array
+ title: Top Logprobs
+ additionalProperties: true
+ type: object
+ required:
+ - token
+ - logprob
+ - top_logprobs
+ title: ChatCompletionTokenLogprob
+ ChatCompletionToolCall:
+ properties:
+ id:
+ type: string
+ title: id
+ description: The unique identifier for the tool call
+ name:
+ type: string
+ title: name
+ description: The name of the tool called
+ arguments:
+ additionalProperties: true
+ type: object
+ title: arguments
+ description: The arguments passed to the tool
+ result:
+ anyOf:
+ - {}
+ - type: "null"
+ title: result
+ description: The result returned by the tool
+ structured_content:
+ anyOf:
+ - {}
+ - type: "null"
+ title: structured_content
+ description: The structured content returned by the tool
+ is_error:
+ anyOf:
+ - type: boolean
+ - type: "null"
+ title: is_error
+ description: Indicates if there was an error during the tool call
+ type: object
+ required:
+ - id
+ - name
+ - arguments
+ title: ChatCompletionToolCall
+ ChatCompletionUserMessageParam:
+ properties:
+ content:
+ anyOf:
+ - type: string
+ - items:
+ anyOf:
+ - $ref: "#/components/schemas/ChatCompletionContentPartTextParam"
+ - $ref: "#/components/schemas/ChatCompletionContentPartImageParam"
+ - $ref: "#/components/schemas/ChatCompletionContentPartVideoParam"
+ type: array
+ title: Content
+ role:
+ type: string
+ const: user
+ title: Role
+ name:
+ type: string
+ title: Name
+ type: object
+ required:
+ - content
+ - role
+ title: ChatCompletionUserMessageParam
+ ChoiceLogprobs-Input:
+ properties:
+ content:
+ anyOf:
+ - items:
+ $ref: "#/components/schemas/ChatCompletionTokenLogprob"
+ type: array
+ - type: "null"
+ title: Content
+ refusal:
+ anyOf:
+ - items:
+ $ref: "#/components/schemas/ChatCompletionTokenLogprob"
+ type: array
+ - type: "null"
+ title: Refusal
+ additionalProperties: true
+ type: object
+ title: ChoiceLogprobs
+ ChoiceLogprobs-Output:
+ properties:
+ content:
+ anyOf:
+ - items:
+ $ref: "#/components/schemas/ChatCompletionTokenLogprob"
+ type: array
+ - type: "null"
+ title: Content
+ refusal:
+ anyOf:
+ - items:
+ $ref: "#/components/schemas/ChatCompletionTokenLogprob"
+ type: array
+ - type: "null"
+ title: Refusal
+ additionalProperties: true
+ type: object
+ title: ChoiceLogprobs
+ CompletionTokensDetails:
+ additionalProperties: true
+ properties:
+ accepted_prediction_tokens:
+ anyOf:
+ - type: integer
+ - type: "null"
+ default: null
+ title: Accepted Prediction Tokens
+ audio_tokens:
+ anyOf:
+ - type: integer
+ - type: "null"
+ default: null
+ title: Audio Tokens
+ reasoning_tokens:
+ anyOf:
+ - type: integer
+ - type: "null"
+ default: null
+ title: Reasoning Tokens
+ rejected_prediction_tokens:
+ anyOf:
+ - type: integer
+ - type: "null"
+ default: null
+ title: Rejected Prediction Tokens
+ title: CompletionTokensDetails
+ type: object
+ CompletionUsage:
+ additionalProperties: true
+ properties:
+ completion_tokens:
+ title: Completion Tokens
+ type: integer
+ prompt_tokens:
+ title: Prompt Tokens
+ type: integer
+ total_tokens:
+ title: Total Tokens
+ type: integer
+ completion_tokens_details:
+ anyOf:
+ - $ref: "#/components/schemas/CompletionTokensDetails"
+ - type: "null"
+ default: null
+ prompt_tokens_details:
+ anyOf:
+ - $ref: "#/components/schemas/PromptTokensDetails"
+ - type: "null"
+ default: null
+ required:
+ - completion_tokens
+ - prompt_tokens
+ - total_tokens
+ title: CompletionUsage
+ type: object
+ Error:
+ properties:
+ code:
+ $ref: "#/components/schemas/ErrorCode"
+ message:
+ type: string
+ title: Message
+ default: Internal server error
+ status:
+ $ref: "#/components/schemas/ErrorStatus"
+ default: 500
+ param:
+ anyOf:
+ - type: string
+ - type: "null"
+ title: Param
+ type:
+ anyOf:
+ - $ref: "#/components/schemas/ErrorContentFilterType"
+ - type: "null"
+ type: object
+ title: Error
+ ErrorCode:
+ type: string
+ enum:
+ - content_filter
+ - invalid_authentication
+ - invalid_authorization
+ - rate_limit_reached
+ - exceeded_quota
+ - internal_server_error
+ - overloaded
+ - timeout
+ - too_large
+ - unprocessable
+ - conflict
+ - Not found
+ - no_longer_supported
+ - client_closed_request
+ title: ErrorCode
+ ErrorContentFilterType:
+ type: string
+ enum:
+ - safety
+ - blocklist
+ - incomplete
+ title: ErrorContentFilterType
+ ErrorStatus:
+ type: integer
+ enum:
+ - 400
+ - 401
+ - 403
+ - 429
+ - 429
+ - 500
+ - 503
+ - 504
+ - 413
+ - 422
+ - 409
+ - 404
+ - 410
+ - 499
+ title: ErrorStatus
+ ImageGenerationItem-Input:
+ properties:
+ b64_json:
+ type: string
+ title: B64 Json
+ description: The base64-encoded JSON of the generated image.
+ model:
+ type: string
+ title: Model
+ revised:
+ type: boolean
+ title: Revised
+ description: Indicates whether the prompt was revised before generation.
+ revised_prompt:
+ type: string
+ title: Revised Prompt
+ description: The prompt used for generation, which may be revised from the
+ original prompt.
+ type: object
+ required:
+ - b64_json
+ - model
+ - revised
+ - revised_prompt
+ title: ImageGenerationItem
+ ImageGenerationItem-Output:
+ properties:
+ b64_json:
+ type: string
+ title: B64 Json
+ description: The base64-encoded JSON of the generated image.
+ revised:
+ type: boolean
+ title: Revised
+ description: Indicates whether the prompt was revised before generation.
+ revised_prompt:
+ type: string
+ title: Revised Prompt
+ description: The prompt used for generation, which may be revised from the
+ original prompt.
+ type: object
+ required:
+ - b64_json
+ - revised
+ - revised_prompt
+ title: ImageGenerationItem
+ ImageGenerationModels:
+ type: string
+ enum:
+ - Fanar-Oryx-IG-2
+ title: ImageGenerationModels
+ ImageGenerationRequest:
+ properties:
+ model:
+ $ref: "#/components/schemas/ImageGenerationModels"
+ description: The model to use for the image generation.
+ prompt:
+ type: string
+ title: Prompt
+ description: A text description of the desired image.
+ revise:
+ type: boolean
+ title: Revise
+ description: Whether to automatically revise the prompt to enhance style,
+ quality, and cultural alignment for improved generation results.
+ default: true
+ type: object
+ required:
+ - model
+ - prompt
+ title: ImageGenerationRequest
+ example:
+ model: Fanar-Oryx-IG-2
+ prompt: A futuristic cityscape at sunset
+ ImageGenerationResponse:
+ properties:
+ id:
+ type: string
+ title: Id
+ description: A unique identifier for the image generation.
+ created:
+ type: integer
+ title: Created
+ description: The timestamp of when the image was created.
+ data:
+ items:
+ $ref: "#/components/schemas/ImageGenerationItem-Output"
+ type: array
+ title: Data
+ description: A list of the generated image.
+ type: object
+ required:
+ - id
+ - created
+ - data
+ title: ImageGenerationResponse
+ ImageURL:
+ properties:
+ url:
+ type: string
+ title: Url
+ detail:
+ type: string
+ enum:
+ - auto
+ - low
+ - high
+ title: Detail
+ type: object
+ required:
+ - url
+ title: ImageURL
+ LLM:
+ type: string
+ enum:
+ - Fanar-S-1-7B
+ - Fanar-C-1-8.7B
+ - Fanar-C-2-27B
+ title: LLM
+ MadhabEnum:
+ type: string
+ enum:
+ - all
+ - hanafi
+ - maliki
+ - shafii
+ - hanbali
+ title: MadhabEnum
+ ModelObject:
+ type: string
+ enum:
+ - model
+ title: ModelObject
+ ModelsResponse:
+ properties:
+ id:
+ type: string
+ title: Id
+ description: A unique identifier for the models.
+ models:
+ items:
+ $ref: "#/components/schemas/AvailableModel"
+ type: array
+ title: Models
+ description: The available models.
+ type: object
+ required:
+ - id
+ - models
+ title: ModelsResponse
+ ModerationModels:
+ type: string
+ enum:
+ - Fanar-Guard-2
+ title: ModerationModels
+ PoemGenerationModels:
+ type: string
+ enum:
+ - Fanar-Diwan
+ title: PoemGenerationModels
+ PoemGenerationRequest:
+ properties:
+ model:
+ $ref: "#/components/schemas/PoemGenerationModels"
+ description: The model to use for the poem generation.
+ prompt:
+ type: string
+ title: Prompt
+ description: A text description of the desired poem.
+ type: object
+ required:
+ - model
+ - prompt
+ title: PoemGenerationRequest
+ example:
+ model: Fanar-Diwan
+ prompt: Write a poem about the sea
+ PoemGenerationResponse:
+ properties:
+ id:
+ type: string
+ title: Id
+ description: A unique identifier for the poem generation.
+ poem:
+ type: string
+ title: Poem
+ description: The generated poem.
+ type: object
+ required:
+ - id
+ - poem
+ title: PoemGenerationResponse
+ PromptTokensDetails:
+ additionalProperties: true
+ properties:
+ audio_tokens:
+ anyOf:
+ - type: integer
+ - type: "null"
+ default: null
+ title: Audio Tokens
+ cached_tokens:
+ anyOf:
+ - type: integer
+ - type: "null"
+ default: null
+ title: Cached Tokens
+ title: PromptTokensDetails
+ type: object
+ QuranReciters:
+ type: string
+ enum:
+ - abdul-basit
+ - maher-al-muaiqly
+ - mahmoud-al-husary
+ title: QuranReciters
+ STTFormat:
+ type: string
+ enum:
+ - text
+ - srt
+ - json
+ title: STTFormat
+ STTModels:
+ type: string
+ enum:
+ - Fanar-Aura-STT-1
+ - Fanar-Aura-STT-LF-1
+ title: STTModels
+ STTSegement:
+ properties:
+ speaker:
+ type: string
+ title: Speaker
+ description: The speaker label for the segment.
+ start_time:
+ type: number
+ title: Start Time
+ description: The start time of the segment in seconds.
+ end_time:
+ type: number
+ title: End Time
+ description: The end time of the segment in seconds.
+ duration:
+ type: number
+ title: Duration
+ description: The duration of the segment in seconds.
+ text:
+ type: string
+ title: Text
+ description: The transcribed text segment.
+ type: object
+ required:
+ - speaker
+ - start_time
+ - end_time
+ - duration
+ - text
+ title: STTSegement
+ SafetyFilterRequest:
+ properties:
+ model:
+ $ref: "#/components/schemas/ModerationModels"
+ description: The model to use for safety filtering.
+ prompt:
+ type: string
+ title: Prompt
+ description: The prompt.
+ response:
+ type: string
+ title: Response
+ description: The model's response to the prompt.
+ type: object
+ required:
+ - model
+ - prompt
+ - response
+ title: SafetyFilterRequest
+ example:
+ model: Fanar-Guard-2
+ prompt: What is the weather?
+ response: The weather is sunny today.
+ SafetyFilterResponse:
+ properties:
+ safety:
+ type: number
+ title: Safety
+ description: The safety score for general safety aspects such as toxicity,
+ violence, self-harm, etc.
+ cultural_awareness:
+ type: number
+ title: Cultural Awareness
+ description: The cultural awareness score for aspects such as stereotypes,
+ insensitive content, and bias.
+ type: object
+ required:
+ - safety
+ - cultural_awareness
+ title: SafetyFilterResponse
+ SourcesEnum:
+ type: string
+ enum:
+ - islam_qa
+ - islamweb
+ - islamweb_fatwa
+ - islamweb_consult
+ - islamweb_article
+ - islamweb_library
+ - sunnah
+ - quran
+ - tafsir
+ - dorar
+ - islamonline
+ - shamela
+ title: SourcesEnum
+ SpeechToTextResponseJson:
+ properties:
+ segments:
+ items:
+ $ref: "#/components/schemas/STTSegement"
+ type: array
+ title: Segments
+ description: The list of segments for the transcribed text.
+ type: object
+ required:
+ - segments
+ title: SpeechToTextResponseJson
+ SpeechToTextResponseWithJson-Input:
+ properties:
+ id:
+ type: string
+ title: Id
+ description: A unique identifier for the speech-to-text.
+ json:
+ $ref: "#/components/schemas/SpeechToTextResponseJson"
+ description: The transcribed text in JSON format.
+ type: object
+ required:
+ - id
+ - json
+ title: SpeechToTextResponseWithJson
+ SpeechToTextResponseWithJson-Output:
+ properties:
+ id:
+ type: string
+ title: Id
+ description: A unique identifier for the speech-to-text.
+ json:
+ $ref: "#/components/schemas/SpeechToTextResponseJson"
+ description: The transcribed text in JSON format.
+ type: object
+ required:
+ - id
+ - json
+ title: SpeechToTextResponseWithJson
+ SpeechToTextResponseWithSRT:
+ properties:
+ id:
+ type: string
+ title: Id
+ description: A unique identifier for the speech-to-text.
+ srt:
+ type: string
+ title: Srt
+ description: The transcribed text in SRT format.
+ type: object
+ required:
+ - id
+ - srt
+ title: SpeechToTextResponseWithSRT
+ SpeechToTextResponseWithText:
+ properties:
+ id:
+ type: string
+ title: Id
+ description: A unique identifier for the speech-to-text.
+ text:
+ type: string
+ title: Text
+ description: The transcribed text.
+ type: object
+ required:
+ - id
+ - text
+ title: SpeechToTextResponseWithText
+ TTSModels:
+ type: string
+ enum:
+ - Fanar-Aura-TTS-2
+ - Fanar-Sadiq-TTS-1
+ title: TTSModels
+ TTSResponseFormat:
+ type: string
+ enum:
+ - mp3
+ - wav
+ title: TTSResponseFormat
+ TextToSpeechRequest:
+ properties:
+ model:
+ $ref: "#/components/schemas/TTSModels"
+ description: The model to use for the text-to-speech.
+ input:
+ type: string
+ title: Input
+ description: The text to generate audio for.
+ voice:
+ type: string
+ title: Voice
+ description: |-
+ The voice to use for the text-to-speech. Details are below:
+
+ enum:
+ - Abdulrahman
+ - Amelia
+ - Emily
+ - Hamad
+ - Harry
+ - Huda
+ - Jake
+ - Jasim
+ - Noor
+ - Radwa
+ response_format:
+ $ref: "#/components/schemas/TTSResponseFormat"
+ description: The format of the output audio. Supported formats are `mp3` and
+ `wav`.
+ default: mp3
+ quran_reciter:
+ $ref: "#/components/schemas/QuranReciters"
+ description: The Quran reciter to use when using Fanar-Sadiq-TTS-1 model for
+ Quranic text.
+ default: abdul-basit
+ with_emotion:
+ type: boolean
+ title: With Emotion
+ description: "Enable emotional speech synthesis. **Only applicable to
+ `Fanar-Aura-TTS-2` and to voices where `emotion: true` in GET
+ /v1/voices.** When the selected voice does not support emotion, or
+ when used with `Fanar-Sadiq-TTS-1`, the request is rejected with a
+ 422 error. Defaults to false."
+ default: false
+ stream:
+ type: boolean
+ title: Stream
+ description: Stream the audio as it is generated. Supported for both `wav` and
+ `mp3`.
+ default: false
+ type: object
+ required:
+ - model
+ - input
+ - voice
+ title: TextToSpeechRequest
+ example:
+ model: Fanar-Aura-TTS-2
+ input: Hello, welcome to Fanar!
+ voice: Harry
+ TokenizationRequest:
+ properties:
+ content:
+ type: string
+ title: Content
+ model:
+ $ref: "#/components/schemas/LLM"
+ type: object
+ required:
+ - content
+ - model
+ title: TokenizationRequest
+ example:
+ content: Hello, how are you?
+ model: Fanar-S-1-7B
+ TokenizationResponse:
+ properties:
+ id:
+ type: string
+ title: Id
+ description: A unique identifier for the tokenization.
+ tokens:
+ type: integer
+ title: Tokens
+ max_request_tokens:
+ type: integer
+ title: Max Request Tokens
+ type: object
+ required:
+ - id
+ - tokens
+ - max_request_tokens
+ title: TokenizationResponse
+ TopLogprob:
+ properties:
+ token:
+ type: string
+ title: Token
+ bytes:
+ anyOf:
+ - items:
+ type: integer
+ type: array
+ - type: "null"
+ title: Bytes
+ logprob:
+ type: number
+ title: Logprob
+ additionalProperties: true
+ type: object
+ required:
+ - token
+ - logprob
+ title: TopLogprob
+ TranslationLangPairs:
+ type: string
+ enum:
+ - en-ar
+ - ar-en
+ title: TranslationLangPairs
+ TranslationModels:
+ type: string
+ enum:
+ - Fanar-Shaheen-MT-1
+ title: TranslationModels
+ TranslationPreprocessing:
+ type: string
+ enum:
+ - default
+ - preserve_html
+ - preserve_whitespace
+ - preserve_whitespace_and_html
+ title: TranslationPreprocessing
+ TranslationRequest:
+ properties:
+ model:
+ $ref: "#/components/schemas/TranslationModels"
+ description: The model to use for the translation.
+ text:
+ type: string
+ title: Text
+ description: The text to translate. It must not exceed 4,000 words.
+ langpair:
+ $ref: "#/components/schemas/TranslationLangPairs"
+ description: "The source-target language pair for translation, the current
+ allowed possible values are:Voice Gender Supported Languages Accent Emotion Abdulrahman Male Arabic Standard ✓ Amelia Female English British — Emily Female English American — Hamad Male Arabic Standard — Harry Male English British — Huda Female Arabic Standard — Jake Male English American — Jasim Male Arabic Standard — Noor Female Arabic Standard — Radwa Female Arabic Standard ✓
- en-ar: for English to
+ Arabic
- ar-en: for Arabic to English"
+ preprocessing:
+ anyOf:
+ - $ref: "#/components/schemas/TranslationPreprocessing"
+ - type: "null"
+ description: "How to preprocess the text before translation:
-
+ default: Splits all sentences by natural punctuation (full
+ stops, question marks, etc.), trims away extra whitespace, and
+ removes HTML tags.
- preserve_html: Does the same as
+ “default”, but tries to preserve HTML tags.
-
+ preserve_whitespace: Aggressively tries to maintain all extra
+ leading/trailing whitespaces and joins sentences across newlines to
+ translate fixed-width content, for example.
-
+ preserve_whitespace_and_html: Combines the previous two."
+ default: default
+ type: object
+ required:
+ - model
+ - text
+ - langpair
+ title: TranslationRequest
+ example:
+ model: Fanar-Shaheen-MT-1
+ text: Hello, how are you?
+ langpair: en-ar
+ TranslationResponse:
+ properties:
+ id:
+ type: string
+ title: Id
+ description: A unique identifier for the translation.
+ text:
+ type: string
+ title: Text
+ description: The translated text
+ type: object
+ required:
+ - id
+ - text
+ title: TranslationResponse
+ URL:
+ properties:
+ url:
+ type: string
+ title: Url
+ type: object
+ required:
+ - url
+ title: URL
+ VideoURL:
+ properties:
+ url:
+ type: string
+ title: Url
+ type: object
+ required:
+ - url
+ title: VideoURL
+ Voice:
+ properties:
+ name:
+ type: string
+ title: Name
+ description: The English name of the voice. This is the identifier passed to the
+ TTS endpoint.
+ examples:
+ - Amelia
+ name_ar:
+ anyOf:
+ - type: string
+ - type: "null"
+ title: Name Ar
+ description: The Arabic display name of the voice, when available.
+ examples:
+ - أميليا
+ gender:
+ anyOf:
+ - type: string
+ - type: "null"
+ title: Gender
+ description: Gender label of the voice (e.g., 'Male', 'Female').
+ examples:
+ - Female
+ accent:
+ anyOf:
+ - type: string
+ - type: "null"
+ title: Accent
+ description: Accent label of the voice (e.g., 'British', 'Gulf', 'American',
+ 'Standard').
+ examples:
+ - British
+ languages:
+ items:
+ type: string
+ type: array
+ title: Languages
+ description: Supported language codes (e.g., 'en', 'ar').
+ examples:
+ - - en
+ type:
+ type: string
+ enum:
+ - public
+ - personal
+ title: Type
+ description: Whether this is a built-in public voice or a personalized voice
+ registered for this API key.
+ examples:
+ - public
+ emotion:
+ type: boolean
+ title: Emotion
+ description: "Whether this voice supports emotional speech synthesis. When true,
+ you may set `with_emotion: true` on POST /v1/audio/speech to enable
+ emotional rendering."
+ default: false
+ examples:
+ - true
+ type: object
+ required:
+ - name
+ - type
+ title: Voice
+ example:
+ accent: British
+ emotion: false
+ gender: Female
+ languages:
+ - en
+ name: Amelia
+ name_ar: أميليا
+ type: public
+ VoiceResponse:
+ properties:
+ voices:
+ items:
+ $ref: "#/components/schemas/Voice"
+ type: array
+ title: Voices
+ description: Available voices. Always includes the built-in public voices.
+ Includes personalized voices registered for this API key when voice
+ personalization is authorized.
+ type: object
+ required:
+ - voices
+ title: VoiceResponse
+ example:
+ voices:
+ - accent: British
+ emotion: false
+ gender: Female
+ languages:
+ - en
+ name: Amelia
+ name_ar: أميليا
+ type: public
+ - accent: Gulf
+ emotion: false
+ gender: Male
+ languages:
+ - ar
+ name: Hamad
+ name_ar: حمد
+ type: public
+ - emotion: false
+ languages: []
+ name: MyVoice
+ type: personal
+ TokenChunk:
+ properties:
+ id:
+ description: A unique identifier for the chat completion
+ title: Id
+ type: string
+ object:
+ default: chat.completion.chunk
+ description: The object type
+ title: Object
+ type: string
+ created:
+ description: The Unix timestamp (in seconds) of when the chat completion was
+ created.
+ title: Created
+ type: integer
+ model:
+ description: The model used for the chat completion.
+ title: Model
+ type: string
+ choices:
+ description: A list of chat completion choices.Can be more than one if `n` is
+ greater than 1.
+ items:
+ $ref: "#/components/schemas/ChoiceToken"
+ title: Choices
+ type: array
+ required:
+ - id
+ - created
+ - model
+ - choices
+ title: TokenChunk
+ type: object
+ ChoiceToken:
+ properties:
+ index:
+ default: 0
+ title: Index
+ type: integer
+ finish_reason:
+ anyOf:
+ - type: string
+ - type: "null"
+ default: null
+ description: The reason the model stopped generating
+ title: Finish Reason
+ delta:
+ $ref: "#/components/schemas/DeltaContent"
+ required:
+ - delta
+ title: ChoiceToken
+ type: object
+ DeltaContent:
+ properties:
+ content:
+ example: Hello
+ title: Content
+ type: string
+ required:
+ - content
+ title: DeltaContent
+ type: object
+ ToolCallChunk:
+ properties:
+ id:
+ description: A unique identifier for the chat completion
+ title: Id
+ type: string
+ object:
+ default: chat.completion.chunk
+ description: The object type
+ title: Object
+ type: string
+ created:
+ description: The Unix timestamp (in seconds) of when the chat completion was
+ created.
+ title: Created
+ type: integer
+ model:
+ description: The model used for the chat completion.
+ title: Model
+ type: string
+ choices:
+ description: A list of chat completion choices.Can be more than one if `n` is
+ greater than 1.
+ items:
+ $ref: "#/components/schemas/ChoiceToolCall"
+ title: Choices
+ type: array
+ required:
+ - id
+ - created
+ - model
+ - choices
+ title: ToolCallChunk
+ type: object
+ ChoiceToolCall:
+ properties:
+ index:
+ default: 0
+ title: Index
+ type: integer
+ finish_reason:
+ anyOf:
+ - type: string
+ - type: "null"
+ default: null
+ description: The reason the model stopped generating
+ title: Finish Reason
+ delta:
+ $ref: "#/components/schemas/DeltaToolCalls"
+ required:
+ - delta
+ title: ChoiceToolCall
+ type: object
+ DeltaToolCalls:
+ properties:
+ tool_calls:
+ description: The list of tool calls that need to be executed.
+ items:
+ $ref: "#/components/schemas/ToolCallData"
+ title: Tool Calls
+ type: array
+ required:
+ - tool_calls
+ title: DeltaToolCalls
+ type: object
+ FunctionData:
+ description: Function call details
+ properties:
+ name:
+ description: The name of the function to call
+ title: Name
+ type: string
+ arguments:
+ description: The arguments to pass to the function as JSON string
+ title: Arguments
+ type: string
+ required:
+ - name
+ - arguments
+ title: FunctionData
+ type: object
+ ToolCallData:
+ description: Tool call in delta.tool_calls
+ properties:
+ index:
+ description: The index of the tool call in the list
+ title: Index
+ type: integer
+ id:
+ description: The unique identifier for the tool call
+ title: Id
+ type: string
+ type:
+ default: function
+ description: The type of the tool call
+ title: Type
+ type: string
+ function:
+ $ref: "#/components/schemas/FunctionData"
+ required:
+ - index
+ - id
+ - function
+ title: ToolCallData
+ type: object
+ ToolResultChunk:
+ properties:
+ id:
+ description: A unique identifier for the chat completion
+ title: Id
+ type: string
+ object:
+ default: chat.completion.chunk
+ description: The object type
+ title: Object
+ type: string
+ created:
+ description: The Unix timestamp (in seconds) of when the chat completion was
+ created.
+ title: Created
+ type: integer
+ model:
+ description: The model used for the chat completion.
+ title: Model
+ type: string
+ choices:
+ description: A list of chat completion choices.Can be more than one if `n` is
+ greater than 1.
+ items:
+ $ref: "#/components/schemas/ChoiceToolResult"
+ title: Choices
+ type: array
+ required:
+ - id
+ - created
+ - model
+ - choices
+ title: ToolResultChunk
+ type: object
+ ChoiceToolResult:
+ properties:
+ index:
+ default: 0
+ title: Index
+ type: integer
+ finish_reason:
+ anyOf:
+ - type: string
+ - type: "null"
+ default: null
+ description: The reason the model stopped generating
+ title: Finish Reason
+ delta:
+ $ref: "#/components/schemas/DeltaToolResult"
+ required:
+ - delta
+ title: ChoiceToolResult
+ type: object
+ DeltaToolResult:
+ properties:
+ tool_result:
+ $ref: "#/components/schemas/ToolResultData"
+ description: The list of tool call results returned by the executed tools.
+ required:
+ - tool_result
+ title: DeltaToolResult
+ type: object
+ ToolResultData:
+ description: Tool result data
+ properties:
+ id:
+ description: The unique identifier for the tool call
+ title: Id
+ type: string
+ name:
+ anyOf:
+ - type: string
+ - type: "null"
+ default: null
+ description: The name of the tool called
+ title: Name
+ arguments:
+ additionalProperties: true
+ description: The arguments passed to the tool
+ title: Arguments
+ type: object
+ result:
+ anyOf:
+ - type: string
+ - type: "null"
+ default: null
+ description: The result returned by the tool
+ title: Result
+ structured_content:
+ anyOf:
+ - additionalProperties: true
+ type: object
+ - type: "null"
+ default: null
+ description: The structured content returned by the tool
+ title: Structured Content
+ is_error:
+ default: false
+ description: Indicates if there was an error during the tool call
+ title: Is Error
+ type: boolean
+ required:
+ - id
+ title: ToolResultData
+ type: object
+ ProgressChunk:
+ properties:
+ id:
+ description: A unique identifier for the chat completion
+ title: Id
+ type: string
+ object:
+ default: chat.completion.chunk
+ description: The object type
+ title: Object
+ type: string
+ created:
+ description: The Unix timestamp (in seconds) of when the chat completion was
+ created.
+ title: Created
+ type: integer
+ model:
+ description: The model used for the chat completion.
+ title: Model
+ type: string
+ progress:
+ $ref: "#/components/schemas/ProgressData"
+ description: Progress event indicating current processing step
+ required:
+ - id
+ - created
+ - model
+ - progress
+ title: ProgressChunk
+ type: object
+ ProgressData:
+ description: Progress event data
+ properties:
+ message:
+ $ref: "#/components/schemas/ProgressMessage"
+ description: Bilingual progress message indicating current processing step
+ required:
+ - message
+ title: ProgressData
+ type: object
+ ProgressMessage:
+ description: Bilingual progress message
+ properties:
+ en:
+ description: Progress message in English
+ title: En
+ type: string
+ ar:
+ description: Progress message in Arabic
+ title: Ar
+ type: string
+ required:
+ - en
+ - ar
+ title: ProgressMessage
+ type: object
+ DoneChunk:
+ properties:
+ id:
+ description: A unique identifier for the chat completion
+ title: Id
+ type: string
+ object:
+ default: chat.completion.chunk
+ description: The object type
+ title: Object
+ type: string
+ created:
+ description: The Unix timestamp (in seconds) of when the chat completion was
+ created.
+ title: Created
+ type: integer
+ model:
+ description: The model used for the chat completion.
+ title: Model
+ type: string
+ choices:
+ description: List of choices with final delta
+ items:
+ $ref: "#/components/schemas/ChoiceFinal"
+ title: Choices
+ type: array
+ usage:
+ anyOf:
+ - $ref: "#/components/schemas/CompletionUsage"
+ - type: "null"
+ default: null
+ description: Usage statistics for the completion request.
+ metadata:
+ anyOf:
+ - additionalProperties: true
+ type: object
+ - type: "null"
+ default: null
+ description: Additional metadata associated with the completion.
+ title: Metadata
+ required:
+ - id
+ - created
+ - model
+ - choices
+ title: DoneChunk
+ type: object
+ ChoiceFinal:
+ properties:
+ index:
+ default: 0
+ title: Index
+ type: integer
+ finish_reason:
+ default: stop
+ title: Finish Reason
+ type: string
+ delta:
+ $ref: "#/components/schemas/DeltaReferences"
+ required:
+ - delta
+ title: ChoiceFinal
+ type: object
+ DeltaReferences:
+ properties:
+ references:
+ anyOf:
+ - items:
+ $ref: "#/components/schemas/ChatCompletionChoiceMessageReference"
+ type: array
+ - type: "null"
+ default: null
+ description: The list of references used in the response
+ title: References
+ title: DeltaReferences
+ type: object
+ ErrorChunk:
+ description: Error chunk
+ properties:
+ id:
+ description: A unique identifier for the chat completion
+ title: Id
+ type: string
+ object:
+ default: chat.completion.chunk
+ description: The object type
+ title: Object
+ type: string
+ created:
+ description: The Unix timestamp (in seconds) of when the chat completion was
+ created.
+ title: Created
+ type: integer
+ model:
+ description: The model used for the chat completion.
+ title: Model
+ type: string
+ choices:
+ items:
+ $ref: "#/components/schemas/ChoiceError"
+ title: Choices
+ type: array
+ required:
+ - id
+ - created
+ - model
+ - choices
+ title: ErrorChunk
+ type: object
+ ChoiceError:
+ properties:
+ index:
+ default: 0
+ title: Index
+ type: integer
+ delta:
+ $ref: "#/components/schemas/DeltaContent"
+ finish_reason:
+ default: error
+ title: Finish Reason
+ type: string
+ required:
+ - delta
+ title: ChoiceError
+ type: object
+ securitySchemes:
+ Bearer:
+ type: http
+ scheme: bearer
+ description: Provide your API key in the Authorization header using the Bearer scheme.
+security:
+ - Bearer: []
+tags:
+ - name: Authentication
+ description: >-
+ All API endpoints require a **Bearer** token. Include it in the
+ `Authorization` header:
+
+
+ ```
+
+ Authorization: Bearer YOUR_API_KEY
+
+ ```
diff --git a/core/src/main/java/qa/fanar/core/ErrorCode.java b/core/src/main/java/qa/fanar/core/ErrorCode.java
index 727f63c..fa05b4e 100644
--- a/core/src/main/java/qa/fanar/core/ErrorCode.java
+++ b/core/src/main/java/qa/fanar/core/ErrorCode.java
@@ -55,7 +55,10 @@ public enum ErrorCode {
NOT_FOUND("Not found"),
/** Feature, model, or endpoint no longer supported. */
- NO_LONGER_SUPPORTED("no_longer_supported");
+ NO_LONGER_SUPPORTED("no_longer_supported"),
+
+ /** The client closed the connection before the server finished responding (HTTP 499). */
+ CLIENT_CLOSED_REQUEST("client_closed_request");
private final String wireValue;
diff --git a/core/src/main/java/qa/fanar/core/FanarClientClosedRequestException.java b/core/src/main/java/qa/fanar/core/FanarClientClosedRequestException.java
new file mode 100644
index 0000000..c862f3b
--- /dev/null
+++ b/core/src/main/java/qa/fanar/core/FanarClientClosedRequestException.java
@@ -0,0 +1,22 @@
+package qa.fanar.core;
+
+/**
+ * The client closed the connection before Fanar finished responding. Maps to
+ * {@link ErrorCode#CLIENT_CLOSED_REQUEST} and HTTP 499.
+ *
+ *
- *
*
- *
The wire field {@code stream} is not modelled here. Buffered vs. streamed delivery + * is a call-site choice on the audio facade ({@code speech(request)} vs. + * {@code speechStream(request)}), and the transport sets the wire field accordingly.
* * @param model the TTS model to use; must not be {@code null} * @param input the text to synthesize; must not be {@code null} @@ -17,6 +24,7 @@ * @param responseFormat audio container ({@link TtsResponseFormat#MP3} or * {@link TtsResponseFormat#WAV}); {@code null} → server default (mp3) * @param quranReciter reciter selection for the Sadiq TTS model; {@code null} → server default + * @param withEmotion enable emotional speech synthesis; {@code null} → server default (off) * * @author Oussama Mahjoub */ @@ -25,18 +33,61 @@ public record TextToSpeechRequest( String input, Voice voice, TtsResponseFormat responseFormat, - QuranReciter quranReciter + QuranReciter quranReciter, + Boolean withEmotion ) { public TextToSpeechRequest { Objects.requireNonNull(model, "model"); Objects.requireNonNull(input, "input"); Objects.requireNonNull(voice, "voice"); - // responseFormat + quranReciter nullable — server applies its defaults + // responseFormat + quranReciter + withEmotion nullable — server applies its defaults } - /** Static factory for the common path: model + text + voice, server-default format/reciter. */ + /** Static factory for the common path: model + text + voice, server defaults elsewhere. */ public static TextToSpeechRequest of(TtsModel model, String input, Voice voice) { - return new TextToSpeechRequest(model, input, voice, null, null); + return new TextToSpeechRequest(model, input, voice, null, null, null); + } + + /** Start a fresh builder. */ + public static Builder builder() { + return new Builder(); + } + + /** + * Fluent builder for {@link TextToSpeechRequest}. Every optional field defaults to + * {@code null}, meaning "use the server default". + * + *{@link #build()} delegates to the record's canonical constructor and therefore runs + * the same validation — missing required fields throw at {@link #build()}, never later.
+ */ + public static final class Builder { + + private TtsModel model; + private String input; + private Voice voice; + private TtsResponseFormat responseFormat; + private QuranReciter quranReciter; + private Boolean withEmotion; + + private Builder() { + // use TextToSpeechRequest.builder() + } + + public Builder model(TtsModel model) { this.model = model; return this; } + public Builder input(String input) { this.input = input; return this; } + public Builder voice(Voice voice) { this.voice = voice; return this; } + public Builder responseFormat(TtsResponseFormat responseFormat) { this.responseFormat = responseFormat; return this; } + public Builder quranReciter(QuranReciter quranReciter) { this.quranReciter = quranReciter; return this; } + public Builder withEmotion(Boolean withEmotion) { this.withEmotion = withEmotion; return this; } + + /** + * Validate and build the {@link TextToSpeechRequest}. + * + * @throws NullPointerException if a required field is missing + */ + public TextToSpeechRequest build() { + return new TextToSpeechRequest(model, input, voice, responseFormat, quranReciter, withEmotion); + } } } diff --git a/core/src/main/java/qa/fanar/core/audio/Voice.java b/core/src/main/java/qa/fanar/core/audio/Voice.java index ef305d9..12bce4a 100644 --- a/core/src/main/java/qa/fanar/core/audio/Voice.java +++ b/core/src/main/java/qa/fanar/core/audio/Voice.java @@ -18,6 +18,9 @@ */ public record Voice(String wireValue) { + /** Male, Standard — Arabic. Supports emotional synthesis ({@code with_emotion}). */ + public static final Voice ABDULRAHMAN = new Voice("Abdulrahman"); + /** Female, British accent — English. */ public static final Voice AMELIA = new Voice("Amelia"); @@ -42,10 +45,13 @@ public record Voice(String wireValue) { /** Female, Standard — Arabic. */ public static final Voice NOOR = new Voice("Noor"); + /** Female, Standard — Arabic. Supports emotional synthesis ({@code with_emotion}). */ + public static final Voice RADWA = new Voice("Radwa"); + /** Snapshot of the SDK's bundled built-in voices. Custom voices created via the API are * outside this set but still valid via {@link #of(String)}. */ public static final SetThe list contains voice names suitable for use in {@code TextToSpeechRequest.voice()}. - * The 8 built-in voices ({@link Voice#KNOWN}) are not included; this list is solely - * the user-created voices.
+ *The listing always includes the built-in public voices and additionally the personalized + * voices registered for this API key when voice personalization is authorized. Each entry is a + * rich {@link AvailableVoice}; its {@link AvailableVoice#name() name} is the identifier accepted + * by {@code TextToSpeechRequest.voice()}.
* - * @param voices voice names, defensively copied and unmodifiable + * @param voices the available voices, defensively copied and unmodifiable * * @author Oussama Mahjoub */ -public record VoiceResponse(ListMirrors the {@code type} enum on the OpenAPI {@code Voice} schema — but open: if Fanar adds + * a new voice category, decoding still succeeds via {@link #of(String)} without waiting for an + * SDK release.
+ * + * @param wireValue the exact string Fanar uses on the wire for this voice type + * + * @author Oussama Mahjoub + */ +public record VoiceType(String wireValue) { + + /** A built-in voice available to every API key. */ + public static final VoiceType PUBLIC = new VoiceType("public"); + + /** A personalized voice registered for this API key via {@code AudioClient.createVoice(...)}. */ + public static final VoiceType PERSONAL = new VoiceType("personal"); + + /** Snapshot of the SDK's bundled constants. */ + public static final SetCollections are defensively copied on construction and returned as unmodifiable views. * Validation follows ADR-015: required-field non-null, well-defined range checks on numeric * fields, and size limits where the wire spec documents them. Model-specific rules — for - * example, {@code enableThinking} only applying to {@code Fanar-C-2-27B} — are Fanar's - * responsibility; the SDK surfaces the server's rejection via the typed exception hierarchy.
+ * example, {@code enableThinking} only applying to {@code Fanar-C-2-27B}, {@code persona} to + * {@code Fanar-Sadiq}, or {@code madhab} to {@code Fanar-Sadiq-2} — are Fanar's responsibility; + * the SDK surfaces the server's rejection via the typed exception hierarchy. * *The wire field {@code stream} is not modelled here. Streaming vs. non-streaming is
* a call-site choice on the domain facade (for example {@code client.chat().stream(request)}),
@@ -65,7 +66,9 @@ public record ChatRequest(
List Mirrors the {@code MadhabEnum} in the Fanar OpenAPI spec — but open: if Fanar adds a new
+ * school, callers can target it via {@link #of(String)} without waiting for an SDK release.
+ * Used on the {@code ChatRequest} {@code madhab} field, which only the {@code Fanar-Sadiq-2}
+ * model honours. Per the OpenAPI spec, the only shape Fanar emits is base64-encoded bytes via
+ * Per the OpenAPI spec, the only image shape Fanar emits is base64-encoded bytes via
* {@code b64_json}. If Fanar later adds a URL-output variant, this type would become a sealed
* hierarchy ({@code ImageGenerationItem} → {@code Base64Item} / {@code UrlItem}); for now the
* record stays flat to match what the server actually sends. Structural twin of {@code qa.fanar.core.internal.sse.SseStreamPublisher}, minus frame
+ * assembly and JSON decoding — audio chunks are opaque bytes in whatever container the request
+ * selected (mp3 or wav). Chunk boundaries follow transport reads and carry no semantic meaning;
+ * consumers concatenate them in emission order to reconstruct the full clip. Single-subscriber by construction: subscribing twice triggers {@code onError} on the second
+ * subscriber. The first subscription launches a virtual thread that pulls chunks from the
+ * underlying {@link InputStream} and honours the subscriber's {@code request(long)} demand
+ * before every {@code onNext}. Cancellation closes the stream. Internal (ADR-018). Parsed by a small hand-rolled scanner rather than the {@link
+ * qa.fanar.core.spi.FanarJsonCodec} SPI: codec implementations reflect over target types, and
+ * this package is deliberately not exported (ADR-018), so a codec running as a JPMS module could
+ * not access an envelope DTO defined here. The envelope is a three-field, spec-pinned shape; the
+ * scanner is strict about JSON syntax but any deviation from the expected shape yields
+ * {@code null}, letting the {@link ExceptionMapper} fall back to HTTP-status routing. Internal (ADR-018). This first pass distinguishes exceptions by HTTP status only. A later PR will parse the
- * typed {@code ErrorCode} from the response body so we can, for example, distinguish
- * {@link FanarRateLimitException} (transient) from {@code FanarQuotaExceededException}
- * (permanent) — both HTTP 429. For now HTTP 429 always maps to rate-limit, which is the safe
- * default (the retry interceptor will give up after the configured attempt count regardless). Routing is two-stage. When the body is a well-formed Fanar error envelope
+ * ({@code {"error":{"code":…,"message":…,"status":…}}}), the typed {@link ErrorCode} decides the
+ * subtype — this is what distinguishes {@link FanarQuotaExceededException} (permanent) from
+ * {@link FanarRateLimitException} (transient), both HTTP 429, and keeps a non-filter 400 from
+ * masquerading as a {@link FanarContentFilterException}. When the body is anything else (blank,
+ * HTML from an intermediary, truncated JSON) or carries an unknown code, the HTTP status decides. Reads and closes the response body. The error message is the body text when non-blank,
- * falling back to a canonical status description otherwise. The {@code Retry-After} header is
- * honoured for HTTP 429. Reads and closes the response body. The exception message is the envelope's {@code message}
+ * when present, the raw body text otherwise, falling back to a canonical status description when
+ * both are blank. The {@code Retry-After} header is honoured for rate-limit errors. Internal (ADR-018). Request records deliberately do not model the wire field {@code stream} — buffered vs.
+ * streamed delivery is a call-site choice on the domain facade (chat {@code send} vs.
+ * {@code stream}, audio {@code speech} vs. {@code speechStream}), so the flag is spliced into
+ * the already-encoded JSON instead. Internal (ADR-018).
Portable {@link ChatOptions} map to their Fanar equivalents; pass a + * {@link FanarChatOptions} to additionally reach the Fanar-only knobs (persona, madhab, + * thinking mode, Islamic-RAG scoping, vLLM sampling) — see ADR-024.
+ * *What the adapter does not do:
*Pass an instance as the prompt's options ({@code ChatClient.prompt().options(...)} or + * {@code new Prompt(messages, options)}); {@code FanarChatModel} maps the portable getters like + * any {@link ChatOptions} and additionally applies the Fanar extras. Any other + * {@link ChatOptions} implementation keeps working — the extras are then simply unset + * (ADR-024).
+ * + *{@link Builder} extends Spring AI's {@link DefaultChatOptionsBuilder}, so + * {@link #mutate()} round-trips all fields — including the Fanar extras — through the + * {@code ChatClient} pipeline (which rebuilds request options via {@code mutate()}), and + * {@link Builder#combineWith(ChatOptions.Builder)} merges extras when combining two Fanar + * builders (non-null values from the other builder win; the portable fields follow Spring AI's + * own merge rules).
+ * + *Instances are immutable; collections are defensively copied at build time. Field semantics + * and validation mirror {@code qa.fanar.core.chat.ChatRequest} — validation happens when the + * request is built, not here.
+ * + * @author Oussama Mahjoub + */ +public final class FanarChatOptions implements ChatOptions { + + // --- portable (ChatOptions) --- + private final String model; + private final Double temperature; + private final Double topP; + private final Integer topK; + private final Integer maxTokens; + private final Double frequencyPenalty; + private final Double presencePenalty; + private final ListExtends {@link DefaultChatOptionsBuilder} so Spring AI treats it as a first-class + * {@link ChatOptions.Builder}: the portable setters, {@code clone()}, and the portable half + * of {@code combineWith(...)} are inherited. The override below additionally merges the + * Fanar extras when the other builder is also a {@code FanarChatOptions.Builder} — + * non-null scalar and collection values from {@code other} replace this builder's values + * (collections replace rather than concatenate: they are filters, and appending two + * filters is not a meaningful union).
+ */ + public static final class Builder extends DefaultChatOptionsBuilderRetrieve via {@code imageResponse.getResult().getMetadata()} and narrow with + * {@code instanceof FanarImageGenerationMetadata} — the same access pattern other Spring AI + * providers use for their revised-prompt metadata.
+ * + * @param revised whether Fanar revised the prompt before generation + * @param revisedPrompt the prompt used for generation — equal to the request prompt when + * {@code revised} is {@code false} + * + * @author Oussama Mahjoub + */ +public record FanarImageGenerationMetadata(boolean revised, String revisedPrompt) + implements ImageGenerationMetadata { + + public FanarImageGenerationMetadata { + Objects.requireNonNull(revisedPrompt, "revisedPrompt"); + } +} diff --git a/spring-ai-starter/src/main/java/qa/fanar/spring/ai/FanarImageModel.java b/spring-ai-starter/src/main/java/qa/fanar/spring/ai/FanarImageModel.java index a3c6dad..2da3bd2 100644 --- a/spring-ai-starter/src/main/java/qa/fanar/spring/ai/FanarImageModel.java +++ b/spring-ai-starter/src/main/java/qa/fanar/spring/ai/FanarImageModel.java @@ -10,6 +10,7 @@ import org.springframework.ai.image.ImageOptions; import org.springframework.ai.image.ImagePrompt; import org.springframework.ai.image.ImageResponse; +import org.springframework.ai.image.ImageResponseMetadata; import qa.fanar.core.FanarClient; import qa.fanar.core.images.ImageGenerationRequest; @@ -35,6 +36,10 @@ *Pass a {@link FanarImageOptions} to reach Fanar's {@code revise} flag (automatic prompt + * revision, server default {@code true}) — see ADR-024. Each result carries a + * {@link FanarImageGenerationMetadata} with the revision outcome.
+ * * @author Oussama Mahjoub */ public final class FanarImageModel implements ImageModel { @@ -57,11 +62,19 @@ public FanarImageModel(FanarClient fanar, qa.fanar.core.images.ImageModel defaul @Override public ImageResponse call(ImagePrompt prompt) { Objects.requireNonNull(prompt, "prompt"); - ImageGenerationRequest request = new ImageGenerationRequest(resolveModel(prompt), promptText(prompt)); + ImageGenerationRequest request = new ImageGenerationRequest( + resolveModel(prompt), promptText(prompt), resolveRevise(prompt)); ImageGenerationResponse fanarResponse = fanar.images().generate(request); return toSpringAiResponse(fanarResponse); } + private static Boolean resolveRevise(ImagePrompt prompt) { + // Fanar extra beyond the portable ImageOptions surface (ADR-024); null → server default. + return prompt.getOptions() instanceof FanarImageOptions fanarOptions + ? fanarOptions.getRevise() + : null; + } + private qa.fanar.core.images.ImageModel resolveModel(ImagePrompt prompt) { ImageOptions options = prompt.getOptions(); if (options != null && options.getModel() != null && !options.getModel().isBlank()) { @@ -90,8 +103,10 @@ private static String promptText(ImagePrompt prompt) { private static ImageResponse toSpringAiResponse(ImageGenerationResponse fanarResponse) { ListPass an instance on the {@code ImagePrompt}; {@code FanarImageModel} maps + * {@link #getModel()} like any {@link ImageOptions} and additionally applies {@code revise}. + * The remaining portable getters (n, width, height, response format, style) have no Fanar wire + * field and are dropped, as documented on the adapter. Any other implementation keeps working — + * {@code revise} is then simply unset (ADR-024).
+ * + * @author Oussama Mahjoub + */ +public final class FanarImageOptions implements ImageOptions { + + private final String model; + private final Integer n; + private final Integer width; + private final Integer height; + private final String responseFormat; + private final String style; + private final Boolean revise; + + private FanarImageOptions(Builder b) { + this.model = b.model; + this.n = b.n; + this.width = b.width; + this.height = b.height; + this.responseFormat = b.responseFormat; + this.style = b.style; + this.revise = b.revise; + } + + /** Start a fresh builder. */ + public static Builder builder() { + return new Builder(); + } + + @Override public String getModel() { return model; } + @Override public Integer getN() { return n; } + @Override public Integer getWidth() { return width; } + @Override public Integer getHeight() { return height; } + @Override public String getResponseFormat() { return responseFormat; } + @Override public String getStyle() { return style; } + + /** + * Whether Fanar may auto-revise the prompt (server default {@code true}); {@code false} + * keeps the prompt verbatim, {@code null} accepts the default. + */ + public Boolean getRevise() { return revise; } + + /** Fluent builder; every field defaults to {@code null} ("use the adapter/server default"). */ + public static final class Builder { + + private String model; + private Integer n; + private Integer width; + private Integer height; + private String responseFormat; + private String style; + private Boolean revise; + + private Builder() { + // use FanarImageOptions.builder() + } + + public Builder model(String model) { this.model = model; return this; } + public Builder n(Integer n) { this.n = n; return this; } + public Builder width(Integer width) { this.width = width; return this; } + public Builder height(Integer height) { this.height = height; return this; } + public Builder responseFormat(String responseFormat) { this.responseFormat = responseFormat; return this; } + public Builder style(String style) { this.style = style; return this; } + public Builder revise(Boolean revise) { this.revise = revise; return this; } + + public FanarImageOptions build() { + return new FanarImageOptions(this); + } + } +} diff --git a/spring-ai-starter/src/main/java/qa/fanar/spring/ai/FanarTextToSpeechModel.java b/spring-ai-starter/src/main/java/qa/fanar/spring/ai/FanarTextToSpeechModel.java index 04dba8e..821a81a 100644 --- a/spring-ai-starter/src/main/java/qa/fanar/spring/ai/FanarTextToSpeechModel.java +++ b/spring-ai-starter/src/main/java/qa/fanar/spring/ai/FanarTextToSpeechModel.java @@ -8,6 +8,7 @@ import org.springframework.ai.audio.tts.TextToSpeechOptions; import org.springframework.ai.audio.tts.TextToSpeechPrompt; import org.springframework.ai.audio.tts.TextToSpeechResponse; +import reactor.adapter.JdkFlowAdapter; import reactor.core.publisher.Flux; import qa.fanar.core.FanarClient; @@ -22,13 +23,16 @@ *Maps Spring AI's {@link TextToSpeechPrompt} + {@link TextToSpeechOptions} onto a Fanar * {@link TextToSpeechRequest} and returns the raw audio bytes wrapped as a {@link Speech}.
* - *Streaming: Fanar's TTS endpoint returns the entire synthesized audio in one HTTP response — - * it does not chunk-stream. {@link #stream(TextToSpeechPrompt)} consequently emits exactly one - * {@link TextToSpeechResponse} containing the full audio, which is functionally equivalent to - * {@link #call(TextToSpeechPrompt)} but satisfies the {@code StreamingTextToSpeechModel} SPI.
+ *Streaming: {@link #stream(TextToSpeechPrompt)} uses Fanar's chunked delivery + * ({@code stream:true} on the wire) via {@code AudioClient.speechStream(...)} and emits one + * {@link TextToSpeechResponse} per audio chunk as the server generates it. Chunk boundaries + * follow transport reads — concatenate the chunks' bytes in emission order to reconstruct the + * full clip.
* *Spring AI's {@link TextToSpeechOptions#getSpeed()} is silently dropped — Fanar's wire format - * has no playback-speed parameter. Callers can resample client-side after receiving the bytes.
+ * has no playback-speed parameter. Callers can resample client-side after receiving the bytes. + * Pass a {@link FanarTextToSpeechOptions} to reach the Fanar-only knobs (emotional synthesis, + * Quranic reciter) — see ADR-024. * * @author Oussama Mahjoub */ @@ -62,18 +66,25 @@ public TextToSpeechResponse call(TextToSpeechPrompt prompt) { @Override public FluxPass an instance on the {@code TextToSpeechPrompt}; {@code FanarTextToSpeechModel} maps the + * portable getters like any {@link TextToSpeechOptions} and additionally applies the Fanar + * extras. Any other implementation keeps working — the extras are then simply unset + * (ADR-024). {@link #getSpeed()} remains unsupported by Fanar's wire format and is dropped.
+ * + * @author Oussama Mahjoub + */ +public final class FanarTextToSpeechOptions implements TextToSpeechOptions { + + private final String model; + private final String voice; + private final String format; + private final Double speed; + private final Boolean withEmotion; + private final QuranReciter quranReciter; + + private FanarTextToSpeechOptions(Builder b) { + this.model = b.model; + this.voice = b.voice; + this.format = b.format; + this.speed = b.speed; + this.withEmotion = b.withEmotion; + this.quranReciter = b.quranReciter; + } + + /** Start a fresh builder. */ + public static Builder builder() { + return new Builder(); + } + + @Override public String getModel() { return model; } + @Override public String getVoice() { return voice; } + @Override public String getFormat() { return format; } + @Override public Double getSpeed() { return speed; } + + /** + * Emotional speech synthesis ({@code Fanar-Aura-TTS-2} + emotion-capable voices only), + * or {@code null}. + */ + public Boolean getWithEmotion() { return withEmotion; } + + /** Reciter selection for {@code Fanar-Sadiq-TTS-1}, or {@code null}. */ + public QuranReciter getQuranReciter() { return quranReciter; } + + /** Fluent builder; every field defaults to {@code null} ("use the adapter/server default"). */ + public static final class Builder { + + private String model; + private String voice; + private String format; + private Double speed; + private Boolean withEmotion; + private QuranReciter quranReciter; + + private Builder() { + // use FanarTextToSpeechOptions.builder() + } + + public Builder model(String model) { this.model = model; return this; } + public Builder voice(String voice) { this.voice = voice; return this; } + public Builder format(String format) { this.format = format; return this; } + public Builder speed(Double speed) { this.speed = speed; return this; } + public Builder withEmotion(Boolean withEmotion) { this.withEmotion = withEmotion; return this; } + public Builder quranReciter(QuranReciter quranReciter) { this.quranReciter = quranReciter; return this; } + + public FanarTextToSpeechOptions build() { + return new FanarTextToSpeechOptions(this); + } + } +} diff --git a/spring-ai-starter/src/test/java/qa/fanar/spring/ai/FanarChatModelTest.java b/spring-ai-starter/src/test/java/qa/fanar/spring/ai/FanarChatModelTest.java index 938edf3..cace4cb 100644 --- a/spring-ai-starter/src/test/java/qa/fanar/spring/ai/FanarChatModelTest.java +++ b/spring-ai-starter/src/test/java/qa/fanar/spring/ai/FanarChatModelTest.java @@ -24,9 +24,14 @@ import qa.fanar.core.FanarClient; import qa.fanar.core.RetryPolicy; +import qa.fanar.core.chat.BookName; import qa.fanar.core.chat.ChatModel; +import qa.fanar.core.chat.Madhab; +import qa.fanar.core.chat.Source; import qa.fanar.json.jackson3.Jackson3FanarJsonCodec; +import java.util.Map; + import static org.assertj.core.api.Assertions.assertThat; /** @@ -115,6 +120,100 @@ void mixedRoleMessagesAllForwarded() { .contains("hi back"); } + @Test + void fanarChatOptionsForwardEveryVendorKnob() { + server.createContext("/v1/chat/completions", exchange -> { + capturedRequestBody = new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8); + byte[] body = okResponse("ok"); + exchange.sendResponseHeaders(200, body.length); + try (OutputStream out = exchange.getResponseBody()) { out.write(body); } + }); + server.start(); + client = clientFor(server); + + BookName book = BookName.KNOWN.iterator().next(); + FanarChatModel model = new FanarChatModel(client, ChatModel.FANAR); + FanarChatOptions options = FanarChatOptions.builder() + .model("Fanar-Sadiq-2") + .temperature(0.3) + .persona("Warm, patient teacher") + .madhab(List.of(Madhab.HANAFI, Madhab.ALL)) + .enableThinking(true) + .restrictToIslamic(true) + .bookNames(List.of(book)) + .preferredSources(List.of(Source.QURAN)) + .excludeSources(List.of(Source.DORAR)) + .filterSources(List.of(Source.TAFSIR)) + .logitBias(Map.of("50256", -100.0)) + .logprobs(true) + .topLogprobs(5) + .n(2) + .minP(0.05) + .repetitionPenalty(1.1) + .bestOf(3) + .lengthPenalty(1.2) + .earlyStopping(true) + .stopTokenIds(List.of(50256)) + .ignoreEos(false) + .minTokens(4) + .skipSpecialTokens(true) + .spacesBetweenSpecialTokens(false) + .truncatePromptTokens(2048) + .promptLogprobs(1) + .build(); + model.call(new Prompt(List.of(new UserMessage("x")), options)); + + assertThat(capturedRequestBody) + .contains("\"model\":\"Fanar-Sadiq-2\"") + .contains("\"temperature\":0.3") + .contains("\"persona\":\"Warm, patient teacher\"") + .contains("\"madhab\":[\"hanafi\",\"all\"]") + .contains("\"enable_thinking\":true") + .contains("\"restrict_to_islamic\":true") + .contains("\"book_names\":[\"" + book.wireValue() + "\"]") + .contains("\"preferred_sources\":[\"quran\"]") + .contains("\"exclude_sources\":[\"dorar\"]") + .contains("\"filter_sources\":[\"tafsir\"]") + .contains("\"logit_bias\":{\"50256\":-100.0}") + .contains("\"logprobs\":true") + .contains("\"top_logprobs\":5") + .contains("\"n\":2") + .contains("\"min_p\":0.05") + .contains("\"repetition_penalty\":1.1") + .contains("\"best_of\":3") + .contains("\"length_penalty\":1.2") + .contains("\"early_stopping\":true") + .contains("\"stop_token_ids\":[50256]") + .contains("\"ignore_eos\":false") + .contains("\"min_tokens\":4") + .contains("\"skip_special_tokens\":true") + .contains("\"spaces_between_special_tokens\":false") + .contains("\"truncate_prompt_tokens\":2048") + .contains("\"prompt_logprobs\":1"); + } + + @Test + void emptyFanarChatOptionsBehavesLikePortableDefaults() { + server.createContext("/v1/chat/completions", exchange -> { + capturedRequestBody = new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8); + byte[] body = okResponse("ok"); + exchange.sendResponseHeaders(200, body.length); + try (OutputStream out = exchange.getResponseBody()) { out.write(body); } + }); + server.start(); + client = clientFor(server); + + FanarChatModel model = new FanarChatModel(client, ChatModel.FANAR); + model.call(new Prompt(List.of(new UserMessage("x")), FanarChatOptions.builder().build())); + + assertThat(capturedRequestBody) + .contains("\"model\":\"Fanar\"") + .doesNotContain("persona") + .doesNotContain("madhab") + .doesNotContain("enable_thinking") + .doesNotContain("restrict_to_islamic"); + } + @Test void chatOptionsForwardSamplingKnobs() { server.createContext("/v1/chat/completions", exchange -> { diff --git a/spring-ai-starter/src/test/java/qa/fanar/spring/ai/FanarChatOptionsTest.java b/spring-ai-starter/src/test/java/qa/fanar/spring/ai/FanarChatOptionsTest.java new file mode 100644 index 0000000..55f841a --- /dev/null +++ b/spring-ai-starter/src/test/java/qa/fanar/spring/ai/FanarChatOptionsTest.java @@ -0,0 +1,288 @@ +package qa.fanar.spring.ai; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import qa.fanar.core.chat.BookName; +import qa.fanar.core.chat.Madhab; +import qa.fanar.core.chat.Source; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class FanarChatOptionsTest { + + private static final BookName BOOK = BookName.KNOWN.iterator().next(); + + @Test + void builderRoundtripsAllFields() { + FanarChatOptions o = FanarChatOptions.builder() + .model("Fanar-Sadiq-2") + .temperature(0.3) + .topP(0.9) + .topK(40) + .maxTokens(64) + .frequencyPenalty(0.1) + .presencePenalty(0.2) + .stopSequences(List.of("END")) + .persona("Warm, patient teacher") + .madhab(List.of(Madhab.HANAFI)) + .enableThinking(true) + .restrictToIslamic(true) + .bookNames(List.of(BOOK)) + .preferredSources(List.of(Source.QURAN)) + .excludeSources(List.of(Source.DORAR)) + .filterSources(List.of(Source.TAFSIR)) + .logitBias(Map.of("50256", -100.0)) + .logprobs(true) + .topLogprobs(5) + .n(2) + .minP(0.05) + .repetitionPenalty(1.1) + .bestOf(3) + .lengthPenalty(1.2) + .earlyStopping(true) + .stopTokenIds(List.of(50256)) + .ignoreEos(false) + .minTokens(8) + .skipSpecialTokens(true) + .spacesBetweenSpecialTokens(false) + .truncatePromptTokens(2048) + .promptLogprobs(1) + .build(); + + assertThat(o.getModel()).isEqualTo("Fanar-Sadiq-2"); + assertThat(o.getTemperature()).isEqualTo(0.3); + assertThat(o.getTopP()).isEqualTo(0.9); + assertThat(o.getTopK()).isEqualTo(40); + assertThat(o.getMaxTokens()).isEqualTo(64); + assertThat(o.getFrequencyPenalty()).isEqualTo(0.1); + assertThat(o.getPresencePenalty()).isEqualTo(0.2); + assertThat(o.getStopSequences()).containsExactly("END"); + assertThat(o.getPersona()).isEqualTo("Warm, patient teacher"); + assertThat(o.getMadhab()).containsExactly(Madhab.HANAFI); + assertThat(o.getEnableThinking()).isTrue(); + assertThat(o.getRestrictToIslamic()).isTrue(); + assertThat(o.getBookNames()).containsExactly(BOOK); + assertThat(o.getPreferredSources()).containsExactly(Source.QURAN); + assertThat(o.getExcludeSources()).containsExactly(Source.DORAR); + assertThat(o.getFilterSources()).containsExactly(Source.TAFSIR); + assertThat(o.getLogitBias()).containsEntry("50256", -100.0); + assertThat(o.getLogprobs()).isTrue(); + assertThat(o.getTopLogprobs()).isEqualTo(5); + assertThat(o.getN()).isEqualTo(2); + assertThat(o.getMinP()).isEqualTo(0.05); + assertThat(o.getRepetitionPenalty()).isEqualTo(1.1); + assertThat(o.getBestOf()).isEqualTo(3); + assertThat(o.getLengthPenalty()).isEqualTo(1.2); + assertThat(o.getEarlyStopping()).isTrue(); + assertThat(o.getStopTokenIds()).containsExactly(50256); + assertThat(o.getIgnoreEos()).isFalse(); + assertThat(o.getMinTokens()).isEqualTo(8); + assertThat(o.getSkipSpecialTokens()).isTrue(); + assertThat(o.getSpacesBetweenSpecialTokens()).isFalse(); + assertThat(o.getTruncatePromptTokens()).isEqualTo(2048); + assertThat(o.getPromptLogprobs()).isEqualTo(1); + } + + @Test + void unsetFieldsStayNull() { + FanarChatOptions o = FanarChatOptions.builder().build(); + assertThat(o.getModel()).isNull(); + assertThat(o.getTemperature()).isNull(); + assertThat(o.getTopP()).isNull(); + assertThat(o.getTopK()).isNull(); + assertThat(o.getMaxTokens()).isNull(); + assertThat(o.getFrequencyPenalty()).isNull(); + assertThat(o.getPresencePenalty()).isNull(); + assertThat(o.getStopSequences()).isNull(); + assertThat(o.getPersona()).isNull(); + assertThat(o.getMadhab()).isNull(); + assertThat(o.getEnableThinking()).isNull(); + assertThat(o.getRestrictToIslamic()).isNull(); + assertThat(o.getBookNames()).isNull(); + assertThat(o.getPreferredSources()).isNull(); + assertThat(o.getExcludeSources()).isNull(); + assertThat(o.getFilterSources()).isNull(); + assertThat(o.getLogitBias()).isNull(); + assertThat(o.getLogprobs()).isNull(); + assertThat(o.getTopLogprobs()).isNull(); + assertThat(o.getN()).isNull(); + assertThat(o.getMinP()).isNull(); + assertThat(o.getRepetitionPenalty()).isNull(); + assertThat(o.getBestOf()).isNull(); + assertThat(o.getLengthPenalty()).isNull(); + assertThat(o.getEarlyStopping()).isNull(); + assertThat(o.getStopTokenIds()).isNull(); + assertThat(o.getIgnoreEos()).isNull(); + assertThat(o.getMinTokens()).isNull(); + assertThat(o.getSkipSpecialTokens()).isNull(); + assertThat(o.getSpacesBetweenSpecialTokens()).isNull(); + assertThat(o.getTruncatePromptTokens()).isNull(); + assertThat(o.getPromptLogprobs()).isNull(); + } + + @Test + void collectionsAreDefensivelyCopiedAndUnmodifiable() { + List