# Chat Completions Source: https://docs.sunbird.ai/api-reference/ai/chat-completions api-reference/openapi.json POST /tasks/chat/completions OpenAI-compatible chat completion powered by Sunbird's Sunflower model. Single-instruction usage is a request with one user message; conversational usage passes the running message history. Set `stream: true` for Server-Sent Events in OpenAI `chat.completion.chunk` format, terminated by `data: [DONE]`. # Change Password Source: https://docs.sunbird.ai/api-reference/authentication/change-password api-reference/openapi.json POST /auth/change-password # Forgot Password Source: https://docs.sunbird.ai/api-reference/authentication/forgot-password api-reference/openapi.json POST /auth/forgot-password # Get Profile Source: https://docs.sunbird.ai/api-reference/authentication/get-profile api-reference/openapi.json GET /auth/me # Login Source: https://docs.sunbird.ai/api-reference/authentication/login api-reference/openapi.json POST /auth/token # Profile Status Source: https://docs.sunbird.ai/api-reference/authentication/profile-status api-reference/openapi.json GET /auth/profile/status # Refresh Token Source: https://docs.sunbird.ai/api-reference/authentication/refresh-token api-reference/openapi.json POST /auth/refresh-token Rotate the caller's API key. Bumps the user's token version (revoking the previous key) and issues a new token carrying the user's *current* account type. Rate limits are keyed by user, not token, so refreshing never resets a quota. # Register Source: https://docs.sunbird.ai/api-reference/authentication/register api-reference/openapi.json POST /auth/register # Resend Verification Source: https://docs.sunbird.ai/api-reference/authentication/resend-verification api-reference/openapi.json POST /auth/resend-verification # Reset Password Source: https://docs.sunbird.ai/api-reference/authentication/reset-password api-reference/openapi.json POST /auth/reset-password # Update Profile Source: https://docs.sunbird.ai/api-reference/authentication/update-profile api-reference/openapi.json PUT /auth/profile # Verify Email Source: https://docs.sunbird.ai/api-reference/authentication/verify-email api-reference/openapi.json GET /auth/verify-email Landing endpoint for the emailed verification link. Opening the link marks the account as verified and **always redirects** to the web app — it never reveals whether an account exists. You normally do not call this endpoint yourself; it is the target of the link sent by `POST /auth/register` and `POST /auth/resend-verification`. # API Reference Source: https://docs.sunbird.ai/api-reference/introduction comprehensive reference for the Sunbird AI API endpoints. Welcome to the Sunbird AI API Reference. This documentation provides detailed specifications for all available endpoints. ## Base URL All API requests should be made to: ```bash theme={null} https://api.sunbird.ai ``` ## Authentication All endpoints require authentication using a Bearer token. ```bash theme={null} Authorization: Bearer ``` A full walkthrough of every Sunbird AI endpoint — authentication, translation, speech-to-text, text-to-speech, language detection, and Sunflower chat — with runnable Python examples. Looking for the older endpoints? Use the **version dropdown** at the top of the sidebar to switch to **v1 · Deprecated**. See the [Changelog](/resources/changelog) and [Deprecations](/resources/deprecations) pages for what changed. ## Modules Transcribe audio in 51 African languages with `/tasks/audio/transcriptions`. Synthesize speech, batch jobs, and voice discovery. Translate between 67 African languages plus English. OpenAI-compatible chat completions with `sunflower-9b` or `sunflower-14b`. Language identification for text and audio. User management, email verification, and token generation. # Audio Language Detection Source: https://docs.sunbird.ai/api-reference/language/audio-language api-reference/openapi.json POST /tasks/auto_detect_audio_language Detect the language spoken in an audio file. Upload the audio as `multipart/form-data`. The file is stored in cloud storage for processing and the detected language code is returned. **Example response** ```json { "detected_language": "lug" } ``` **Errors** - `502` — the language detection service failed or could not be reached - `503` — the language detection service timed out # Language ID Source: https://docs.sunbird.ai/api-reference/language/language-id api-reference/openapi.json POST /tasks/language_id Identify the language of a piece of text. Backed by a classification model covering 64 languages, mostly African plus English and French. A language is only reported when its probability clears the confidence threshold (default `0.35`); below that the response is `"language not detected"`, which is more useful than a confident guess at a language the model does not actually support. **Example request** ```json { "text": "Oli otya?" } ``` **Example response** ```json { "language": "lug", "confidence": 0.97 } ``` When confidence is below the threshold: ```json { "language": "language not detected", "confidence": null } ``` **Errors** - `502` — the language identification service failed - `503` — the language identification service timed out # Batch Text to Speech Source: https://docs.sunbird.ai/api-reference/speech/batch-speech api-reference/openapi.json POST /tasks/audio/speech/batch Unified batch Text-to-Speech endpoint. Synthesizes 1-128 items in a single sunbird-orpheus-tts pass and uploads each result to GCS. Per-item failures are reported with status='error'; the request returns 200 if at least one item succeeds, 502 if every item fails. Replaces /tasks/modal/orpheus/tts/batch. # List Voices Source: https://docs.sunbird.ai/api-reference/speech/list-voices api-reference/openapi.json GET /tasks/voice/speakers List available Orpheus-3B speakers. Returns the catalog grouped by language (OrpheusSpeakersResponse); add 'language' to get one language's voices (OrpheusLanguageSpeakersResponse). Replaces /tasks/modal/orpheus/speakers, /tasks/modal/orpheus/speakers/{language}, and /tasks/modal/tts/speakers. # Refresh Audio URL Source: https://docs.sunbird.ai/api-reference/speech/refresh-audio-url api-reference/openapi.json GET /tasks/audio/speech/url Generate a fresh signed URL for an existing audio object in GCS (e.g. a previously synthesized speech file whose signed URL has expired). Replaces /tasks/modal/tts/refresh-url. # Speech to Text Source: https://docs.sunbird.ai/api-reference/speech/speech-to-text api-reference/openapi.json POST /tasks/audio/transcriptions Unified Speech-to-Text endpoint, powered by Sunbird's faster-whisper ASR model covering 51 African languages. Accepts an uploaded audio file and a required language code; set timestamps=true to also receive per-segment timings. Replaces /stt, /stt_from_gcs, /org/stt, and /modal/stt. # Text to Speech Source: https://docs.sunbird.ai/api-reference/speech/text-to-speech api-reference/openapi.json POST /tasks/audio/speech Unified Text-to-Speech endpoint, served by the RunPod Orpheus-3B deployment. response_mode='url' (default) returns a signed GCS audio URL; 'stream' returns raw audio bytes with no storage; 'both' returns raw audio bytes with the signed URL in the X-Audio-Url header. Replaces /tasks/modal/tts, /tasks/runpod/tts, and /tasks/modal/orpheus/tts. # Translate Source: https://docs.sunbird.ai/api-reference/translation/translate api-reference/openapi.json POST /tasks/translate Translate text between supported languages using the sunflower-9b model. Languages are accepted as ISO 639-3 codes (e.g. `lug`) or full names (e.g. `Luganda`), case-insensitively. `source_language` is optional — when omitted, Sunflower infers the source language from the text. Translation works between any pair of supported languages. **Supported languages** — English plus the 67 African languages `sunflower-9b` supports. Quality varies by language; see the model's good/moderate/basic tiers. Acholi (`ach`), Afrikaans (`afr`), Akan (`aka`), Alur (`alz`), Amharic (`amh`), Aringa (`luc`), Ateso (`teo`), Bambara (`bam`), Bari (`bfa`), Bemba (`bem`), Berber (`ber`), Chichewa (`nya`), Dagaare (`dga`), Dagbani (`dag`), Dinka (`din`), English (`eng`), Ewe (`ewe`), Fulani (`ful`), Hausa (`hau`), Igbo (`ibo`), Ik (`ikx`), Jopadhola (`adh`), Kabyle (`kab`), Kakwa (`keo`), Kalenjin (`kln`), Kanuri (`kau`), Karamojong (`kdj`), Kikuyu (`kik`), Kinyarwanda (`kin`), Kirundi (`run`), Kumam (`kdi`), Kupsabiny (`kpz`), Kwamba (`rwm`), Lango (`laj`), Lendu (`led`), Lingala (`lin`), Lubwisi (`tlj`), Luganda (`lug`), Lugbara (`lgg`), Lugungu (`rub`), Lugwere (`gwr`), Luhya (`luy`), Lumasaba (`myx`), Lunyole (`nuj`), Luo (`luo`), Lusoga (`xog`), Ma'di (`mhi`), Malagasy (`mlg`), Ndebele (`nbl`), Nigerian Pidgin (`pcm`), Oromo (`orm`), Pokot (`pok`), Rukiga (`cgg`), Rukonjo (`koo`), Runyankole (`nyn`), Runyoro (`nyo`), Ruruuli (`ruc`), Rutooro (`ttj`), Samia (`lsm`), Shona (`sna`), Somali (`som`), Sotho (`sot`), Swahili (`swa`), Tswana (`tsn`), Wolof (`wol`), Xhosa (`xho`), Yoruba (`yor`), Zulu (`zul`). **Example request** ```json { "source_language": "eng", "target_language": "lug", "text": "Hello, how are you?" } ``` **Example response** ```json { "id": "trans-1a2b3c...", "status": "COMPLETED", "output": { "translated_text": "Oli otya?", "source_language": "eng", "target_language": "lug" } } ``` **Errors** - `400` — unsupported language, or `source_language` equals `target_language` - `502` — the model returned empty output or failed unexpectedly - `503` — the model is still loading or inference timed out # Generate Upload URL Source: https://docs.sunbird.ai/api-reference/uploads/generate-upload-url api-reference/openapi.json POST /tasks/generate-upload-url Generate a signed URL for direct upload to Google Cloud Storage. # WhatsApp Webhook Source: https://docs.sunbird.ai/api-reference/webhooks/whatsapp-webhook api-reference/openapi.json POST /tasks/webhook Handle incoming WhatsApp Business API webhooks. Processes incoming WhatsApp messages and events, returning fast responses for text messages and deferring heavy work to background processing. **Features** - Fast text responses (2–4 seconds) - Background processing for heavy operations - Duplicate message detection - Language preference support The raw request body is verified against the `X-Hub-Signature-256` header before it is parsed. An invalid signature is rejected (`403` by default) when signature enforcement is enabled. **Example payload** ```json { "entry": [{ "changes": [{ "value": { "messages": [{ "from": "1234567890", "text": { "body": "Hello" } }], "contacts": [{ "profile": { "name": "John" } }], "metadata": { "phone_number_id": "9876543210" } } }] }] } ``` **Example response** ```json { "status": "success", "processing_time": 2.5 } ``` # Authentication Source: https://docs.sunbird.ai/authentication How to authenticate your requests to the Sunbird AI API The Sunbird AI API uses **Bearer Token** authentication. You must include a valid access token in the `Authorization` header of your HTTP requests. ## Overview 1. **Register** an account and **verify** your email address. 2. **Login** to obtain an access token. 3. **Include** the token in your requests. ## Registration To start using the API, you need to create an account. You have two options: * **Web:** Sign up at [https://api.sunbird.ai/register](https://api.sunbird.ai/register), then grab your access token / API key from the [tokens page](https://api.sunbird.ai/keys). * **API:** Register programmatically via the `/auth/register` endpoint. You will need a valid email address. ```bash theme={null} curl -X POST "https://api.sunbird.ai/auth/register" \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "password": "securePassword123", "username": "user123", "organization": "Example Org" }' ``` ## Email Verification After registering, we send a verification link to your email address. Opening the link calls `GET /auth/verify-email?token=...` and marks the account as verified (`email_verified: true` on your [profile](/api-reference/authentication/get-profile)). If the email did not arrive, request a new link while authenticated: ```bash theme={null} curl -X POST "https://api.sunbird.ai/auth/resend-verification" \ -H "Authorization: Bearer " ``` ## Getting an Access Token After registering, exchange your credentials for an access token using the `/auth/token` endpoint. Note that this endpoint expects **form-encoded** data, not JSON. ```bash theme={null} curl -X POST "https://api.sunbird.ai/auth/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=user@example.com&password=securePassword123" ``` The response will include your token: ```json theme={null} { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "bearer" } ``` ## Using the Token Include the token in the `Authorization` header of your requests: ```http theme={null} Authorization: Bearer ``` ### Example Request ```python Python theme={null} import requests headers = { "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } response = requests.get("https://api.sunbird.ai/auth/me", headers=headers) ``` ```javascript JavaScript theme={null} fetch('https://api.sunbird.ai/auth/me', { headers: { 'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' } }); ``` ## Token Lifetime Access tokens **do not expire**. Your token effectively acts as your **API key** — use the same token to authenticate every request to the Sunbird AI API. Generate it once (from the [tokens page](https://api.sunbird.ai/keys) or the `/auth/token` endpoint), store it securely, and reuse it. If a request returns a `401 Unauthorized` error, the token is missing or invalid — double-check that it is included correctly in the `Authorization` header. ## Rotating Your Token If a token is leaked or you simply want a fresh key, call `POST /auth/refresh-token` with your current token. This **revokes the previous token** and issues a new one carrying your current account type. Rate limits are keyed by user, not token, so refreshing never resets a quota. ```bash theme={null} curl -X POST "https://api.sunbird.ai/auth/refresh-token" \ -H "Authorization: Bearer " ``` ```json theme={null} { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "bearer" } ``` Update every integration that uses the old token — it stops working as soon as the new one is issued. ## Security Best Practices Never commit your API tokens or passwords to version control (like GitHub). * **Environment Variables**: Store your tokens in environment variables (e.g., `SUNBIRD_API_TOKEN`). * **Backend Proxy**: If you are building a frontend application, route requests through your own backend to keep your credentials hidden. Do not expose your token in client-side code. ## OAuth Integration We support Google OAuth for easier login. This is primarily handled via our web interface, but the underlying flow uses standard OAuth 2.0 protocols. ## Common Errors | Status Code | Error | Description | | :---------- | :----------- | :-------------------------------------------------- | | `401` | Unauthorized | The token is missing, invalid, or expired. | | `403` | Forbidden | You do not have permission to access this resource. | # Error Handling Source: https://docs.sunbird.ai/guides/error-handling Best practices for handling API errors The Sunbird AI API uses standard HTTP status codes to indicate the success or failure of requests. ## Common Status Codes | Code | Description | Action | | :---- | :------------------------------------------------------- | :-------------------------------------------------------------------- | | `200` | **OK**. The request was successful. | Process the response body. | | `400` | **Bad Request**. Invalid parameters or malformed JSON. | Check your request payload and parameters. | | `401` | **Unauthorized**. Missing or invalid API token. | Verify your `Authorization` header. | | `404` | **Not Found**. The endpoint or resource does not exist. | Check the URL path. | | `429` | **Too Many Requests**. Rate limit exceeded. | Implement backoff and retry (see [Rate Limits](/guides/rate-limits)). | | `500` | **Server Error**. Internal system issue. | Retry the request later. | | `503` | **Service Unavailable**. Model is loading or overloaded. | Retry with exponential backoff. | ## Timeouts Some AI models, particularly large ones like Sunflower or Whisper, may take time to process requests. Server-side timeouts (e.g., `408`, `504`) can occur if the request takes too long. For large operations (like long audio files), consider using the **asynchronous workflows** or **signed URL** upload methods where available to avoid timeouts. ## Deprecation Headers Several legacy endpoints (e.g. `/tasks/stt`, `/tasks/tts`, `/tasks/sunflower_inference`) still work but are **deprecated**. When you call one, the API returns `Deprecation` and `Sunset` response headers indicating that the route will be removed in a future release. Inspect these headers in your client and migrate to the unified endpoints — `/tasks/audio/transcriptions`, `/tasks/audio/speech`, and `/tasks/chat/completions` — before the sunset date. ## Python Example Here is a robust way to handle errors in Python: ```python theme={null} import requests import time def call_api_robustly(url, headers, data, max_retries=3): for attempt in range(max_retries): response = requests.post(url, headers=headers, json=data) if response.status_code == 200: return response.json() if response.status_code in [429, 503]: wait_time = (2 ** attempt) # Exponential backoff print(f"Rate limited. Retrying in {wait_time}s...") time.sleep(wait_time) continue # Non-retryable error print(f"Error {response.status_code}: {response.text}") break return None ``` # Language Detection Source: https://docs.sunbird.ai/guides/language-detection Identify languages from text or audio Sunbird AI can automatically detect the language of a given text or audio file. This is useful for routing content to the correct translation or transcription pipeline. ## Text Language Identification Use the `/tasks/language_id` endpoint to identify the language of a text string. ### Supported Languages The classifier covers **64 languages** — mostly African, plus English and French. A language is only reported when the model's confidence clears the server-side threshold (default `0.35`); below that, `language` is `"language not detected"` and `confidence` is `null`. `text` must be 3–200 characters. ### Example Request ```python Python theme={null} import os import requests from dotenv import load_dotenv load_dotenv() url = "https://api.sunbird.ai/tasks/language_id" access_token = os.getenv("AUTH_TOKEN") headers = { "accept": "application/json", "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", } payload = {"text": "Oli otya? Webale nnyo ku kujja wano."} response = requests.post(url, headers=headers, json=payload) result = response.json() print(f"Detected language: {result}") ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.sunbird.ai/tasks/language_id', { method: 'POST', headers: { 'Authorization': 'Bearer ', 'Content-Type': 'application/json' }, body: JSON.stringify({ text: 'Oli otya? Webale nnyo ku kujja wano.' }) }); console.log(await response.json()); ``` ### Response ```json theme={null} { "language": "lug", "confidence": 0.98 } ``` `confidence` is the model's 0–1 confidence in the identified language (omitted when the model does not report one). Filter on it if you want a stricter bar than the server's threshold. When no language clears the threshold: ```json theme={null} { "language": "language not detected", "confidence": null } ``` ## Audio Language Detection Use the `/tasks/auto_detect_audio_language` endpoint to identify the language spoken in an audio file. ### Example Request ```python Python theme={null} import os import requests from dotenv import load_dotenv load_dotenv() url = "https://api.sunbird.ai/tasks/auto_detect_audio_language" access_token = os.getenv("AUTH_TOKEN") files = {"audio": open("recording.mp3", "rb")} headers = {"Authorization": f"Bearer {access_token}"} response = requests.post(url, files=files, headers=headers) print(response.json()) ``` ### Response ```json theme={null} { "detected_language": "ach" } ``` # Rate Limits Source: https://docs.sunbird.ai/guides/rate-limits Understanding API rate limits and quotas Sunbird AI enforces rate limits to ensure fair usage and system stability. Limits are applied per account based on the type of plan you are on. ## Account Tiers Rate limits are determined by the `account_type` in your authentication token: Designed for testing and development. Usage is limited to prevent abuse. Higher rate limits suitable for production applications. Custom rate limits tailored to high-volume needs. Contact sales for details. ## Handling Rate Limits When you exceed your rate limit, the API will return a `429 Too Many Requests` status code. ### Best Practices Inspect the `Retry-After` header in the 429 response to know how long to wait. Implement an exponential backoff strategy. If a request fails, wait for a short period (e.g., 1s), then retry. If it fails again, double the wait time (2s, 4s, etc.). Add random "jitter" to your wait times to prevent all your clients from retrying at the exact same millisecond. # Speech to Text Source: https://docs.sunbird.ai/guides/speech-to-text Convert audio to text across 51 African languages The Speech to Text (STT) service transcribes audio files into text. The unified **`POST /tasks/audio/transcriptions`** endpoint is powered by Sunbird's faster-whisper ASR model ([`Sunbird/faster-whisper-51-african-languages`](https://huggingface.co/Sunbird/faster-whisper-51-african-languages), a [Whisper large-v3 fine-tune](https://huggingface.co/Sunbird/asr-whisper-51-african-languages)) covering **51 African languages**. **Breaking change (v2.1).** This endpoint previously accepted `platform`, `adapter`, `whisper`, `recognise_speakers`, `org`, and `gcs_blob_name`. Those parameters have been **removed** and are now rejected with `422`. Send only `audio`, `language`, and (optionally) `timestamps`. ## Audio Requirements * **Formats**: MP3, WAV, M4A, OGG, and more. * **Duration Limit**: For files larger than 100MB, only the first 10 minutes are transcribed. * **File Size**: Direct uploads are supported. Use the [signed-URL workflow](#file-uploads-signed-urls) to stage large files in Google Cloud Storage. ## Transcribing a File `audio` and `language` are both **required**. The model reuses Whisper's language-token slots for African languages, which makes automatic language detection unreliable — always pass a language explicitly. ### Parameters | Parameter | Type | Required | Description | | :----------- | :------ | :------- | :----------------------------------------------------------------------------- | | `audio` | file | Yes | The audio file to transcribe (multipart/form-data). | | `language` | string | Yes | ISO 639-3 code — one of the [51 supported languages](#supported-languages-51). | | `timestamps` | boolean | No | Return per-segment start/end times in `segments`. Defaults to `false`. | ```python Python theme={null} import os import requests from dotenv import load_dotenv load_dotenv() url = "https://api.sunbird.ai/tasks/audio/transcriptions" access_token = os.getenv("AUTH_TOKEN") headers = { "accept": "application/json", "Authorization": f"Bearer {access_token}", } files = { "audio": ("recording.wav", open("/path/to/audio_file.wav", "rb"), "audio/wav"), } data = { "language": "lug", # required: ISO 639-3 code } response = requests.post(url, headers=headers, files=files, data=data) result = response.json() print(f"Transcription: {result['audio_transcription']}") ``` ```javascript JavaScript theme={null} const formData = new FormData(); formData.append('audio', fileInput.files[0]); formData.append('language', 'lug'); const response = await fetch('https://api.sunbird.ai/tasks/audio/transcriptions', { method: 'POST', headers: { 'Authorization': 'Bearer ' }, body: formData }); console.log(await response.json()); ``` ```bash cURL theme={null} curl -X POST "https://api.sunbird.ai/tasks/audio/transcriptions" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -F "audio=@/path/to/audio_file.wav" \ -F "language=lug" ``` ### Response ```json theme={null} { "audio_transcription": "Ekibiina ekiddukanya ...", "language": "lug", "audio_url": "gs://.../audio.wav", "audio_transcription_id": 123, "duration_seconds": 12.4, "segments": null, "usage": { "type": "duration", "seconds": 12.4 }, "diarization_output": {}, "formatted_diarization_output": "", "was_audio_trimmed": false, "original_duration_minutes": null } ``` ## Timestamped Segments Set `timestamps=true` to also receive per-segment start/end times in `segments`. It is `false` by default, in which case `segments` comes back as `null`. ```python theme={null} data = { "language": "ach", "timestamps": True, } response = requests.post(url, headers=headers, files=files, data=data) result = response.json() print(result["audio_transcription"]) for segment in result["segments"]: print(f"[{segment['start']:7.2f} -> {segment['end']:7.2f}] {segment['text']}") ``` ```json theme={null} { "audio_transcription": "Gyebaleko ssebo", "language": "ach", "duration_seconds": 2.25, "segments": [ {"start": 0.0, "end": 1.0, "text": "Gyebaleko"}, {"start": 1.0, "end": 2.25, "text": "ssebo"} ] } ``` ## Supported Languages (51) Pass the ISO 639-3 code as `language`. All ten languages served by the previous version of this endpoint are still supported. | Code | Language | Code | Language | Code | Language | | :---- | :-------- | :---- | :---------- | :---- | :-------------- | | `ach` | Acholi | `kab` | Kabyle | `nyn` | Runyankole | | `afr` | Afrikaans | `kau` | Kanuri | `orm` | Oromo | | `aka` | Akan | `kik` | Kikuyu | `pcm` | Nigerian Pidgin | | `amh` | Amharic | `kin` | Kinyarwanda | `ruc` | Ruruuli | | `bam` | Bambara | `kln` | Kalenjin | `rwm` | Kwamba | | `bem` | Bemba | `koo` | Rukonjo | `sna` | Shona | | `ber` | Berber | `kpo` | Ikposo | `som` | Somali | | `cgg` | Rukiga | `led` | Lendu | `sot` | Sotho | | `dag` | Dagbani | `lgg` | Lugbara | `swa` | Swahili | | `dga` | Dagaare | `lin` | Lingala | `teo` | Ateso | | `eng` | English | `lth` | Thur | `tsn` | Tswana | | `ewe` | Ewe | `lug` | Luganda | `ttj` | Rutooro | | `fra` | French | `luo` | Luo | `wol` | Wolof | | `ful` | Fulani | `luy` | Luhya | `xho` | Xhosa | | `hau` | Hausa | `mlg` | Malagasy | `xog` | Lusoga | | `ibo` | Igbo | `myx` | Lumasaba | `yor` | Yoruba | | | | `nbl` | Ndebele | `zul` | Zulu | | | | `nya` | Chichewa | | | Per-language accuracy scales with the amount of training data available for each language. See the [model card](https://huggingface.co/Sunbird/asr-whisper-51-african-languages) for per-language WER/CER figures. ## File Uploads (Signed URLs) Generate secure signed URLs for direct client uploads to Google Cloud Storage. URLs are valid for **30 minutes** and include path-traversal protection. ```python theme={null} import os import requests from dotenv import load_dotenv load_dotenv() # Step 1: Generate an upload URL url = "https://api.sunbird.ai/tasks/generate-upload-url" access_token = os.getenv("AUTH_TOKEN") headers = { "accept": "application/json", "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", } data = {"file_name": "recording.wav", "content_type": "audio/wav"} response = requests.post(url, headers=headers, json=data) result = response.json() upload_url = result["upload_url"] file_id = result["file_id"] print(f"Upload URL: {upload_url}") print(f"File ID: {file_id}") print(f"Expires at: {result['expires_at']}") # Step 2: Upload the file directly to GCS with open("/path/to/your/recording.wav", "rb") as f: upload_response = requests.put( upload_url, data=f, headers={"Content-Type": "audio/wav"} ) if upload_response.status_code == 200: print("File uploaded successfully!") ``` # Summarization Source: https://docs.sunbird.ai/guides/summarization Summarize text with the Sunflower model Summarization is now handled by the **Sunflower** conversational AI through the OpenAI-compatible `POST /tasks/chat/completions` endpoint. Instead of a dedicated summarization route, you send the text you want condensed along with a **summarization instruction prompt**. `sunflower-9b` is the default model. For the highest-accuracy summaries of Ugandan-language text, pass `"model": "sunflower-14b"` as in the examples below (English + 31 Ugandan/regional languages) — see [Choosing a Model](/guides/sunflower-chat#choosing-a-model). ## How it Works You drive summarization through the `messages` array: * A **system** message defines the summarization behavior (length, tone, and any anonymization requirements). * A **user** message carries the text to be summarized. This approach is flexible — adjust the instruction to control summary length, language, format (bullets vs. prose), and whether to anonymize personal identifiable information (PII). ### Example Request ```python Python theme={null} import requests url = "https://api.sunbird.ai/tasks/chat/completions" headers = { "accept": "application/json", "Authorization": "Bearer ", "Content-Type": "application/json", } text = ( "John Doe, who lives in Kampala, reported that the water supply was cut off " "yesterday at 2 PM. He called the utility company but got no response." ) payload = { "model": "sunflower-14b", "messages": [ { "role": "system", "content": ( "You are a summarization assistant. Produce a concise summary of " "the user's text. Anonymize any personal identifiable information " "(names, exact locations, contact details)." ), }, {"role": "user", "content": text}, ], "temperature": 0.3, } response = requests.post(url, headers=headers, json=payload) print(response.json()["choices"][0]["message"]["content"]) ``` ```python OpenAI SDK theme={null} from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.sunbird.ai/tasks", ) completion = client.chat.completions.create( model="sunflower-14b", messages=[ { "role": "system", "content": ( "You are a summarization assistant. Produce a concise summary of " "the user's text. Anonymize any personal identifiable information." ), }, {"role": "user", "content": "John Doe, who lives in Kampala, reported that the water supply was cut off yesterday at 2 PM."}, ], temperature=0.3, ) print(completion.choices[0].message.content) ``` ### Response The summary is returned in the standard chat completion format: ```json theme={null} { "id": "chatcmpl-...", "object": "chat.completion", "model": "sunflower-14b", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "A resident of Kampala reported a water supply interruption yesterday afternoon and was unable to reach the utility company." }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 58, "completion_tokens": 24, "total_tokens": 82 } } ``` Tune the system instruction to control the output — for example, "Summarize in three bullet points in Luganda" or "Give a one-sentence summary." See the [Sunflower Chat](/guides/sunflower-chat) guide for the full set of supported parameters. # Sunflower Chat Source: https://docs.sunbird.ai/guides/sunflower-chat Build conversational AI with the Sunflower models Sunflower is Sunbird AI's family of large language models, fine-tuned for African languages and cultural context. It provides conversational AI through an **OpenAI-compatible** endpoint: `POST /tasks/chat/completions`. Because the request and response formats mirror the OpenAI Chat Completions API, you can move between the OpenAI API and the Sunbird API by changing only the base URL and API key. ## Choosing a Model Select a model with the `model` field. Two models are available: | `model` | Coverage | Best for | | :--------------------------- | :-------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------- | | **`sunflower-9b`** (default) | **67 African languages** plus English (good / moderate / basic tiers) | Broad pan-African translation, instruction-following, and multi-turn chat | | **`sunflower-14b`** | English + **31 Ugandan / regional languages** (32 total) | High-accuracy translation, factual Q\&A, summarization, and explanation across Ugandan languages | `sunflower-9b` is the default when `model` is omitted — pass `"model": "sunflower-14b"` to keep using the 14B model. The old identifier `Sunbird/Sunflower-14B` is **no longer accepted** — sending it returns a `400` error asking you to use `sunflower-14b` instead. **`sunflower-9b` — supported languages** (67 African languages, grouped by the model's understanding tier): * **Good:** Afrikaans, Zulu, Nigerian Pidgin, Chichewa, Swahili, Xhosa, Somali, Lingala, Sotho, Igbo, Malagasy, Kinyarwanda, Hausa, Tswana, Yoruba, Luganda, Shona, Ndebele, Amharic, Kirundi, Runyoro, Luo, Ewe, Runyankole. * **Moderate:** Kikuyu, Lusoga, Acholi, Rukiga, Rutooro, Oromo, Bemba, Ruruuli, Akan, Lango, Lugwere, Lugbara, Kumam, Lugungu, Lumasaba, Ateso, Lunyole. * **Basic:** Kabyle, Rukonjo, Bambara, Lubwisi, Wolof, Samia, Jopadhola, Alur, Bari, Kakwa, Dagbani, Karamojong, Berber, Fulani, Kwamba, Ma'di, Aringa, Luhya, Kalenjin, Kupsabiny, Lendu, Dagaare, Ik, Pokot, Kanuri, Dinka. **`sunflower-14b` — supported languages** (English + 31 Ugandan/regional): Acholi (`ach`), Alur (`alz`), Aringa (`luc`), Ateso (`teo`), Bari (`bfa`), English (`eng`), Jopadhola (`adh`), Kakwa (`keo`), Karamojong (`kdj`), Kinyarwanda (`kin`), Kumam (`kdi`), Kupsabiny (`kpz`), Kwamba (`rwm`), Lango (`laj`), Lubwisi (`tlj`), Lugbara (`lgg`), Lugungu (`rub`), Lugwere (`gwr`), Luganda (`lug`), Lumasaba (`myx`), Lunyole (`nuj`), Ma'di (`mhi`), Pokot (`pok`), Rukiga (`cgg`), Rukonjo (`koo`), Runyankole (`nyn`), Runyoro (`nyo`), Ruruuli (`ruc`), Rutooro (`ttj`), Samia (`lsm`), Swahili (`swa`), Lusoga (`xog`). Per-language quality scales with the amount of training data available for each language. Full evaluations and language codes are on the model pages: [sunflower-14b](https://salt.sunbird.ai/models/sunflower-14b) and [sunflower-qwen3.5-9b](https://salt.sunbird.ai/models/sunflower-qwen3.5-9b/). ## Chat Completion ### Example Request ```python Python theme={null} import requests url = "https://api.sunbird.ai/tasks/chat/completions" headers = { "accept": "application/json", "Authorization": "Bearer ", "Content-Type": "application/json", } payload = { "model": "sunflower-14b", "messages": [ {"role": "user", "content": "Good morning, what is the weather today?"} ], "temperature": 0.3, } response = requests.post(url, headers=headers, json=payload) print(response.status_code) print(response.json()) ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.sunbird.ai/tasks/chat/completions', { method: 'POST', headers: { 'Authorization': 'Bearer ', 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'sunflower-14b', messages: [{ role: 'user', content: 'Good morning, what is the weather today?' }], temperature: 0.3 }) }); console.log(await response.json()); ``` ### Response ```json theme={null} { "id": "chatcmpl-8f14e45fceea167a5a36dedd4bea2543", "object": "chat.completion", "created": 1718000000, "model": "sunflower-14b", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "I'm glad you're up! While I can't provide real-time weather updates, I can help you understand weather forecasts or explain common weather patterns in Uganda." }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 22, "completion_tokens": 47, "total_tokens": 69 } } ``` ## Using the OpenAI SDK Because the endpoint is OpenAI-compatible, the official OpenAI Python SDK works out of the box — just point `base_url` at `https://api.sunbird.ai/tasks`: ```python theme={null} from openai import OpenAI client = OpenAI( api_key="", base_url="https://api.sunbird.ai/tasks", ) completion = client.chat.completions.create( model="sunflower-14b", messages=[ { "role": "user", "content": "translate from english to luganda: i am very hungry they should serve food in time", } ], temperature=0.1, ) print(completion.choices[0].message.content) # Ndi muyala nnyo, emmere erina okugabibwa mu budde. ``` ## Multi-turn Conversations Maintain context by sending the running message history. Each message has a `role` and `content`: * **system**: Sets the behavior and persona of the AI. When omitted, each model applies its own default Sunflower system message. * **user**: The input from the human user. * **assistant**: A previous response from the AI (used for history). ```python theme={null} payload = { "model": "sunflower-14b", "messages": [ {"role": "system", "content": "You are a helpful multilingual assistant."}, {"role": "user", "content": "Translate 'hello' to Luganda."}, {"role": "assistant", "content": "'Hello' is 'Gyebaleko'."}, {"role": "user", "content": "And to Acholi?"}, ], } ``` ## Streaming Set `"stream": true` to receive Server-Sent Events in OpenAI `chat.completion.chunk` format, terminated by `data: [DONE]`. With the OpenAI SDK: ```python theme={null} stream = client.chat.completions.create( model="sunflower-14b", messages=[{"role": "user", "content": "Tell me about Uganda."}], stream=True, ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` ## Supported Parameters | Parameter | Description | | :------------ | :------------------------------------------------------------------------------------------------------------- | | `model` | `sunflower-9b` (default) or `sunflower-14b`. The old `Sunbird/Sunflower-14B` identifier is no longer accepted. | | `messages` | The conversation history. | | `temperature` | 0.0–2.0, default 0.3. | | `max_tokens` | Maximum tokens to generate. | | `top_p` | Nucleus sampling. | | `stop` | Stop sequence(s). | | `stream` | Stream the response as SSE when `true`. | # Text to Speech Source: https://docs.sunbird.ai/guides/text-to-speech Convert text into natural-sounding speech in 20 African languages The Text to Speech (TTS) service converts written text into spoken audio. The unified **`POST /tasks/audio/speech`** endpoint is served by the **RunPod Orpheus-3B** deployment (`sunbird-orpheus-tts`) — multilingual and multi-speaker, covering 20 African languages with 40+ voices. Voices are catalog tags (e.g. `salt_lug_0001`); list them with `GET /tasks/voice/speakers`. **Breaking change (v2.1).** This endpoint previously accepted `model`, `platform`, and `max_new_audio_tokens`, and offered a `spark-tts` model. RunPod Orpheus is now the only TTS backend: those parameters are **rejected**, and the legacy per-provider routes (`/tasks/modal/tts`, `/tasks/runpod/tts`, `/tasks/modal/orpheus/*`, `/tasks/tts`, the `/stream*` and speaker-listing routes, and `refresh-url`) were **removed**. Use the endpoints below. ## Single Synthesis ### Parameters | Parameter | Type | Required | Description | | :------------------------------------------- | :------ | :------- | :---------------------------------------------------------------------------------------------------------------------------------- | | `text` | string | Yes | The text to convert to speech. | | `voice` | string | No | Orpheus speaker catalog tag (see `GET /tasks/voice/speakers`). If omitted, a speaker is chosen for `language` (Luganda by default). | | `language` | string | No | ISO 639-3 code (e.g. `lug`) or full name. | | `response_mode` | string | No | `url` (default), `stream`, or `both` — see [Response Modes](#response-modes). | | `temperature`, `top_p`, `repetition_penalty` | number | No | Sampling knobs. | | `max_tokens` | integer | No | Max tokens per chunk. | | `seed` | integer | No | Deterministic seed. | ```python Python theme={null} import os import requests from dotenv import load_dotenv load_dotenv() url = "https://api.sunbird.ai/tasks/audio/speech" access_token = os.getenv("AUTH_TOKEN") headers = { "accept": "application/json", "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", } payload = { "text": "I am a nurse who takes care of many people.", "voice": "salt_lug_0001", # catalog tag; see GET /tasks/voice/speakers "language": "eng", # ISO code or full name; picks a voice if none given "response_mode": "url", # "url" (default), "stream", or "both" } response = requests.post(url, headers=headers, json=payload) print(response.status_code) print(response.json()) ``` ```javascript JavaScript theme={null} const response = await fetch('https://api.sunbird.ai/tasks/audio/speech', { method: 'POST', headers: { 'Authorization': 'Bearer ', 'Content-Type': 'application/json' }, body: JSON.stringify({ text: 'I am a nurse who takes care of many people.', voice: 'salt_lug_0001', language: 'eng', response_mode: 'url' }) }); console.log(await response.json()); ``` ### Response ```json theme={null} { "audio_url": "https://storage.googleapis.com/.../tts_audio/....wav?...", "model": "sunbird-orpheus-tts", "platform": "runpod", "voice": "salt_lug_0001", "audio_url_expires_at": "2026-06-03T22:59:36.954061Z", "language": "lug", "sample_rate": 24000, "duration_seconds": 4.0, "gcs_object": "orpheus_tts/2026-06-03/....wav", "request_id": "0f1e2d3c4b5a...", "usage": { "input_characters": 43, "output_audio_bytes": 192044, "duration_seconds": 4.0 }, "timings_ms": {"inference_ms": 1820.5, "upload_ms": 234.1, "total_ms": 2095.6} } ``` The `audio_url` is a temporary signed URL valid for \~30 minutes. Download or stream the audio promptly, or re-sign it with `GET /tasks/audio/speech/url`. ## Response Modes `response_mode` controls how the audio is returned: * `url` — synthesize, upload to GCP, return a signed URL (valid \~30 minutes) — default * `stream` — stream the raw audio bytes straight back (constant client memory; no GCS upload) * `both` — stream the raw audio bytes **and** return the signed URL in the `X-Audio-Url` response header RunPod synthesizes the full waveform before sending the first byte, so `stream` bounds client memory but does not reduce time-to-first-audio. ## Languages and Voices Speaker IDs encode both the source corpus (`salt_*`, `waxal_*`, `slr32_*`, `slr129_*`, `bateesa_*`) and the language. Languages marked with an em dash are present in the model's training mix but do **not** currently expose individual voice IDs in this checkpoint. | Config | Language | Region | Speaker IDs | | ------ | ------------ | ---------------------- | --------------------------------------------------------------------------------------- | | `ach` | Acholi | Uganda, South Sudan | `salt_ach_0001`, `waxal_ach_0001`, `waxal_ach_0005`, `waxal_ach_0006`, `waxal_ach_0008` | | `afr` | Afrikaans | South Africa, Namibia | `slr32_afr_0009` | | `eng` | English | (control language) | `salt_eng_0001`, `salt_eng_0002`, `salt_eng_0003` | | `ewe` | Ewe | Ghana, Togo | `slr129_ewe_0001` | | `ful` | Fulah | West Africa (Sahel) | `waxal_ful_0003`, `waxal_ful_0004`, `waxal_ful_0006` | | `hau` | Hausa | Nigeria, Niger, Chad | `waxal_hau_0004`, `waxal_hau_0006`, `waxal_hau_0007`, `waxal_hau_0008` | | `ibo` | Igbo | Nigeria | `waxal_ibo_0003`, `waxal_ibo_0005`, `waxal_ibo_0008` | | `kik` | Kikuyu | Kenya | `waxal_kik_0003`, `waxal_kik_0004` | | `kin` | Kinyarwanda | Rwanda | `bateesa_kin_0001` | | `lgg` | Lugbara | Uganda, DRC | — | | `lin` | Lingala | DRC, Republic of Congo | `slr129_lin_0001` | | `lug` | Luganda | Uganda | `salt_lug_0001`, `waxal_lug_0002`–`waxal_lug_0008` | | `luo` | Luo (Dholuo) | Kenya, Tanzania | `waxal_luo_0001`–`waxal_luo_0004` | | `nyn` | Runyankole | Uganda | `salt_nyn_0001`, `waxal_nyn_0003`, `waxal_nyn_0004`, `waxal_nyn_0007`, `waxal_nyn_0008` | | `sot` | Sesotho | Lesotho, South Africa | — | | `swa` | Swahili | East Africa | `waxal_swa_0006`, `waxal_swa_0007` | | `teo` | Ateso | Uganda, Kenya | `salt_teo_0001` | | `tsn` | Setswana | Botswana, South Africa | — | | `xho` | Xhosa | South Africa | `slr32_xho_0012` | | `yor` | Yoruba | Nigeria, Benin | `waxal_yor_0002`, `waxal_yor_0006`, `waxal_yor_0008` | Per-language quality scales with the amount of training data collected for that language. **Audition the voices** before relying on a particular speaker — use the discovery snippet under [Listing Voices](#listing-voices). ## Listing Voices ```python theme={null} auth = {"Authorization": f"Bearer {access_token}"} # Orpheus voices grouped by language (default) print(requests.get("https://api.sunbird.ai/tasks/voice/speakers", headers=auth).json()) # Voices for one language print(requests.get( "https://api.sunbird.ai/tasks/voice/speakers", headers=auth, params={"language": "lug"}, ).json()) ``` ## Batch Synthesis Synthesize up to **16** items in a single request — each item is its own RunPod queue job and they run concurrently. Per-item failures are reported with `status="error"`; the request returns `200` if at least one item succeeds and `502` if every item fails. ```python theme={null} url = "https://api.sunbird.ai/tasks/audio/speech/batch" payload = { "items": [ {"text": "Good morning.", "voice": "salt_lug_0001"}, {"text": "How are you?", "voice": "salt_eng_0001"}, ] } response = requests.post(url, headers=headers, json=payload) print(response.json()) ``` ## Refreshing an Expired URL Signed URLs expire after \~30 minutes. Re-sign a stored object with `GET /tasks/audio/speech/url`: ```python theme={null} print(requests.get( "https://api.sunbird.ai/tasks/audio/speech/url", headers={"Authorization": f"Bearer {access_token}"}, params={"gcs_object": "orpheus_tts/2026-06-03/abc.wav"}, ).json()) ``` # Introduction Source: https://docs.sunbird.ai/introduction Welcome to the Sunbird AI Developer Documentation Sunbird AI provides a suite of open-source AI tools and APIs for African languages. Our platform includes high-quality models for translation, speech recognition, and text generation. ## Get Started Make your first API request in 5 minutes. Walk through every endpoint with runnable Python examples. Transcribe audio files in Ugandan languages. View the list of supported languages and codes. Train and finetune custom models with our Python library. ## Explore Models Run our 14B and 32B multilingual LLMs efficiently. ## Supported Languages We currently support over 30 languages, with a focus on Ugandan and East African dialects. Key features include translation for all, plus STT and TTS for select ones. For the full list, including codes, support levels, and TTS IDs, see our [Supported Languages reference](languages). ## Getting Started Ready to start building? Follow these steps to make your first API call. Sign up for a free account to get your API credentials. Generate an access token to authenticate your requests. Use our SDKs or HTTP client to interact with the API. Get up and running in less than 5 minutes. ## Community and Support Need help? We're here for you. * [GitHub](https://github.com/SunbirdAI) - Report bugs and contribute. # Supported Languages Source: https://docs.sunbird.ai/languages Detailed list of languages supported by Sunbird AI, including translation, STT, TTS, and notes. ## Sunbird AI Language Support by Endpoint Sunbird AI focuses on African languages, especially Ugandan dialects. Below is a comprehensive table of supported languages. For integration details, see the [API Reference](/api-reference/introduction) or [Guides](/guides/speech-to-text). Coverage differs per endpoint: speech-to-text spans **51 African languages**, chat/translation spans **67** (`sunflower-9b`) or **32** (`sunflower-14b`), and text-to-speech spans **20**. **Code** is the canonical ISO/SALT code the API accepts (full language names also work where an endpoint takes a `language` or `voice`). * **`/tasks/audio/speech`** — text-to-speech (sunbird-orpheus-tts voice catalog). See [Text to Speech](/guides/text-to-speech). * **`/tasks/audio/transcriptions`** — speech-to-text, powered by Sunbird's faster-whisper ASR model covering 51 African languages. See [Speech to Text](/guides/speech-to-text). * **`/tasks/chat/completions`** — Sunflower chat. The column below reflects the default **`sunflower-9b`** model (67 African languages plus English), which `/tasks/translate` shares. The alternative **`sunflower-14b`** model covers **English + 31 Ugandan/regional languages** — see [Choosing a Model](/guides/sunflower-chat#choosing-a-model). | Language name | Code | TTS `/tasks/audio/speech` | STT `/tasks/audio/transcriptions` | Sunflower `/tasks/chat/completions` | | :---------------- | :---- | :-----------------------: | :-------------------------------: | :---------------------------------: | | Acholi | `ach` | ✅ | ✅ | ✅ | | Afrikaans | `afr` | ✅ | ✅ | ✅ | | Akan | `aka` | x | ✅ | ✅ | | Alur | `alz` | x | x | ✅ | | Amharic | `amh` | x | ✅ | ✅ | | Aringa | `luc` | x | x | ✅ | | Ateso | `teo` | ✅ | ✅ | ✅ | | Bambara | `bam` | x | ✅ | ✅ | | Bari | `bfa` | x | x | ✅ | | Bemba | `bem` | x | ✅ | ✅ | | Berber | `ber` | x | ✅ | ✅ | | Chichewa | `nya` | x | ✅ | ✅ | | Dagaare | `dga` | x | ✅ | ✅ | | Dagbani | `dag` | x | ✅ | ✅ | | Dinka | `din` | x | x | ✅ | | English | `eng` | ✅ | ✅ | ✅ | | Ewe | `ewe` | ✅ | ✅ | ✅ | | French | `fra` | x | ✅ | x | | Fulah / Fulani | `ful` | ✅ | ✅ | ✅ | | Hausa | `hau` | ✅ | ✅ | ✅ | | Igbo | `ibo` | ✅ | ✅ | ✅ | | Ik | `ikx` | x | x | ✅ | | Ikposo | `kpo` | x | ✅ | x | | Jopadhola | `adh` | x | x | ✅ | | Kabyle | `kab` | x | ✅ | ✅ | | Kakwa | `keo` | x | x | ✅ | | Kalenjin | `kln` | x | ✅ | ✅ | | Kanuri | `kau` | x | ✅ | ✅ | | Karamojong | `kdj` | x | x | ✅ | | Kikuyu | `kik` | ✅ | ✅ | ✅ | | Kinyarwanda | `kin` | ✅ | ✅ | ✅ | | Kirundi | `run` | x | x | ✅ | | Kumam | `kdi` | x | x | ✅ | | Kupsabiny | `kpz` | x | x | ✅ | | Kwamba | `rwm` | x | ✅ | ✅ | | Lango | `laj` | x | x | ✅ | | Lendu | `led` | x | ✅ | ✅ | | Lingala | `lin` | ✅ | ✅ | ✅ | | Lubwisi | `tlj` | x | x | ✅ | | Lugbara | `lgg` | ✅ † | ✅ | ✅ | | Lugungu | `rub` | x | x | ✅ | | Lugwere | `gwr` | x | x | ✅ | | Luganda | `lug` | ✅ | ✅ | ✅ | | Luhya | `luy` | x | ✅ | ✅ | | Lumasaba | `myx` | x | ✅ | ✅ | | Lunyole | `nuj` | x | x | ✅ | | Luo (Dholuo) | `luo` | ✅ | ✅ | ✅ | | Lusoga | `xog` | x | ✅ | ✅ | | Ma'di | `mhi` | x | x | ✅ | | Malagasy | `mlg` | x | ✅ | ✅ | | Ndebele | `nbl` | x | ✅ | ✅ | | Nigerian Pidgin | `pcm` | x | ✅ | ✅ | | Oromo | `orm` | x | ✅ | ✅ | | Pokot | `pok` | x | x | ✅ | | Rukiga | `cgg` | x | ✅ | ✅ | | Rukonjo | `koo` | x | ✅ | ✅ | | Runyankole | `nyn` | ✅ | ✅ | ✅ | | Runyoro | `nyo` | x | x | ✅ | | Ruruuli | `ruc` | x | ✅ | ✅ | | Rutooro | `ttj` | x | ✅ | ✅ | | Samia | `lsm` | x | x | ✅ | | Sesotho / Sotho | `sot` | ✅ † | ✅ | ✅ | | Setswana / Tswana | `tsn` | ✅ † | ✅ | ✅ | | Shona | `sna` | x | ✅ | ✅ | | Somali | `som` | x | ✅ | ✅ | | Swahili | `swa` | ✅ | ✅ | ✅ | | Thur | `lth` | x | ✅ | x | | Wolof | `wol` | x | ✅ | ✅ | | Xhosa | `xho` | ✅ | ✅ | ✅ | | Yoruba | `yor` | ✅ | ✅ | ✅ | | Zulu | `zul` | x | ✅ | ✅ | **†** Lugbara, Sesotho, and Setswana are in the sunbird-orpheus-tts training mix but currently expose **no individual voice IDs** in this checkpoint, so practical synthesis depends on a future voice release. # Quickstart Source: https://docs.sunbird.ai/quickstart Get started with the Sunbird AI API in minutes This guide will help you make your first API call to Sunbird AI. We'll walk through creating an account, getting an access token, and translating text. ## Prerequisites Before you begin, ensure you have: * A valid email address for registration. * Basic knowledge of HTTP requests or a programming language like Python or JavaScript. * `curl` installed (optional, for command-line testing). ## Step 1: Create an Account You can create an account using the `/auth/register` endpoint or through our web portal (if available). For this guide, we'll use the API. ```bash cURL theme={null} curl -X POST "https://api.sunbird.ai/auth/register" \ -H "Content-Type: application/json" \ -d '{ "email": "your-email@example.com", "password": "strongpassword123", "username": "yourusername" }' ``` ## Step 2: Get an Access Token Once registered, you need to obtain an access token to authenticate your requests. The token **does not expire**. ```bash cURL theme={null} curl -X POST "https://api.sunbird.ai/auth/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=your-email@example.com&password=strongpassword123" ``` The response will contain your `access_token`. Keep this safe! ```json theme={null} { "access_token": "eyJhbGciOiJIUzI1NiIsInR...", "token_type": "bearer" } ``` ## Step 3: Make Your First API Call Now let's detect the language of a text snippet. ```python Python theme={null} import requests API_TOKEN = "YOUR_ACCESS_TOKEN" BASE_URL = "https://api.sunbird.ai" headers = { "Authorization": f"Bearer {API_TOKEN}", "Content-Type": "application/json" } payload = { "text": "Muli mutya bannange?" } response = requests.post(f"{BASE_URL}/tasks/language_id", json=payload, headers=headers) print(response.json()) ``` ```javascript JavaScript theme={null} const API_TOKEN = 'YOUR_ACCESS_TOKEN'; const BASE_URL = 'https://api.sunbird.ai'; async function detectLanguage() { const response = await fetch(`${BASE_URL}/tasks/language_id`, { method: 'POST', headers: { 'Authorization': `Bearer ${API_TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ text: 'Muli mutya bannange?' }) }); const data = await response.json(); console.log(data); } detectLanguage(); ``` ```bash cURL theme={null} curl -X POST "https://api.sunbird.ai/tasks/language_id" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "text": "Muli mutya bannange?" }' ``` ### Response You should receive a JSON response with the language code: ```json theme={null} { "language": "lug", "confidence": 0.99 } ``` ## Next Steps Congratulations! You've successfully used the Sunbird AI API. Here's what you can explore next: Learn more about token management and security. Step-by-step walkthrough of every endpoint with Python examples. Transcribe audio files. Explore all available endpoints. ## Troubleshooting Ensure your access token is correct. If lost, generate a new one using the `/auth/token` endpoint. Check your request body. Ensure all required fields (like `source_language` and `target_language`) are present and valid. # Changelog Source: https://docs.sunbird.ai/resources/changelog API changes, new endpoints, and deprecations This page tracks notable changes to the Sunbird AI API. Use the **version dropdown** at the top of the sidebar to switch between the current API (**v2.1 · Latest**) and the older endpoints (**v1 · Deprecated**). ## Breaking changes ### Speech to Text — `POST /tasks/audio/transcriptions` * Now powered by Sunbird's faster-whisper ASR model ([`Sunbird/faster-whisper-51-african-languages`](https://huggingface.co/Sunbird/faster-whisper-51-african-languages)) covering **51 African languages** (up from 10). All previously supported languages remain available. * `audio` and `language` are both **required**. Automatic language detection is not supported — always pass an explicit ISO 639-3 code. * New `timestamps` flag (default `false`) returns per-segment `start`/`end`/`text` in a new `segments` response field. The response also gains `duration_seconds` and `usage`. * **Removed parameters** (now rejected with `422`): `platform`, `adapter`, `whisper`, `recognise_speakers`, `org`, `gcs_blob_name`. Speaker diarization and the organization workflow are no longer available on this endpoint. ### Text to Speech — `POST /tasks/audio/speech` * All synthesis is served by the **RunPod Orpheus-3B** deployment (`sunbird-orpheus-tts`, 20 African languages / 40+ speakers). The `spark-tts` model and the Modal backend were removed. * **Removed parameters** (now rejected): `model`, `platform`, `max_new_audio_tokens`. New tuning knobs: `temperature`, `top_p`, `repetition_penalty` (alongside `max_tokens` and `seed`). * `voice` is now optional — when omitted a speaker is chosen for `language` (Luganda by default). * `response_mode` (`url` | `stream` | `both`) now applies to all requests; `both` returns the signed URL in the `X-Audio-Url` header. * `POST /tasks/audio/speech/batch` accepts **1–16 items** per request (previously 128). `GET /tasks/voice/speakers` drops the `model` query parameter. * Responses now always include `platform` (`runpod`) and add `usage` (`input_characters`, `output_audio_bytes`, `duration_seconds`). * The legacy per-provider TTS routes were **removed**: `/tasks/tts`, `/tasks/modal/tts` (+ `/stream`, `/stream-with-url`, `/refresh-url`, `/speakers`), `/tasks/modal/orpheus/*`, `/tasks/runpod/tts`. ### Chat — `POST /tasks/chat/completions` * The `model` field now selects between **`sunflower-9b`** (default; 67 African languages plus English) and **`sunflower-14b`** (English + 31 Ugandan/regional languages). Pass `"model": "sunflower-14b"` to keep using the 14B model. Each model applies its own default system prompt when the request omits one. * The old identifier `Sunbird/Sunflower-14B` is **no longer accepted** and returns `400`. ## Other changes * `POST /tasks/translate` now runs on `sunflower-9b` and translates between any pair of **67 African languages plus English** (up from 32). Unsupported languages return `400`. Responses gain an optional `usage` object with Sunflower token counts. * `POST /tasks/language_id` is backed by a 64-language classifier and returns a `confidence` score; when no language clears the threshold (`0.35`) it returns `"language not detected"`. `POST /tasks/classify_language` was folded into it and **removed**. * `POST /auth/register` no longer accepts `account_type`, `oauth_type`, or `email_verified` in the request body — they are always set server-side. * `POST /auth/register` and `GET /auth/me` expose `email_verified`. ## New endpoints * `GET /auth/verify-email` — landing endpoint for the emailed verification link. * `POST /auth/resend-verification` — resend the verification email for the authenticated user. * `POST /auth/refresh-token` — rotate the caller's API key (revokes the previous token). ## Unified endpoints We consolidated the speech, text-to-speech, and conversational AI surfaces into a smaller set of unified endpoints. The old routes still work but are **deprecated** and now return `Deprecation` and `Sunset` response headers. They are documented under the **v1 · Deprecated** version, and the full list is kept on the [Deprecations](/resources/deprecations) page. ### Migration map | Area | Deprecated (v1) | Use instead (v2) | | :---------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------- | | Speech to Text | `POST /tasks/stt`, `POST /tasks/modal/stt`, `POST /tasks/stt_from_gcs`, `POST /tasks/org/stt` | `POST /tasks/audio/transcriptions` | | Text to Speech | `POST /tasks/tts`, `POST /tasks/modal/tts` (+ `/stream`, `/stream-with-url`, `/refresh-url`, `/speakers`), `POST /tasks/modal/orpheus/tts` (+ `/batch`, `/speakers`), `POST /tasks/runpod/tts` | `POST /tasks/audio/speech`, `POST /tasks/audio/speech/batch`, `GET /tasks/voice/speakers`, `GET /tasks/audio/speech/url` | | Conversational AI | `POST /tasks/sunflower_inference`, `POST /tasks/sunflower_simple` | `POST /tasks/chat/completions` (OpenAI-compatible) | | Summarization | `POST /tasks/summarise` | `POST /tasks/chat/completions` with a summarization instruction prompt | ### Highlights * **Speech to Text** — a single `POST /tasks/audio/transcriptions` endpoint routes to the Modal (Whisper large-v3) or RunPod backend, with optional speaker diarization. * **Text to Speech** — `POST /tasks/audio/speech` exposes two models (`orpheus-3b-tts` and `spark-tts`), plus batch synthesis, voice discovery, and signed-URL refresh. * **Conversational AI** — `POST /tasks/chat/completions` is OpenAI-compatible, so the official OpenAI SDKs work by changing only the base URL and API key. It supports multi-turn conversations and streaming. * **Translation** — `POST /tasks/translate` (Sunflower) now translates between any pair of 32 languages, with optional source-language inference. ### New endpoints * `PUT /auth/profile` — Update Profile * `GET /auth/profile/status` — Profile Status * `POST /tasks/classify_language` — Classify Language Migrating an existing integration? See the per-feature [Guides](/guides/speech-to-text) for full request/response examples, or jump straight to the relevant endpoint page in this API Reference. # Deprecations Source: https://docs.sunbird.ai/resources/deprecations Deprecated and removed endpoints, and what to use instead This page lists every endpoint that has been deprecated or removed from the Sunbird AI API, together with its replacement. Deprecated routes are **not** documented in the current API Reference — do not build new integrations against them. ## How deprecation works * A **deprecated** route still responds, but every response carries `Deprecation`, `Sunset`, and `Link` headers. `Sunset` is the date after which the route will be removed; `Link` points at the replacement. * A **removed** route returns `404`. * Inspect these headers in your client and migrate before the sunset date. See [Error Handling](/guides/error-handling#deprecation-headers). Reference pages for the older routes remain available under the **v1 · Deprecated** version in the sidebar dropdown, for teams still completing a migration. ## Deprecated endpoints (still responding) | Deprecated route | Use instead | Notes | | :-------------------------------- | :--------------------------------- | :--------------------------------------------------------------------------------------------------- | | `POST /tasks/stt` | `POST /tasks/audio/transcriptions` | Pass `language` explicitly (required). Speaker diarization is not available on the unified endpoint. | | `POST /tasks/stt_from_gcs` | `POST /tasks/audio/transcriptions` | Upload the file directly; `gcs_blob_name` is no longer accepted. | | `POST /tasks/org/stt` | `POST /tasks/audio/transcriptions` | The organization workflow is not available on the unified endpoint. | | `POST /tasks/modal/stt` | `POST /tasks/audio/transcriptions` | Same request shape minus `platform`/`adapter`/`whisper`. | | `POST /tasks/sunflower_inference` | `POST /tasks/chat/completions` | OpenAI-compatible; send the instruction as messages. | | `POST /tasks/sunflower_simple` | `POST /tasks/chat/completions` | A single instruction is a request with one `user` message. | | `POST /tasks/summarise` | `POST /tasks/chat/completions` | Use a summarization system prompt — see [Summarization](/guides/summarization). | ## Removed endpoints These routes were removed in **v2.1** and now return `404`. | Removed route | Use instead | | :------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------- | | `POST /tasks/tts`, `POST /tasks/modal/tts`, `POST /tasks/runpod/tts`, `POST /tasks/modal/orpheus/tts` | `POST /tasks/audio/speech` | | `POST /tasks/modal/tts/stream` | `POST /tasks/audio/speech` with `response_mode: "stream"` | | `POST /tasks/modal/tts/stream-with-url` | `POST /tasks/audio/speech` with `response_mode: "both"` | | `POST /tasks/modal/orpheus/tts/batch` | `POST /tasks/audio/speech/batch` (1–16 items) | | `GET /tasks/modal/tts/speakers`, `GET /tasks/modal/orpheus/speakers`, `GET /tasks/modal/orpheus/speakers/{language}` | `GET /tasks/voice/speakers` (optional `language`) | | `GET /tasks/modal/tts/refresh-url` | `GET /tasks/audio/speech/url` | | `POST /tasks/classify_language` | `POST /tasks/language_id` (now returns `confidence` and `"language not detected"` below the threshold) | ## Removed parameters and values | Endpoint | Removed | Replacement | | :--------------------------------- | :--------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------- | | `POST /tasks/audio/transcriptions` | `platform`, `adapter`, `whisper`, `recognise_speakers`, `org`, `gcs_blob_name` (rejected with `422`) | Send `audio`, `language`, and optionally `timestamps`. | | `POST /tasks/audio/speech` | `model`, `platform`, `max_new_audio_tokens`; the `spark-tts` model | RunPod Orpheus is the only backend. Tune with `temperature`, `top_p`, `repetition_penalty`, `max_tokens`, `seed`. | | `GET /tasks/voice/speakers` | `model` query parameter | Filter with `language` only. | | `POST /tasks/chat/completions` | `model: "Sunbird/Sunflower-14B"` (returns `400`) | `sunflower-9b` (default) or `sunflower-14b`. | | `POST /auth/register` | `account_type`, `oauth_type`, `email_verified` (ignored; always set server-side) | Send `username`, `email`, `password`, `organization`, and the optional profile fields. | # API Tutorial Source: https://docs.sunbird.ai/resources/tutorial Complete walkthrough of every Sunbird AI endpoint with runnable Python examples # Inference Source: https://docs.sunbird.ai/sunflower/inference # Sunbird Sunflower Quantized Model Usage ## Installation ````markdown theme={null} ## Installation Install the required Python packages: ```bash !pip install torch transformers accelerate bitsandbytes ```` * **torch**: PyTorch library for model computation. * **transformers**: Hugging Face Transformers library for model handling. * **accelerate**: Optimizes model loading across devices. * **bitsandbytes**: Enables efficient 4-bit and 8-bit model quantization. *** ## Import the necessary libraries for loading and running the model. ```python theme={null} import torch import transformers from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig import os from getpass import getpass ``` ## 3. Authentication ```python theme={null} # Set your Hugging Face token to access models os.environ["HF_TOKEN"] = getpass("HF_TOKEN: ") ``` *** ## 4. Load Tokenizer and Model Replace `` with the specific model you want to use, e.g., `Sunbird/Sunflower-14B-4bit-nf4-bnb` or `Sunbird/Sunflower-14B-8bit-bnb`. ```python theme={null} # Load tokenizer tokenizer = AutoTokenizer.from_pretrained("") # Configure quantization (choose one) quantization_config = BitsAndBytesConfig( # For 4-bit models load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16, bnb_4bit_use_double_quant=True, # For 8-bit models, comment out above and use: # load_in_8bit=True, # llm_int8_threshold=6.0 ) # Load model model = AutoModelForCausalLM.from_pretrained( "", quantization_config=quantization_config, device_map="auto", # or "cuda:0" for single GPU trust_remote_code=True ) ``` > Note: Only change `` and adjust quantization parameters. Everything else remains the same for any model. *** ## 5. Prepare Chat Prompt ```python theme={null} # Define system message (assistant role) SYSTEM_MESSAGE = """You are Sunflower, a multilingual assistant for Ugandan languages made by Sunbird AI. You specialise in accurate translations, explanations, summaries, and other cross-lingual tasks.""" # Example user prompt prompt_text = "Sunbird AI is a non-profit research organization in Kampala, Uganda. We build and deploy practical applied machine learning systems for African contexts." user_prompt = f"Translate to Luganda: {prompt_text}" # Structure messages messages = [ {"role": "system", "content": SYSTEM_MESSAGE}, {"role": "user", "content": user_prompt} ] # Prepare model input prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) inputs = tokenizer([prompt], return_tensors="pt").to(model.device) ``` *** ## 6. Configure Text Generation ```python theme={null} # Streamer outputs tokens in real-time text_streamer = transformers.TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True) # Generation parameters num_beams = 5 # Adjust for higher quality generation_config = { "max_new_tokens": 512, # Maximum tokens to generate "temperature": 0.3, # Lower = more deterministic "do_sample": True, # Enable sampling "no_repeat_ngram_size": 5, # Avoid repeated phrases "num_beams": num_beams, # Beam search width } ``` * Beam search improves output quality but slows generation. * `TextStreamer` works only when `num_beams == 1`. *** ## 7. Generate Output ```python theme={null} # Generate model output outputs = model.generate( **inputs, **generation_config, streamer=text_streamer if num_beams == 1 else None, ) # Decode if using multi-beam search if num_beams > 1: response = tokenizer.decode(outputs[0][len(inputs['input_ids'][0]):], skip_special_tokens=True) print(response) ``` * Single-beam: real-time streaming. * Multi-beam: output decoded after generation. *** ## 8. Notes for Future Models * The workflow is **identical for any Sunflower model**. * Only model-specific changes: 1. `` in `from_pretrained`. 2. Quantization parameters (`load_in_4bit` / `load_in_8bit`). * Everything else (chat template, streaming, generation) stays the same. *** # LLM Compression Source: https://docs.sunbird.ai/sunflower/llm_compression # Overview Source: https://docs.sunbird.ai/sunflower/overview # 🌻 Sunflower Quantized Inference The **Sunflower** models are available in **14B** and **32B** sizes, supporting **8-bit** and **4-bit quantized inference**. These models are capable of high-quality **translation**, text generation, and conversational tasks, optimized for efficient performance on GPUs with limited memory. Balanced memory usage (\~16GB for 14B) with high accuracy. Low memory usage (\~10GB for 14B) and faster inference speeds. Do not set both 8-bit and 4-bit modes simultaneously. ## Model Variants ### Sunflower 14B * **14B 8-bit:** Balanced memory and accuracy, suitable for most GPUs. * **14B 4-bit:** Optimized for memory-limited GPUs and faster inference. ### Sunflower 32B * **32B 8-bit:** High accuracy, requires significant VRAM. * **32B 4-bit:** Reduced memory usage, slightly lower accuracy but faster. ## Best Practices Use **4-bit models** when GPU memory is constrained. Use **8-bit models** when you need maximum accuracy and have sufficient VRAM. * Monitor GPU memory for large inputs or batch processing. * Adjust inference parameters (sequence length) to fit your hardware limits. # Quantization Source: https://docs.sunbird.ai/sunflower/quantization # Converting Sunflower LoRA Fine-tuned Models to GGUF Quantizations In this guide, provide a tutorial for converting LoRA fine-tuned Sunflower models to GGUF format with multiple quantization levels, including experimental ultra-low bit quantizations. ## Table of Contents * [Prerequisites](#prerequisites) * [Environment Setup](#environment-setup) * [Model Preparation](#model-preparation) * [LoRA Merging](#lora-merging) * [GGUF Conversion](#gguf-conversion) * [Quantization Process](#quantization-process) * [Experimental Quantizations](#experimental-quantizations) * [Quality Testing](#quality-testing) * [Ollama Integration](#ollama-integration) * [Distribution](#distribution) * [Troubleshooting](#troubleshooting) ## Prerequisites ### Hardware Requirements * **RAM**: Minimum 32GB (64GB recommended for 14B+ models) * **Storage**: 200GB+ free space for intermediate files * **GPU**: Optional but recommended for faster processing ### Software Requirements * Linux/macOS (WSL2 for Windows) * Python 3.9+ * Git and Git LFS * CUDA toolkit (optional, for GPU acceleration) ## Environment Setup ### 1. Install Dependencies ```bash theme={null} # Install Python packages pip install torch transformers accelerate pip install sentencepiece protobuf pip install peft huggingface_hub pip install -U huggingface_hub # Install system dependencies (Ubuntu/Debian) sudo apt update sudo apt install -y cmake build-essential git git-lfs ``` ### 2. Clone and Build llama.cpp ```bash theme={null} # Clone llama.cpp git clone https://github.com/ggml-org/llama.cpp cd llama.cpp # Build with CUDA support (if available) cmake -B build -DGGML_CUDA=ON cmake --build build --config Release # OR build for CPU only make clean && make # Create working directories mkdir -p models gguf_outputs cd .. ``` ## Model Preparation ### 1. Download Models ```bash theme={null} # Download base model huggingface-cli download jq/sunflower-14b-bs64-lr1e-4 --local-dir models/base_model # Download LoRA adapter huggingface-cli download jq/qwen3-14b-sunflower-20250915 --local-dir models/lora_model # Download merged model (LoRA already merged) huggingface-cli download Sunbird/qwen3-14b-sunflower-merged --local-dir models/merged_model ``` **Example:** ```bash theme={null} huggingface-cli download jq/sunflower-14b-bs64-lr1e-4 --local-dir models/base_model huggingface-cli download jq/qwen3-14b-sunflower-20250915 --local-dir models/lora_model # Download merged model (LoRA already merged) huggingface-cli download Sunbird/qwen3-14b-sunflower-merged --local-dir models/merged_model ``` ## LoRA Merging 1. Create Merge Script (Skip if Using Pre-merged Model) Note: If you downloaded Sunbird/qwen3-14b-sunflower-merged, skip this section and go directly to GGUF Conversion. Create merge\_lora.py only if using separate base model and LoRA adapter: Create `merge_lora.py`: ```python theme={null} #!/usr/bin/env python3 """ Merge LoRA adapter with base model """ from transformers import AutoModelForCausalLM, AutoTokenizer from peft import PeftModel import torch import sys def merge_lora_model(): print("Loading base model...") base_model = AutoModelForCausalLM.from_pretrained( "models/base_model", torch_dtype=torch.bfloat16, # Use bfloat16 for better precision device_map="auto", low_cpu_mem_usage=True ) print("Loading LoRA adapter...") model = PeftModel.from_pretrained(base_model, "models/lora_model") print("Merging LoRA with base model...") merged_model = model.merge_and_unload() print("Saving merged model...") merged_model.save_pretrained("models/merged_model", safe_serialization=True) # Save tokenizer print("Saving tokenizer...") tokenizer = AutoTokenizer.from_pretrained("models/base_model") tokenizer.save_pretrained("models/merged_model") print("Merge completed successfully!") print("Merged model saved to: models/merged_model") if __name__ == "__main__": merge_lora_model() ``` ### 2. Run Merge Process ```bash theme={null} python merge_lora.py ``` **Expected output**: Merged model saved to `models/merged_model` ## GGUF Conversion ### 1. Convert to F16 GGUF ```bash theme={null} # Convert merged model to F16 GGUF (preserves quality for quantization) python3 llama.cpp/convert_hf_to_gguf.py models/merged_model \ --outfile gguf_outputs/model-merged-f16.gguf \ --outtype f16 # Verify file size (should be ~2GB per billion parameters for F16) ls -lh gguf_outputs/model-merged-f16.gguf ``` **Expected size**: \~28GB for 14B model in F16 ## Quantization Process ### 1. Generate Importance Matrix The importance matrix (imatrix) significantly improves quantization quality by identifying which weights are most critical to model performance. ```bash theme={null} # Download calibration dataset wget [https://raw.githubusercontent.com/ggerganov/llama.cpp/master/examples/perplexity/wiki.test.raw](https://huggingface.co/nisten/llama3-8b-instruct-32k-gguf/raw/main/wiki.test.raw) # Generate importance matrix (this takes 30-60 minutes) ./llama.cpp/build/bin/llama-imatrix \ -m gguf_outputs/model-merged-f16.gguf \ -f wiki.test.raw \ --chunk 512 \ -o gguf_outputs/model-imatrix.dat \ -ngl 32 \ --verbose ``` **Note**: Adjust `-ngl` based on your GPU memory (0 for CPU-only) > **Note: Understanding Importance Matrix (imatrix)** > > The importance matrix is a calibration technique that identifies which model weights contribute most significantly to output quality. During quantization, weights deemed "important" by the matrix receive higher precision allocation, while less critical weights can be more aggressively compressed. This selective approach significantly improves quantized model quality compared to uniform quantization. > > The imatrix is generated by running representative text through the model and measuring activation patterns. While general text datasets (like WikiText) work well for most models, using domain-specific calibration data (e.g., translation examples for the Sunflower model) can provide marginal quality improvements. The process adds 30-60 minutes to quantization time but is highly recommended for production models, especially when using aggressive quantizations like Q4\_K\_M and below. ### 2. Standard Quantizations Create quantized models with different quality/size trade-offs: ```bash theme={null} # Q8_0: Near-lossless quality (~15GB for 14B model) ./llama.cpp/build/bin/llama-quantize \ --imatrix gguf_outputs/model-imatrix.dat \ gguf_outputs/model-merged-f16.gguf \ gguf_outputs/model-q8_0.gguf \ Q8_0 # Q6_K: High quality (~12GB for 14B model) ./llama.cpp/build/bin/llama-quantize \ --imatrix gguf_outputs/model-imatrix.dat \ gguf_outputs/model-merged-f16.gguf \ gguf_outputs/model-q6_k.gguf \ Q6_K # Q5_K_M: Balanced quality/size (~10GB for 14B model) ./llama.cpp/build/bin/llama-quantize \ --imatrix gguf_outputs/model-imatrix.dat \ gguf_outputs/model-merged-f16.gguf \ gguf_outputs/model-q5_k_m.gguf \ Q5_K_M # Q4_K_M: Recommended for most users (~8GB for 14B model) ./llama.cpp/build/bin/llama-quantize \ --imatrix gguf_outputs/model-imatrix.dat \ gguf_outputs/model-merged-f16.gguf \ gguf_outputs/model-q4_k_m.gguf \ Q4_K_M ``` ### 3. Quantization Options Reference | Quantization | Bits per Weight | Quality | Use Case | | ------------ | --------------- | ---------- | ---------------------------- | | Q8\_0 | \~8.0 | Highest | Production, quality critical | | Q6\_K | \~6.6 | High | Production, balanced | | Q5\_K\_M | \~5.5 | Good | Most users | | Q4\_K\_M | \~4.3 | Acceptable | Resource constrained | ## Experimental Quantizations **Warning**: These quantizations achieve extreme compression but may significantly impact model quality. ### Ultra-Low Bit Quantizations ```bash theme={null} # IQ2_XXS: Extreme compression (~4GB for 14B model) ./llama.cpp/build/bin/llama-quantize \ --imatrix gguf_outputs/model-imatrix.dat \ gguf_outputs/model-merged-f16.gguf \ gguf_outputs/model-iq2_xxs.gguf \ IQ2_XXS # TQ1_0: Ternary quantization (~3.7GB for 14B model) ./llama.cpp/build/bin/llama-quantize \ --imatrix gguf_outputs/model-imatrix.dat \ gguf_outputs/model-merged-f16.gguf \ gguf_outputs/model-tq1_0.gguf \ TQ1_0 # IQ1_S: Maximum compression (~3.4GB for 14B model) ./llama.cpp/build/bin/llama-quantize \ --imatrix gguf_outputs/model-imatrix.dat \ gguf_outputs/model-merged-f16.gguf \ gguf_outputs/model-iq1_s.gguf \ IQ1_S ``` ### Experimental Quantization Reference | Quantization | Bits per Weight | Compression | Warning Level | | ------------ | --------------- | ----------- | --------------------- | | IQ2\_XXS | 2.06 | 85% smaller | Moderate quality loss | | TQ1\_0 | 1.69 | 87% smaller | High quality loss | | IQ1\_S | 1.56 | 88% smaller | Severe quality loss | ## Quality Testing ### 1. Quick Functionality Test ```bash theme={null} # Test standard quantization ./llama.cpp/build/bin/llama-cli \ -m gguf_outputs/model-q4_k_m.gguf \ -p "Your test prompt here" \ -n 100 \ --verbose # Test experimental quantization ./llama.cpp/build/bin/llama-cli \ -m gguf_outputs/model-iq1_s.gguf \ -p "Your test prompt here" \ -n 100 \ --verbose ``` ### 2. Perplexity Evaluation ```bash theme={null} # Download evaluation dataset wget https://s3.amazonaws.com/research.metamind.io/wikitext/wikitext-2-raw-v1.zip unzip wikitext-2-raw-v1.zip # Test perplexity (lower is better) ./llama.cpp/build/bin/llama-perplexity \ -m gguf_outputs/model-q4_k_m.gguf \ -f wikitext-2-raw/wiki.test.raw \ -ngl 32 ``` ### 3. Size Verification ```bash theme={null} # Check all quantization sizes ls -lh gguf_outputs/*.gguf ``` **Expected output** (14B model): ``` 28G model-merged-f16.gguf 15G model-q8_0.gguf 12G model-q6_k.gguf 10G model-q5_k_m.gguf 8.4G model-q4_k_m.gguf 4.1G model-iq2_xxs.gguf 3.7G model-tq1_0.gguf 3.4G model-iq1_s.gguf ``` ## Ollama Integration Ollama provides an easy way to run your quantized models locally with a simple API interface. ### Installation and Setup ```bash theme={null} # Install Ollama (Linux/macOS) curl -fsSL https://ollama.ai/install.sh | sh # Or download from https://ollama.ai for Windows # Start Ollama service (runs in background) ollama serve ``` ### Creating Modelfiles for Different Quantizations **Q4\_K\_M (Recommended) - Modelfile:** ```bash theme={null} cat > Modelfile.q4 << 'EOF' FROM ./gguf_outputs/model-q4_k_m.gguf # System prompt for your specific use case SYSTEM """You are a linguist and translator specializing in Ugandan languages, made by Sunbird AI.""" # Chat template (adjust for your base model architecture) TEMPLATE """<|im_start|>system {{ .System }}<|im_end|> <|im_start|>user {{ .Prompt }}<|im_end|> <|im_start|>assistant {{ .Response }}<|im_end|>""" # Stop tokens PARAMETER stop "<|im_start|>" PARAMETER stop "<|im_end|>" # Generation parameters PARAMETER temperature 0.3 PARAMETER top_p 0.95 PARAMETER top_k 40 PARAMETER repeat_penalty 1.1 PARAMETER num_ctx 4096 PARAMETER num_predict 500 EOF ``` **Experimental IQ1\_S - Modelfile:** ```bash theme={null} cat > Modelfile.iq1s << 'EOF' FROM ./gguf_outputs/model-iq1_s.gguf SYSTEM """You are a translator for Ugandan languages. Note: This is an experimental ultra-compressed model - quality may be limited.""" # Same template and parameters as above TEMPLATE """<|im_start|>system {{ .System }}<|im_end|> <|im_start|>user {{ .Prompt }}<|im_end|> <|im_start|>assistant {{ .Response }}<|im_end|>""" PARAMETER stop "<|im_start|>" PARAMETER stop "<|im_end|>" PARAMETER temperature 0.3 PARAMETER top_p 0.95 PARAMETER num_ctx 2048 # Smaller context for experimental model EOF ``` ### Importing Models to Ollama ```bash theme={null} # Import Q4_K_M model (recommended) ollama create sunflower-14b:q4 -f Modelfile.q4 # Import experimental IQ1_S model ollama create sunflower-14b:iq1s -f Modelfile.iq1s # Import other quantizations ollama create sunflower-14b:q5 -f Modelfile.q5 ollama create sunflower-14b:q6 -f Modelfile.q6 # Verify models are imported ollama list ``` **Expected output:** ``` NAME ID SIZE MODIFIED sunflower-14b:q4 abc123def 8.4GB 2 minutes ago sunflower-14b:iq1s def456ghi 3.4GB 1 minute ago ``` ### Using Ollama Models **Interactive Chat:** ```bash theme={null} # Start interactive session with Q4 model ollama run sunflower-14b:q4 # Example conversation: # >>> Translate to Luganda: Hello, how are you today? # >>> Give a dictionary definition of the Samia term "ovulwaye" in English # >>> /bye (to exit) # Start with experimental model ollama run sunflower-14b:iq1s ``` **Single Prompt Inference:** ```bash theme={null} # Quick translation with Q4 model ollama run sunflower-14b:q4 "Translate to Luganda: People in villages rarely accept new technologies." # Test experimental model ollama run sunflower-14b:iq1s "Translate to Luganda: Good morning" # Dictionary definition ollama run sunflower-14b:q4 'Give a dictionary definition of the Samia term "ovulwaye" in English' ``` ### Ollama API Usage **Start API Server:** ```bash theme={null} # Ollama automatically serves API on http://localhost:11434 # Test API endpoint curl http://localhost:11434/api/version ``` **Python API Client:** ```python theme={null} import requests import json def translate_with_ollama(text, target_lang="Luganda", model="sunflower-14b:q4"): url = "http://localhost:11434/api/generate" payload = { "model": model, "prompt": f"Translate to {target_lang}: {text}", "stream": False } response = requests.post(url, json=payload) return response.json()["response"] # Test translation result = translate_with_ollama("Hello, how are you?") print(result) # Test experimental model result_experimental = translate_with_ollama( "Good morning", model="sunflower-14b:iq1s" ) print("Experimental model:", result_experimental) ``` **curl API Examples:** ```bash theme={null} # Basic translation curl -X POST http://localhost:11434/api/generate \ -H "Content-Type: application/json" \ -d '{ "model": "sunflower-14b:q4", "prompt": "Translate to Luganda: How are you today?", "stream": false }' # Streaming response curl -X POST http://localhost:11434/api/generate \ -H "Content-Type: application/json" \ -d '{ "model": "sunflower-14b:q4", "prompt": "Translate to Luganda: People in villages rarely accept new technologies.", "stream": true }' ``` ### Model Management ```bash theme={null} # List all models ollama list # Show model details ollama show sunflower-14b:q4 # Remove a model ollama rm sunflower-14b:iq1s # Copy model with new name ollama cp sunflower-14b:q4 sunflower-translator # Pull/push to Ollama registry (if you publish there) # ollama push sunflower-14b:q4 ``` ### Performance Comparison Script Create `test_models.py`: ```python theme={null} import time import requests models = ["sunflower-14b:q4", "sunflower-14b:iq1s"] test_prompt = "Translate to Luganda: Hello, how are you today?" def test_model(model_name, prompt): start_time = time.time() response = requests.post("http://localhost:11434/api/generate", json={ "model": model_name, "prompt": prompt, "stream": False }) end_time = time.time() result = response.json() return { "model": model_name, "response": result["response"], "time": end_time - start_time, "tokens": len(result["response"].split()) } # Test all models for model in models: result = test_model(model, test_prompt) print(f"Model: {result['model']}") print(f"Response: {result['response']}") print(f"Time: {result['time']:.2f}s") print(f"Tokens: {result['tokens']}") print("-" * 50) ``` ### Production Deployment ```bash theme={null} # Create production Modelfile with optimized settings cat > Modelfile.production << 'EOF' FROM ./gguf_outputs/model-q4_k_m.gguf SYSTEM """You are a professional translator for Ugandan languages, made by Sunbird AI. Provide accurate, contextually appropriate translations.""" TEMPLATE """<|im_start|>system {{ .System }}<|im_end|> <|im_start|>user {{ .Prompt }}<|im_end|> <|im_start|>assistant {{ .Response }}<|im_end|>""" PARAMETER stop "<|im_start|>" PARAMETER stop "<|im_end|>" # Production-optimized parameters PARAMETER temperature 0.1 # Lower for consistency PARAMETER top_p 0.9 # Slightly more focused PARAMETER repeat_penalty 1.05 # Minimal repetition penalty PARAMETER num_ctx 4096 # Full context PARAMETER num_predict 200 # Reasonable response length EOF # Create production model ollama create sunflower-translator:production -f Modelfile.production ``` ## Distribution ### 1. Hugging Face Upload Create upload script `upload_models.py`: ```python theme={null} #!/usr/bin/env python3 from huggingface_hub import HfApi, login, create_repo def upload_gguf_models(): repo_id = "your-org/your-model-GGUF" # Update this local_folder = "gguf_outputs" # Login and create repository login() api = HfApi() create_repo(repo_id, exist_ok=True, repo_type="model") # Upload all files api.upload_folder( folder_path=local_folder, repo_id=repo_id, allow_patterns=["*.gguf", "*.dat"], commit_message="Add GGUF quantized models" ) print(f"Upload complete: https://huggingface.co/{repo_id}") if __name__ == "__main__": upload_gguf_models() ``` ```bash theme={null} # Login to Hugging Face huggingface-cli login # Run upload python upload_models.py ``` ### 2. Ollama Integration (Complete Guide) #### Installation and Setup ```bash theme={null} # Install Ollama (Linux/macOS) curl -fsSL https://ollama.ai/install.sh | sh # Or download from https://ollama.ai for Windows # Start Ollama service (runs in background) ollama serve ``` #### Creating Modelfiles for Different Quantizations **Q4\_K\_M (Recommended) - Modelfile:** ```bash theme={null} cat > Modelfile.q4 << 'EOF' FROM ./gguf_outputs/model-q4_k_m.gguf # System prompt for your specific use case SYSTEM """You are a linguist and translator specializing in Ugandan languages, made by Sunbird AI.""" # Chat template (adjust for your base model architecture) TEMPLATE """<|im_start|>system {{ .System }}<|im_end|> <|im_start|>user {{ .Prompt }}<|im_end|> <|im_start|>assistant {{ .Response }}<|im_end|>""" # Stop tokens PARAMETER stop "<|im_start|>" PARAMETER stop "<|im_end|>" # Generation parameters PARAMETER temperature 0.3 PARAMETER top_p 0.95 PARAMETER top_k 40 PARAMETER repeat_penalty 1.1 PARAMETER num_ctx 4096 PARAMETER num_predict 500 EOF ``` **Experimental IQ1\_S - Modelfile:** ```bash theme={null} cat > Modelfile.iq1s << 'EOF' FROM ./gguf_outputs/model-iq1_s.gguf SYSTEM """You are a translator for Ugandan languages. Note: This is an experimental ultra-compressed model - quality may be limited.""" # Same template and parameters as above TEMPLATE """<|im_start|>system {{ .System }}<|im_end|> <|im_start|>user {{ .Prompt }}<|im_end|> <|im_start|>assistant {{ .Response }}<|im_end|>""" PARAMETER stop "<|im_start|>" PARAMETER stop "<|im_end|>" PARAMETER temperature 0.3 PARAMETER top_p 0.95 PARAMETER num_ctx 2048 # Smaller context for experimental model EOF ``` #### Importing Models to Ollama ```bash theme={null} # Import Q4_K_M model (recommended) ollama create sunflower-14b:q4 -f Modelfile.q4 # Import experimental IQ1_S model ollama create sunflower-14b:iq1s -f Modelfile.iq1s # Import other quantizations ollama create sunflower-14b:q5 -f Modelfile.q5 ollama create sunflower-14b:q6 -f Modelfile.q6 # Verify models are imported ollama list ``` **Expected output:** ``` NAME ID SIZE MODIFIED sunflower-14b:q4 abc123def 8.4GB 2 minutes ago sunflower-14b:iq1s def456ghi 3.4GB 1 minute ago ``` #### Using Ollama Models **Interactive Chat:** ```bash theme={null} # Start interactive session with Q4 model ollama run sunflower-14b:q4 # Example conversation: # >>> Translate to Luganda: Hello, how are you today? # >>> Give a dictionary definition of the Samia term "ovulwaye" in English # >>> /bye (to exit) # Start with experimental model ollama run sunflower-14b:iq1s ``` **Single Prompt Inference:** ```bash theme={null} # Quick translation with Q4 model ollama run sunflower-14b:q4 "Translate to Luganda: People in villages rarely accept new technologies." # Test experimental model ollama run sunflower-14b:iq1s "Translate to Luganda: Good morning" # Dictionary definition ollama run sunflower-14b:q4 'Give a dictionary definition of the Samia term "ovulwaye" in English' ``` #### Ollama API Usage **Start API Server:** ```bash theme={null} # Ollama automatically serves API on http://localhost:11434 # Test API endpoint curl http://localhost:11434/api/version ``` **Python API Client:** ```python theme={null} import requests import json def translate_with_ollama(text, target_lang="Luganda", model="sunflower-14b:q4"): url = "http://localhost:11434/api/generate" payload = { "model": model, "prompt": f"Translate to {target_lang}: {text}", "stream": False } response = requests.post(url, json=payload) return response.json()["response"] # Test translation result = translate_with_ollama("Hello, how are you?") print(result) # Test experimental model result_experimental = translate_with_ollama( "Good morning", model="sunflower-14b:iq1s" ) print("Experimental model:", result_experimental) ``` **curl API Examples:** ```bash theme={null} # Basic translation curl -X POST http://localhost:11434/api/generate \ -H "Content-Type: application/json" \ -d '{ "model": "sunflower-14b:q4", "prompt": "Translate to Luganda: How are you today?", "stream": false }' # Streaming response curl -X POST http://localhost:11434/api/generate \ -H "Content-Type: application/json" \ -d '{ "model": "sunflower-14b:q4", "prompt": "Translate to Luganda: People in villages rarely accept new technologies.", "stream": true }' ``` #### Model Management ```bash theme={null} # List all models ollama list # Show model details ollama show sunflower-14b:q4 # Remove a model ollama rm sunflower-14b:iq1s # Copy model with new name ollama cp sunflower-14b:q4 sunflower-translator # Pull/push to Ollama registry (if you publish there) # ollama push sunflower-14b:q4 ``` #### Performance Comparison Script Create `test_models.py`: ```python theme={null} import time import requests models = ["sunflower-14b:q4", "sunflower-14b:iq1s"] test_prompt = "Translate to Luganda: Hello, how are you today?" def test_model(model_name, prompt): start_time = time.time() response = requests.post("http://localhost:11434/api/generate", json={ "model": model_name, "prompt": prompt, "stream": False }) end_time = time.time() result = response.json() return { "model": model_name, "response": result["response"], "time": end_time - start_time, "tokens": len(result["response"].split()) } # Test all models for model in models: result = test_model(model, test_prompt) print(f"Model: {result['model']}") print(f"Response: {result['response']}") print(f"Time: {result['time']:.2f}s") print(f"Tokens: {result['tokens']}") print("-" * 50) ``` #### Troubleshooting Ollama **Common Issues:** 1. **Model fails to load:** ```bash theme={null} # Check model file exists ls -la gguf_outputs/model-q4_k_m.gguf # Verify Modelfile syntax ollama create sunflower-14b:q4 -f Modelfile.q4 --verbose ``` 2. **Out of memory:** ```bash theme={null} # Use smaller quantization ollama run sunflower-14b:iq1s # Uses only 3.4GB # Or reduce context size in Modelfile PARAMETER num_ctx 2048 ``` 3. **Poor quality with experimental models:** ```bash theme={null} # Compare outputs ollama run sunflower-14b:q4 "Your test prompt" ollama run sunflower-14b:iq1s "Your test prompt" # Expected: IQ1_S may have degraded quality ``` 4. **Ollama service not running:** ```bash theme={null} # Start service ollama serve # Or check if running ps aux | grep ollama ``` #### Production Deployment ```bash theme={null} # Create production Modelfile with optimized settings cat > Modelfile.production << 'EOF' FROM ./gguf_outputs/model-q4_k_m.gguf SYSTEM """You are a professional translator for Ugandan languages, made by Sunbird AI. Provide accurate, contextually appropriate translations.""" TEMPLATE """<|im_start|>system {{ .System }}<|im_end|> <|im_start|>user {{ .Prompt }}<|im_end|> <|im_start|>assistant {{ .Response }}<|im_end|>""" PARAMETER stop "<|im_start|>" PARAMETER stop "<|im_end|>" # Production-optimized parameters PARAMETER temperature 0.1 # Lower for consistency PARAMETER top_p 0.9 # Slightly more focused PARAMETER repeat_penalty 1.05 # Minimal repetition penalty PARAMETER num_ctx 4096 # Full context PARAMETER num_predict 200 # Reasonable response length EOF # Create production model ollama create sunflower-translator:production -f Modelfile.production ``` ## Troubleshooting ### Common Issues **1. Out of Memory During Merge** ```bash theme={null} # Use smaller precision or CPU offloading export PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:512 ``` **2. GGUF Conversion Fails** ```bash theme={null} # Check model architecture compatibility python3 llama.cpp/convert_hf_to_gguf.py models/merged_model --outfile test.gguf --dry-run ``` **3. Quantization Too Slow** ```bash theme={null} # Use fewer threads or disable imatrix for testing ./llama.cpp/build/bin/llama-quantize \ gguf_outputs/model-merged-f16.gguf \ gguf_outputs/model-q4_k_m.gguf \ Q4_K_M \ 4 # Use 4 threads ``` **4. Experimental Quantizations Unusable** * This is expected for extreme quantizations like IQ1\_S * Test with your specific use case * Consider using IQ2\_XXS as minimum viable quantization ### File Size Expectations For a 14B parameter model: * **Merge process**: Requires 2x model size in RAM (\~56GB peak) * **F16 GGUF**: \~28GB final size * **Quantized models**: 3GB-15GB depending on level * **Total storage needed**: \~200GB for all quantizations ### Performance Notes * **Importance matrix generation**: 30-60 minutes on modern hardware * **Each quantization**: 5-10 minutes per model * **Upload time**: Varies by connection, large files use Git LFS * **Memory usage**: Peaks during merge, lower during quantization ### Ollama-Specific Issues **1. Model fails to load:** ```bash theme={null} # Check model file exists ls -la gguf_outputs/model-q4_k_m.gguf # Verify Modelfile syntax ollama create sunflower-14b:q4 -f Modelfile.q4 --verbose ``` **2. Out of memory with Ollama:** ```bash theme={null} # Use smaller quantization ollama run sunflower-14b:iq1s # Uses only 3.4GB # Or reduce context size in Modelfile PARAMETER num_ctx 2048 ``` **3. Poor quality with experimental models:** ```bash theme={null} # Compare outputs ollama run sunflower-14b:q4 "Your test prompt" ollama run sunflower-14b:iq1s "Your test prompt" # Expected: IQ1_S may have degraded quality ``` **4. Ollama service not running:** ```bash theme={null} # Start service ollama serve # Or check if running ps aux | grep ollama ``` **5. API connection issues:** ```bash theme={null} # Test API availability curl http://localhost:11434/api/version # Check if port is blocked netstat -tlnp | grep 11434 ``` ## Conclusion This tutorial demonstrates the complete pipeline for converting LoRA fine-tuned models to GGUF format with multiple quantization levels. The process enables deployment of large models on resource-constrained hardware while maintaining various quality/size trade-offs. The experimental ultra-low quantizations (IQ1\_S, IQ2\_XXS, TQ1\_0) push the boundaries of model compression and should be used with appropriate quality expectations. For production use, Q4\_K\_M provides the best balance of quality and size, while Q5\_K\_M and Q6\_K offer better quality at larger sizes. Always evaluate quantized models on your specific tasks before deployment. ## References * [llama.cpp Repository](https://github.com/ggml-org/llama.cpp) * [GGUF Format Specification](https://github.com/ggerganov/ggml/blob/master/docs/gguf.md) * [PEFT Library Documentation](https://huggingface.co/docs/peft) * [Hugging Face Hub Documentation](https://huggingface.co/docs/hub) # Getting Started Source: https://docs.sunbird.ai/sunflower/sunflower # Sunflower API Documentation **Version:** 1.0\ **Date:** September 2025\ **Developed by:** Sunbird AI *** ## Table of Contents 1. [Introduction](#introduction) 2. [Authentication](#authentication) 3. [Rate Limiting](#rate-limiting) 4. [API Endpoints](#api-endpoints) 5. [Error Handling](#error-handling) 6. [Code Examples](#code-examples) 7. [Best Practices](#best-practices) 8. [Troubleshooting](#troubleshooting) *** ## Introduction The Sunflower API provides access to Sunbird AI's multilingual language model, specifically designed for Ugandan languages. The API specializes in: * **Multilingual conversations** in Ugandan languages (Luganda, Acholi, Ateso, etc.) * **Cross-lingual translations** and explanations * **Cultural context understanding** * **Educational content** in local languages ### Key Features * Automatic retry with exponential backoff * Context-aware responses * Usage tracking and monitoring * Support for custom system messages * Message history management * Professional error handling ### Model Information * **Primary Model:** Sunbird/Sunflower-14B-FP8 * **Supported Languages:** Luganda, Acholi, Ateso, English, and other Ugandan languages * **Model Types:** Qwen *** ## Authentication All API requests require authentication using a valid API key. Include your API key in the request headers: ```http theme={null} Authorization: Bearer YOUR_API_KEY ``` ### Obtaining API Keys 1. If you don't already have an account, create one at [https://api.sunbird.ai/register](https://api.sunbird.ai/register) and login. 2. Go to the [tokens page](https://api.sunbird.ai/tokens) to get your access token which you'll use to authenticate *** ## Rate Limiting The API implements rate limiting based on account types: * **Free Tier:** Limited requests per hour * **Professional:** Higher rate limits * **Enterprise:** Custom rate limits *** ## API Endpoints ### 1. Chat Completions Endpoint **Endpoint:** `POST tasks/sunflower_inference` Professional endpoint for multilingual chat completions with full conversation management. #### Request Format ```json theme={null} { "messages": [ { "role": "system|user|assistant", "content": "Message content" } ], "model_type": "qwen", "temperature": 0.3, "stream": false, "system_message": "Optional custom system message" } ``` #### Parameters | Parameter | Type | Required | Default | Description | | ---------------- | ------- | -------- | ------- | ------------------------------ | | `messages` | Array | Yes | - | List of conversation messages | | `model_type` | String | No | "qwen" | Model type: "qwen" | | `temperature` | Float | No | 0.3 | Sampling temperature (0.0-2.0) | | `stream` | Boolean | No | false | Whether to stream the response | | `system_message` | String | No | null | Custom system message | #### Message Object | Field | Type | Required | Description | | --------- | ------ | -------- | ---------------------------------------------- | | `role` | String | Yes | Message role: "system", "user", or "assistant" | | `content` | String | Yes | Message content (cannot be empty) | #### Response Format ```json theme={null} { "content": "AI response content", "model_type": "qwen", "usage": { "completion_tokens": 150, "prompt_tokens": 100, "total_tokens": 250 }, "processing_time": 2.35, "inference_time": 1.85, "message_count": 3 } ``` #### Response Fields | Field | Type | Description | | ----------------- | ------- | -------------------------------- | | `content` | String | The AI's response | | `model_type` | String | Model used for inference | | `usage` | Object | Token usage statistics | | `processing_time` | Float | Total processing time in seconds | | `inference_time` | Float | Model inference time in seconds | | `message_count` | Integer | Number of messages processed | ### 2. Simple Inference Endpoint **Endpoint:** `POST /tasks/sunflower_simple` Simplified interface for single instruction/response interactions. #### Request Format (Form Data) ```json theme={null} { "instruction": "Translate 'Good morning' to Luganda", "model_type": "qwen", "temperature": 0.3, "system_message": "You are Sunflower, a multilingual assistant for Ugandan languages made by Sunbird AI." } ``` | Parameter | Type | Required | Default | Description | | ---------------- | ------ | -------- | ------- | ------------------------------ | | `instruction` | String | Yes | - | The instruction or question | | `model_type` | String | No | "qwen" | Model type: "qwen" | | `temperature` | Float | No | 0.3 | Sampling temperature (0.0-2.0) | | `system_message` | String | No | null | Custom system message | #### Response Format ```json theme={null} { "response": "AI response content", "model_type": "qwen", "processing_time": 1.85, "usage": { "completion_tokens": 120, "prompt_tokens": 80, "total_tokens": 200 }, "success": true } ``` *** ## Error Handling The API uses standard HTTP status codes and provides detailed error messages. ### HTTP Status Codes | Code | Description | | ---- | --------------------------------------- | | 200 | Success | | 400 | Bad Request - Invalid input | | 401 | Unauthorized - Invalid API key | | 429 | Too Many Requests - Rate limit exceeded | | 500 | Internal Server Error | | 502 | Bad Gateway - Empty model response | | 503 | Service Unavailable - Model loading | | 504 | Gateway Timeout - Request timeout | ### Error Response Format ```json theme={null} { "detail": "Error description", "status_code": 400, "timestamp": "2025-09-26T10:30:00Z" } ``` ### Common Error Messages #### Model Loading (503) ```json theme={null} { "detail": "The AI model is currently loading. This usually takes 2-3 minutes. Please try again shortly." } ``` #### Timeout (504) ```json theme={null} { "detail": "The request timed out. Please try again with a shorter prompt or check your network connection." } ``` #### Invalid Request (400) ```json theme={null} { "detail": "Message 0 content cannot be empty" } ``` *** ## Code Examples ### Python Examples #### 1. Basic Chat Completion ```python theme={null} import requests import json def chat_with_sunflower(messages, api_key, base_url="https://api.sunbird.ai"): """ Send a chat completion request to Sunflower API """ url = f"{base_url}/tasks/sunflower_inference" headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } payload = { "messages": messages, "model_type": "qwen", "temperature": 0.3, "stream": False } try: response = requests.post(url, headers=headers, json=payload, timeout=30) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: print(f"Request failed: {e}") return None # Example usage api_key = "your_api_key_here" messages = [ { "role": "system", "content": "You are Sunflower, a multilingual assistant for Ugandan languages made by Sunbird AI." }, { "role": "user", "content": "Translate 'Good morning' to Luganda" } ] result = chat_with_sunflower(messages, api_key) if result: print(f"Response: {result['content']}") print(f"Tokens used: {result['usage']['total_tokens']}") ``` #### 2. Simple Inference ```python theme={null} import requests def simple_inference(instruction, api_key, base_url="https://api.sunbird.ai"): """ Send a simple inference request to Sunflower API """ url = f"{base_url}/tasks/sunflower_simple" headers = { "Authorization": f"Bearer {api_key}" } data = { "instruction": instruction, "model_type": "qwen", "temperature": 0.3 } try: response = requests.post(url, headers=headers, data=data, timeout=30) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: print(f"Request failed: {e}") return None # Example usage api_key = "your_api_key_here" instruction = "Explain the meaning of 'Ubuntu' in Ugandan context" result = simple_inference(instruction, api_key) if result: print(f"Response: {result['response']}") ``` #### 3. Conversation Management ```python theme={null} class SunflowerConversation: def __init__(self, api_key, base_url="https://api.sunbird.ai"): self.api_key = api_key self.base_url = base_url self.messages = [] def set_system_message(self, message): """Set or update the system message""" # Remove existing system message self.messages = [msg for msg in self.messages if msg["role"] != "system"] # Add new system message at the beginning self.messages.insert(0, {"role": "system", "content": message}) def add_user_message(self, content): """Add a user message to the conversation""" self.messages.append({"role": "user", "content": content}) def get_response(self, model_type="qwen", temperature=0.3): """Get AI response and add it to conversation""" url = f"{self.base_url}/tasks/sunflower_inference" headers = { "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json" } payload = { "messages": self.messages, "model_type": model_type, "temperature": temperature } try: response = requests.post(url, headers=headers, json=payload, timeout=30) response.raise_for_status() result = response.json() # Add assistant response to conversation self.messages.append({ "role": "assistant", "content": result["content"] }) return result except requests.exceptions.RequestException as e: print(f"Request failed: {e}") return None def clear_conversation(self): """Clear conversation history but keep system message""" system_messages = [msg for msg in self.messages if msg["role"] == "system"] self.messages = system_messages # Example usage conversation = SunflowerConversation("your_api_key_here") # Set system message conversation.set_system_message( "You are Sunflower, a helpful assistant specializing in Ugandan languages and culture." ) # Have a conversation conversation.add_user_message("Hello, can you greet me in Luganda?") response1 = conversation.get_response() print(f"AI: {response1['content']}") conversation.add_user_message("How do I say 'thank you' in Acholi?") response2 = conversation.get_response() print(f"AI: {response2['content']}") ``` #### 4. Error Handling with Retry Logic ```python theme={null} import time import random from typing import Optional def sunflower_request_with_retry( messages, api_key, max_retries=3, base_delay=2.0, base_url="https://api.sunbird.ai" ) -> Optional[dict]: """ Make a request to Sunflower API with exponential backoff retry """ url = f"{base_url}/tasks/sunflower_inference" headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } payload = { "messages": messages, "model_type": "qwen", "temperature": 0.3 } for attempt in range(max_retries + 1): try: response = requests.post(url, headers=headers, json=payload, timeout=30) if response.status_code == 200: return response.json() elif response.status_code == 503: # Model loading - retry with longer delay if attempt < max_retries: delay = base_delay * (2 ** attempt) + random.uniform(0, 1) print(f"Model loading, retrying in {delay:.1f} seconds...") time.sleep(delay) continue elif response.status_code == 429: # Rate limited - retry with exponential backoff if attempt < max_retries: delay = base_delay * (2 ** attempt) print(f"Rate limited, retrying in {delay:.1f} seconds...") time.sleep(delay) continue else: print(f"Request failed with status {response.status_code}: {response.text}") return None except requests.exceptions.Timeout: if attempt < max_retries: delay = base_delay * (2 ** attempt) print(f"Request timed out, retrying in {delay:.1f} seconds...") time.sleep(delay) continue else: print("Request timed out after all retries") return None except requests.exceptions.RequestException as e: print(f"Request failed: {e}") return None print("All retry attempts exhausted") return None # Example usage messages = [ {"role": "user", "content": "What is the capital of Uganda?"} ] result = sunflower_request_with_retry(messages, "your_api_key_here") if result: print(f"Response: {result['content']}") ``` ### JavaScript/Node.js Examples #### 1. Basic Chat Completion ```javascript theme={null} const axios = require('axios'); async function chatWithSunflower(messages, apiKey, baseUrl = 'https://api.sunbird.ai') { const url = `${baseUrl}/tasks/sunflower_inference`; const headers = { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }; const payload = { messages: messages, model_type: 'qwen', temperature: 0.3, stream: false }; try { const response = await axios.post(url, payload, { headers: headers, timeout: 30000 }); return response.data; } catch (error) { console.error('Request failed:', error.message); if (error.response) { console.error('Response data:', error.response.data); } return null; } } // Example usage const apiKey = 'your_api_key_here'; const messages = [ { role: 'system', content: 'You are Sunflower, a multilingual assistant for Ugandan languages.' }, { role: 'user', content: 'How do you say "welcome" in Luganda?' } ]; chatWithSunflower(messages, apiKey) .then(result => { if (result) { console.log('Response:', result.content); console.log('Tokens used:', result.usage.total_tokens); } }); ``` #### 2. Simple Inference ```javascript theme={null} const FormData = require('form-data'); const axios = require('axios'); async function simpleInference(instruction, apiKey, baseUrl = 'https://api.sunbird.ai') { const url = `${baseUrl}/tasks/sunflower_simple`; const formData = new FormData(); formData.append('instruction', instruction); formData.append('model_type', 'qwen'); formData.append('temperature', '0.3'); const headers = { 'Authorization': `Bearer ${apiKey}`, ...formData.getHeaders() }; try { const response = await axios.post(url, formData, { headers: headers, timeout: 30000 }); return response.data; } catch (error) { console.error('Request failed:', error.message); return null; } } // Example usage const apiKey = 'your_api_key_here'; const instruction = 'Translate "How are you?" to Ateso'; simpleInference(instruction, apiKey) .then(result => { if (result) { console.log('Response:', result.response); } }); ``` ### cURL Examples #### 1. Chat Completion ```bash theme={null} curl -X POST "https://api.sunbird.ai/tasks/sunflower_inference" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "role": "system", "content": "You are Sunflower, a multilingual assistant for Ugandan languages made by Sunbird AI." }, { "role": "user", "content": "Translate \"Good evening\" to Luganda" } ], "model_type": "qwen", "temperature": 0.3 }' ``` #### 2. Simple Inference ```bash theme={null} curl -X POST "https://api.sunbird.ai/tasks/sunflower_simple" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "instruction=What are some traditional Ugandan foods?" \ -F "model_type=qwen" \ -F "temperature=0.3" ``` *** ## Best Practices ### 1. Message Management * **Always include a system message** to provide context and improve response quality * **Keep conversation history relevant** - trim old messages to stay within token limits * **Use clear, specific prompts** for better results ### 2. Error Handling * **Implement retry logic** for transient errors (503, 504, 429) * **Handle model loading delays** - allow 2-3 minutes for cold starts * **Validate input** before sending requests ### 3. Performance Optimization * **Use appropriate temperature settings**: * 0.0-0.3: More deterministic, good for translations * 0.4-0.7: Balanced creativity * 0.8-1.0: More creative responses * **Consider using simple endpoint** for single-turn interactions * **Implement caching** for repeated requests ### 4. Rate Limiting * **Monitor rate limit headers** in responses * **Implement exponential backoff** for rate limit exceeded errors * **Consider request batching** where appropriate ### 5. Security * **Never expose API keys** in client-side code * **Use environment variables** to store credentials * **Implement proper authentication** in your applications *** ## Troubleshooting ### Common Issues and Solutions #### 1. Model Loading Errors (503) **Problem:** "The AI model is currently loading" **Solutions:** * Wait 2-3 minutes before retrying * Implement exponential backoff retry logic * Use longer timeout values during cold starts #### 2. Empty Responses (502) **Problem:** "The model returned an empty response" **Solutions:** * Rephrase your request to be more specific * Check if the input contains inappropriate content * Try a different temperature setting #### 3. Timeout Errors (504) **Problem:** Request times out **Solutions:** * Reduce prompt length * Use simpler queries * Increase timeout values in your code * Check network connectivity #### 4. Rate Limiting (429) **Problem:** Too many requests **Solutions:** * Implement request queuing * Use exponential backoff * Consider upgrading your account tier #### 5. Invalid Message Format (400) **Problem:** Message validation errors **Solutions:** * Ensure all messages have 'role' and 'content' fields * Check that content is not empty * Validate role values ('system', 'user', 'assistant') ### Debugging Tips 1. **Enable detailed logging** to track request/response cycles 2. **Test with simple queries** first before complex conversations 3. **Use the simple endpoint** to isolate issues 4. **Check API status** at Sunbird AI's status page 5. **Monitor token usage** to avoid unexpected costs *** ## Support and Resources ### Getting Help * **Email Support:** [info@sunbird.ai](mailto:info@sunbird.ai) * **Documentation:** [https://salt.sunbird.ai/docs](https://salt.sunbird.ai/docs) *** **© 2025 Sunbird AI. All rights reserved.** *This documentation is subject to updates and improvements. Please check the official documentation website for the latest version.* # Contact Support Source: https://docs.sunbird.ai/support/contact Get in touch with the Sunbird AI team We are here to help! ## Channels * **Email**: [support@sunbird.ai](mailto:info@sunbird.ai) * **GitHub**: [SunbirdAI Issues](https://github.com/SunbirdAI/sunbird-ai-api/issues) * **X**: [@SunbirdAI](https:/x.com/SunbirdAI) ## Reporting Bugs When reporting a bug, please include: 1. The endpoint you are calling. 2. The request payload (redacted of sensitive info). 3. The error message or unexpected behavior. 4. The request ID (if available). # FAQ Source: https://docs.sunbird.ai/support/faq Frequently Asked Questions We offer a free tier for developers and researchers. For high-volume commercial use, please contact us. We support a wide range of African languages. Please refer to our [Supported Languages](/languages) page for the full list and their corresponding codes. No, API tokens do not expire. You can generate a token once and use it indefinitely for your integrations. We currently have soft limits on concurrent requests. If you require higher throughput for enterprise applications, please contact support. **SALT Library**: Yes, you can use the open-source `salt` package to fine-tune our models on your own datasets. **API**: The API serves pre-trained models and does not currently support custom fine-tuning via the endpoint. Files over 100MB should be uploaded via the signed URL workflow. See the [Speech to Text guide](/guides/speech-to-text) for details.