You are working on a project called "the-core-api" and you need to create a detailed documentation for current state of project. Your first task is to create README.md file.

Here is a high level description of "the-core-api":
```
---START_OF_SPEC---

**Project Description**:
The project is a web-based application that provides a chat completion service using various AI models (e.g., GPT-4, GPT-3.5-turbo). The application is built using the FastAPI framework and interacts with an external AI service to generate responses based on user input. The service supports streaming responses, allowing real-time interaction with the AI model by streaming partial results back to the client as they are generated. The application is designed to function as a middle layer between the client and an external AI service, facilitating communication and handling requests for different models.

The project is divided into two main components:
1. **API Layer** (`api.py`): This layer exposes an endpoint that accepts chat completion requests, forwards them to the external AI service, and streams the responses back to the client. It also acts as a reverse proxy, receiving client requests, processing them, and forwarding them to the appropriate backend service (defined in `providers.py`).
2. **Provider Layer** (`providers.py`): This layer is responsible for making asynchronous HTTP requests to the external AI service, handling different models, and processing the responses. It defines the specific APIs being connected to, such as the different GPT models (gpt-4o, gpt-4, gpt-3.5-turbo), and specifies the headers and request formats for each provider.

**Features**:
1. **Chat Completion Endpoint**:
   - The primary feature of the project is a POST endpoint (`/v1/chat/completions`) that accepts chat requests from clients. The requests are validated using Pydantic models and include information such as the model to use and the list of messages (conversation history).
   - The endpoint supports streaming responses, where the AI-generated messages are sent back to the client in real-time using Server-Sent Events (SSE). This allows partial results to be returned as they are generated, enhancing the interactivity of the application.

2. **Model Selection**:
   - The application supports multiple AI models, including `gpt-4`, `gpt-4o`, and `gpt-3.5-turbo`. Users can specify which model they want to use when making a chat request. Each model is associated with a different API endpoint on the external service.
   - The `providers.py` file contains logic to route requests to the appropriate external API endpoint based on the selected model.

3. **External API Integration**:
   - The application integrates with an external AI service (presumably OpenAI or a similar provider) to generate chat completions. It uses asynchronous HTTP requests to communicate with the external API and retrieve responses.
   - The `providers.py` file defines the logic for making these requests and processing the responses. It handles different content types (e.g., JSON) and extracts the relevant message content from the API's response. The file also defines the headers and request format for each provider, ensuring that requests are properly formatted for the backend services.

4. **Streaming Response Handling**:
   - The application uses FastAPI’s `StreamingResponse` to stream the AI-generated responses back to the client. This is particularly useful for long-running tasks where the AI model generates a response over time. The client receives partial results in real-time, improving the user experience.
   - The `oxy_api_stream` function in `api.py` contains the logic for streaming data. It creates a stream of data chunks and yields them one at a time, allowing large amounts of data to be sent efficiently, piece by piece, instead of all at once.

5. **CORS Support**:
   - The application includes CORS middleware, allowing cross-origin requests from any domain. This ensures that the API can be accessed from web clients hosted on different origins, such as front-end applications or third-party services.

6. **Separation of Concerns**:
   - The project is organized into two files: `api.py` and `providers.py`. The `api.py` file handles client interaction and data streaming, while `providers.py` deals with the specifics of connecting to different AI models or services. This separation ensures that the responsibilities are clearly divided, making the codebase more maintainable.

**Technical Specification**:
- **Platform/Technologies**:
  - **Backend**: The backend is built using **FastAPI**, a modern Python web framework known for its performance and ease of use with asynchronous operations.
  - **External API**: The project integrates with an external AI service for chat completions. The service is accessed via HTTP POST requests, and the responses are processed asynchronously using the `aiohttp` library.
  - **Streaming**: The project uses FastAPI’s `StreamingResponse` to stream responses back to the client in real-time. This is implemented in the `api.py` file, where the `oxy_api_stream` function streams the AI-generated messages.
  - **Asynchronous Operations**: The project makes extensive use of Python’s `asyncio` and `aiohttp` for non-blocking, asynchronous HTTP requests. This ensures that the application can handle multiple requests concurrently and efficiently.

- **Endpoints**:
  - **POST `/v1/chat/completions`**:
    - This endpoint accepts a request body containing the model name, a list of messages (conversation history), and an optional `stream` flag.
    - The request body is validated using Pydantic’s `BaseModel`.
    - If the `stream` flag is `True`, the response is streamed back to the client using Server-Sent Events (SSE). Otherwise, the full response is returned once the AI model has generated it.

- **Data Models**:
  - **ChatBody** (in `api.py`): This model defines the structure of the request body for the `/v1/chat/completions` endpoint. It includes:
    - `model`: The name of the AI model to use (e.g., `gpt-4`, `gpt-3.5-turbo`).
    - `messages`: A list of dictionaries representing the conversation history, where each dictionary contains a `role` (e.g., `user`, `assistant`) and `content`.
    - `stream`: An optional boolean flag indicating whether the response should be streamed.

  - **Message** and **ChatCompletionRequest** (in `providers.py`): These models define the structure of the messages and the overall chat completion request sent to the external AI service. The `Message` model includes:
    - `role`: The role of the message sender (e.g., `user`, `assistant`).
    - `content`: The content of the message.

- **External API Interaction**:
  - The `make_request` function in `providers.py` is responsible for sending chat requests to the external AI service. It constructs the request payload (including the model and messages) and sends it to the appropriate API endpoint based on the selected model.
  - The function handles different content types in the response (e.g., JSON, plain text) and raises an error if the external API returns an HTTP error.
  - The `extract_messages` function processes the API’s JSON response and extracts the generated message content.
  - The `HEADERS` constant in `providers.py` defines the headers for each provider, ensuring that requests are properly formatted for the backend services.

- **CORS Configuration**:
  - The application uses FastAPI’s `CORSMiddleware` to enable cross-origin requests. This is configured to allow requests from any origin (`allow_origins=["*"]`), making the API accessible from different domains.

- **Running the Application**:
  - The application can be run locally using Uvicorn, a lightning-fast ASGI server. The `api.py` file includes a `__main__` block that starts the FastAPI app on `0.0.0.0` (all network interfaces) at port `8000`.
  - To run the application, execute the following command:
    ```
    python api.py
    ```

- **Error Handling**:
  - The `oxy_api_stream` function in `api.py` includes basic error handling. If an error occurs while interacting with the external AI service, an error message is streamed back to the client.
  - The `make_request` function in `providers.py` raises an exception if the external API returns an HTTP error (e.g., 4xx or 5xx status codes). This ensures that the client is notified of any issues.

---END_OF_SPEC---
```


Here are the technologies that should be used for this project:
* Python - Python is required to run the FastAPI application.

* fastapi - FastAPI is the web framework used to build the API layer.
* uvicorn - Uvicorn is an ASGI server used to serve the FastAPI application.
* aiohttp - aiohttp is an asynchronous HTTP client used to communicate with external AI services.
* pydantic - Pydantic is used for data validation and parsing in FastAPI.
* python-dotenv - Used to load environment variables from a .env file.
* asyncio - Asyncio is the core Python library for writing asynchronous code.

Here is the feature that you are implementing right now:
```
API File (api.py) as a Reverse Proxy: "The api.py file acts as a reverse proxy. It's the middleman between the client and our backend services. When a client sends a request, api.py receives it, processes it, and then forwards it to the appropriate backend service (in this case, the providers defined in providers.py)."

Streaming Data Logic in API File: "The api.py file contains the logic for streaming data. Look at the oxy_api_stream function - it creates a stream of data chunks and yields them one at a time. This allows us to send large amounts of data efficiently, piece by piece, instead of all at once."

Providers File (providers.py): "The providers.py file is where we define our actual backend services. It contains the specific APIs we're connecting to, like the different GPT models (gpt-4o, gpt-4, gpt-3.5-turbo)."

Headers and Request Format in Providers: "In providers.py, we define the headers and request format for each provider. See the HEADERS constant and the make_request function - they specify exactly how to format requests for our backend services."

Separation of Concerns: "By splitting this into two files, we're keeping things organized. api.py handles the client interaction and data streaming, while providers.py deals with the specifics of connecting to different AI models or services."

How They Work Together: "When a request comes in to api.py, it uses the streaming logic to efficiently handle the data. Then, it uses the information from providers.py to know exactly how to format the request for the specific AI model or service we're using."

You could use an analogy: "Think of api.py as a smart translator at an international conference. It knows how to efficiently relay information (streaming) between attendees (clients) and speakers (backend services). providers.py is like the translator's handbook, containing all the specific languages and customs (headers and request formats) for each speaker. The translator (api.py) uses this handbook (providers.py) to make sure they're communicating correctly with each speaker."
```
These files are currently implemented in the project:
---START_OF_FILES---
**`api.py`** (85 lines of code):
```
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
from pydantic import BaseModel, Field
from typing import List
import json
import asyncio
import uuid
from providers import make_request, make_toolbaz_request

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

class Message(BaseModel):
    role: str
    content: str

class ChatCompletionRequest(BaseModel):
    model: str = Field(default="gpt-4o-mini")
    messages: List[Message]
    stream: bool = False

async def custom_stream_response(response_text, model):
    chunk_size = 5  # Adjust this value to control the size of each chunk
    response_id = f"chatcmpl-{uuid.uuid4().hex[:12]}"
    created = int(asyncio.get_event_loop().time())

    # Initial response
    yield f"data: {json.dumps({'id': response_id, 'object': 'chat.completion.chunk', 'created': created, 'model': model, 'choices': [{'delta': {'role': 'assistant'}, 'index': 0}]})}\n\n"

    for i in range(0, len(response_text), chunk_size):
        chunk = response_text[i:i+chunk_size]
        yield f"data: {json.dumps({'id': response_id, 'object': 'chat.completion.chunk', 'created': created, 'model': model, 'choices': [{'delta': {'content': chunk}, 'index': 0}]})}\n\n"
        await asyncio.sleep(0.1)  # Add a small delay between chunks

    # Final response
    yield f"data: {json.dumps({'id': response_id, 'object': 'chat.completion.chunk', 'created': created, 'model': model, 'choices': [{'delta': {}, 'finish_reason': 'stop', 'index': 0}]})}\n\n"
    yield "data: [DONE]\n\n"

@app.post("/v1/chat/completions")
async def chat_completions(request: ChatCompletionRequest):
    latest_message = request.messages[-1].content

    if request.model in ['gpt-4o', 'gpt-4', 'gpt-3.5-turbo']:
        response_json = await make_request(latest_message, model=request.model)
    else:
        response_json = await make_toolbaz_request(latest_message, request.model, 'session_id')

    response_text = response_json.get('choices', [{}])[0].get('message', {}).get('content', '')

    if request.stream:
        return StreamingResponse(custom_stream_response(response_text, request.model), media_type='text/event-stream')
    else:
        return {
            "id": f"chatcmpl-{uuid.uuid4().hex[:12]}",
            "object": "chat.completion",
            "created": int(asyncio.get_event_loop().time()),
            "model": request.model,
            "choices": [{
                "index": 0,
                "message": {
                    "role": "assistant",
                    "content": response_text
                },
                "finish_reason": "stop"
            }],
            "usage": {
                "prompt_tokens": len(latest_message),
                "completion_tokens": len(response_text),
                "total_tokens": len(latest_message) + len(response_text)
            }
        }

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)


```

**`providers.py`** (67 lines of code):
```
import aiohttp
import json

HEADERS = {
    'Accept': '/',
    'Accept-Language': 'en-US,en;q=0.9',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'Origin': 'https://toolbaz.com/',
    'Pragma': 'no-cache',
    'Referer': 'https://toolbaz.com/',
    'Sec-Fetch-Dest': 'empty',
    'Sec-Fetch-Mode': 'cors',
    'Sec-Fetch-Site': 'same-site',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36',
    'sec-ch-ua': '"Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
}

async def make_request(prompt, model='gpt-4o'):
    headers = {
        'Content-Type': 'application/json'
    }
    data = {
        'messages': [{'role': 'user', 'content': prompt}],
        'model': model
    }
    url = {
        'gpt-4o': "https://nexra.aryahcr.cc/api/chat/complements",
        'gpt-4': "https://nexra.aryahcr.cc/api/chat/gpt",
        'gpt-3.5-turbo': "https://nexra.aryahcr.cc/api/chat/gpt"
    }.get(model, "https://nexra.aryahcr.cc/api/chat/gpt")

    async with aiohttp.ClientSession() as session:
        try:
            async with session.post(url, headers=headers, json=data) as response:
                response.raise_for_status()
                content_type = response.headers.get('Content-Type', '')
                if 'application/json' in content_type:
                    json_response = await response.json()
                else:
                    text_response = await response.text()
                    json_response = json.loads(text_response)
                return {"choices": [{"message": {"content": json_response.get('message', '')}}]}
        except Exception as e:
            print(f"Error in make_request: {e}")
            return {"error": str(e)}

async def make_toolbaz_request(prompt, model, session_id):
    payload = {
        'text': prompt,
        'model': model,
        'sessionid': session_id
    }
    async with aiohttp.ClientSession() as session:
        try:
            async with session.post('https://data.toolbaz.com/writer.php', headers=HEADERS, data=payload) as response:
                response.raise_for_status()
                text_response = await response.text()
                return {"choices": [{"message": {"content": text_response}}]}
        except Exception as e:
            print(f"Error in make_toolbaz_request: {e}")
            return {"error": f"Error in external service: {str(e)}"}


```

**`test.py`** (40 lines of code):
```
import asyncio
import aiohttp
import json

async def make_request(prompt, model):
    url = 'http://localhost:8000/v1/chat/completions'
    payload = {
        'model': model,
        'messages': [
            {'role': 'user', 'content': prompt}
        ],
        'stream': True  # Enable streaming
    }
    async with aiohttp.ClientSession() as session:
        async with session.post(url, json=payload) as response:
            async for line in response.content.iter_any():
                if line:
                    line = line.decode('utf-8').strip()
                    if line.startswith("data:"):
                        data = line[5:].strip()  # Remove the "data: " prefix
                        if data == "[DONE]":
                            break
                        try:
                            json_data = json.loads(data)
                            print('Received:', json_data)
                        except json.JSONDecodeError:
                            print('Received (non-JSON):', data)

async def test_reverse_api():
    models = ['gpt-4o', 'gpt-4', 'gpt-3.5-turbo', 'gpt-4o-mini']
    prompt = 'Hello, how are you?'
    for model in models:
        print(f"\nTesting model: {model}")
        try:
            await make_request(prompt, model)
        except Exception as e:
            print('Error calling reverse API:', str(e))

if __name__ == '__main__':
    asyncio.run(test_reverse_api())
```

---END_OF_FILES---

DO NOT specify commands to create any folders or files, they will be created automatically - just specify the relative path to file that needs to be written.

**IMPORTANT**: Pay attention to file paths: if the command or argument is a file or folder from the project, use paths relative to the project root.
 For example:
 - use `dirname/filename.py` instead of `/path/to/project/dirname/filename.py`
 - use `filename.js` instead of `./filename.js`

Now, based on the project details provided, think step by step and create README.md file for this project. The file should have the following format:

# Project name

Short description (a few sentences) of the project based on the project details.

## Overview

Description of the architecture and technologies used in the project, and the project structure.

## Features

Description of what the app can do and how it can be used.

## Getting started

### Requirements

Required technologies/setup needed on the computer to run the project.

### Quickstart

How to set up the project and run it

### License

The project is proprietary (not open source), just output the standard Copyright (c) 2024. template here.