Goal: Generate a Python script to extract question text from a folder of images using external LLM providers (Groq and Sambanova), handle API rate limits by switching providers, and save the results to a single text file preserving the original image order.

Input:
1.  A folder containing approximately 850 images.
2.  Access to two LLM providers: Groq.com and Sambanova.ai.
3.  API keys for both providers (to be defined as constants within the code).

Output:
1.  A single Python script (`.py` file) implementing the specified logic.
2.  A single text file (`.txt` file) containing the extracted questions from all images, concatenated together.
3.  The content in the output text file must be in the same order as the images are listed when sorted alphabetically by filename. Each image's extracted text should be followed by a newline for separation.

Requirements & Constraints:

1.  **Image Processing Order:** Images from the input folder must be processed such that the output in the text file corresponds to the alphabetical order of the image filenames.
2.  **Concurrency:** The script should process multiple images concurrently using asynchronous programming (e.g., `asyncio`, `aiohttp`) to improve speed, but the results must be collected and written to the output file strictly in the correct order.
3.  **LLM Providers & Models:**
    *   **Primary (Groq):** Use the endpoint `https://api.groq.com/openai/v1/chat/completions`.
        *   Use the models: `"meta-llama/llama-4-scout-17b-16e-instruct"` and `"meta-llama/llama-4-maverick-17b-128e-instruct"`.
        *   The script should attempt requests using these Groq models first. Implement a strategy to try one Groq model, and if it specifically returns a rate-limit error, potentially try the other Groq model *before* switching to Sambanova for that *specific* request attempt.
    *   **Secondary (Sambanova):** Use the endpoint `https://api.sambanova.ai/v1/chat/completions`.
        *   Use the model: `"Llama-4-Maverick-17B-128E-Instruct"`.
        *   This provider is used as a fallback when Groq is rate-limited.
4.  **Rate Limit Handling:**
    *   If a Groq API call returns an error indicating "too many requests" or "quota exceeded" (parse the response/status code for this - typically HTTP 429), the script should switch to using the Sambanova API for the *same image request*.
    *   If the Sambanova request also fails (including rate limits), or if any other non-rate-limit error occurs with either provider for an image, the error should be logged, and the script should ideally attempt a retry for that specific image (e.g., 1-2 retries with exponential backoff) before skipping it and logging the failure.
    *   Consider adding a short delay (e.g., `asyncio.sleep`) between requests, especially to Groq, to help manage the rate limit, even with concurrent calls.
5.  **API Authentication:** API keys (`GROQ_API_KEY`, `SAMBANOVA_API_KEY`) must be defined as string constants directly within the Python script. **Note:** Hardcoding API keys is generally discouraged for security reasons, especially if the code is shared or version controlled. Consider alternatives like environment variables or configuration files in production scenarios.
6.  **Prompt Text:** The exact prompt text for both providers is `"Give only the questions as text, DO NOT CHANGE THEIR CONTENT"`. This must be sent as a text content block in the message payload.
7.  **Image Handling:**
    *   Read image files from the specified folder (e.g., support common types like JPG, PNG).
    *   Convert each image file's content into a Base64 data URL string (`data:image/jpeg;base64,...` or similar, dynamically determining the MIME type).
    *   Include the Base64 data URL in the `image_url.url` field of the message payload for both APIs.
8.  **API Request Format:** Adhere to the provided curl examples for the JSON payload structure, headers (`Content-Type: application/json`, `Authorization: Bearer <API_KEY>`), and HTTP method (`POST`). Set `stream: true` for both APIs. Set reasonable timeouts for API requests.
9.  **API Response Parsing:**
    *   Both APIs return Server-Sent Events (SSE) streams, with JSON chunks prefixed by `data: `. The script must parse these chunks correctly.
    *   Extract the content from the `delta.content` field within the `choices` array of each JSON chunk. Handle cases where `delta.content` might be null or missing.
    *   Concatenate the `delta.content` values from all relevant chunks for a single image request to reconstruct the complete response text.
    *   Handle the `data: [DONE]` marker (or `finish_reason: "stop"` in the last chunk) which signifies the end of a streaming response.
10. **Output File Writing:**
    *   Store the successfully extracted text for each image temporarily, maintaining the original processing order (based on sorted filenames).
    *   After processing all images (or processing completes for an image), write the extracted text to the output text file. Ensure the text for image `B.jpg` is written after the text for image `A.jpg` if they were processed concurrently but `A.jpg` comes first alphabetically. Append a newline character after the text block for each image.
11. **Progress & Error Reporting:**
    *   Log the processing start/end for each image (e.g., filename).
    *   Log which provider/model is being used for each attempt.
    *   Log success/failure status for each image.
    *   Log specific errors encountered (rate limits, HTTP errors, connection errors, parsing errors).
    *   Display overall progress (e.g., "Processed X of Y images"). Use `tqdm` or similar for a progress bar if desired.

Implementation Details:

*   Use `asyncio` for managing concurrency.
*   Use `aiohttp` for making asynchronous HTTP requests and handling streaming responses.
*   Use `os`, `glob` for file system operations.
*   Use `base64` for encoding image data.
*   Use `json` for parsing API response chunks.
*   Use `mimetypes` to determine image MIME types for the data URL.
*   Manage the order of results carefully when using concurrency (e.g., store results in a dictionary keyed by filename or index, then sort and write).
*   Implement robust error handling and retry logic (e.g., exponential backoff for retries).
*   Define API keys as constants within the script (e.g., `GROQ_API_KEY = "your_groq_key_here"`).

Provided API Information (Reference):

*   **Groq Endpoint:** `https://api.groq.com/openai/v1/chat/completions`
*   **Groq Request Body Example:**
    ```json
    {
      "messages": [
        {
          "role": "user",
          "content": [
            {
              "type": "text",
              "text": "Give only the questions as text, DO NOT CHANGE THEIR CONTENT"
            },
            {
              "type": "image_url",
              "image_url": {
                "url": "data:image/jpeg;base64,..."
              }
            }
          ]
        }
      ],
      "model": "<groq_model_name>",
      "temperature": 1,
      "max_completion_tokens": 1024,
      "top_p": 1,
      "stream": true,
      "stop": null
    }
    ```
*   **Groq Raw Response Chunk Example:**
    `data: {"id":"...","object":"chat.completion.chunk","created":...,"model":"...","choices":[{"index":0,"delta":{"content":"Text chunk"},"logprobs":null,"finish_reason":null}], ...}`
    `data: [DONE]` (Sometimes appears, sometimes only finish_reason in last chunk)

*   **Sambanova Endpoint:** `https://api.sambanova.ai/v1/chat/completions`
*   **Sambanova Request Body Example:**
    ```json
    {
      "stream": true,
      "model": "Llama-4-Maverick-17B-128E-Instruct",
      "messages": [
        {
          "role": "user",
          "content": [
            {
              "type": "text",
              "text": "Give only the questions as text, DO NOT CHANGE THEIR CONTENT"
            },
            {
              "type": "image_url",
              "image_url": {
                "url": "data:image/jpeg;base64,..."
              }
            }
          ]
        }
      ]
    }
    ```
*   **Sambanova Raw Response Chunk Example:**
    `data: {"choices":[{"delta":{"content":"Text chunk","role":"assistant"},"finish_reason":null,"index":0,...}],"created":...,"id":"...","model":"...","object":"chat.completion.chunk",...}`
    `data: {"choices":[{"delta":{},"finish_reason":"stop","index":0,...}], ...}`
    `data: [DONE]`

Task:

Generate the complete Python script based on these updated requirements and specifications. Include necessary imports, constant definitions for API keys, function definitions (e.g., for encoding, making API calls, parsing responses), the main execution logic for iterating through images, managing concurrency, handling provider switching and errors, and writing the final output file. Add comments where necessary to explain the logic, especially around API key handling and concurrency management for ordered output.