283 lines
13 KiB
Markdown
283 lines
13 KiB
Markdown
# Cursor Azure GPT-5
|
|
|
|
[](#)
|
|
[](#)
|
|
[](#)
|
|
[](#)
|
|

|
|

|
|

|
|
|
|
A service that allows Cursor to use Azure GPT-5 deployments by:
|
|
- Adapting incoming Cursor **completions API** requests to the **Responses API**
|
|
- Forwarding the requests to Azure
|
|
- Adapting outgoing Azure **Responses API** streams into **completions API** streams
|
|
|
|
This project originates from Cursor's lack of support for Azure models that are only served through the **Responses API**. It will hopefully become obsolete as Cursor continues to improve its model support.
|
|
|
|
> [!WARNING]
|
|
> You still need an active paid Cursor subscription to be able to use this project.
|
|
|
|
> [!IMPORTANT]
|
|
> **Azure** now supports the **Completions API** for the models `gpt-5`, `gpt-5-mini`, and `gpt-5-nano`.
|
|
>
|
|
> They can now be used directly in Cursor, but without the ability to change the _Reasoning Effort_ / _Verbosity_ / _Summary Level_. To do so, you can still use this project.
|
|
>
|
|
> The models `gpt-5-pro` and `gpt-5-codex` remain available only through the **Responses API**, but work great with this project (see list of specific model limitations in the next section).
|
|
|
|
|
|
## Supported Models
|
|
|
|
The entire gpt-5 series is supported, although some models have some limitations on the reasoning effort / verbosity / truncation / summary values they accept:
|
|
|
|
| Variable | Value | 5.2 | 5.2-chat | 5.1 | 5.1-codex | 5.1-codex-mini | 5.1-codex-max | 5 | 5-nano | 5-mini | 5-pro | 5-codex |
|
|
| ---------- | ---------- | --- | -------- | --- | --------- | -------------- | ------------- | --- | ------ | ------ | ----- | ------- |
|
|
| Reasoning | `minimal` | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
| | `low` | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
|
|
| | `medium` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
|
|
| | `high` | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
| Verbosity | `low` | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
| | `medium` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
|
|
| | `high` | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
| Truncation | `auto` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
|
|
| | `disabled` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
|
|
| Summary | `auto` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
|
|
| | `detailed` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
|
|
| | `concise` | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
|
|
_(This matrix is automatically generated, and updated after every new model release.)_
|
|
|
|
## Feature highlights
|
|
|
|
- Switching between `high`/`medium`/`low`/`minimal` reasoning effort levels by selecting different models in Cursor.
|
|
- Configuring different _reasoning summary_ levels (`auto`, `detailed`, `concise`).
|
|
- Displaying _reasoning summaries_ in Cursor natively, like any other reasoning model.
|
|
- Production-ready, so you can share the service among different users in an organization.
|
|
- When running from a terminal, [rich](https://github.com/Textualize/rich) logging of the model's context on every request, including Markdown rendering, syntax highlighting, tool calls/outputs, and more.
|
|
|
|
Feel free to create or vote on any [project issues](https://github.com/gabrii/Cursor-Azure-GPT-5/issues), and star the project to show your support.
|
|
|
|
## Quick start
|
|
|
|
If you prefer to deploy the service (for example, to allow multiple members of your team to use it), check the [Production](#production) section, as the project comes with production-ready containers using `supervisord` and `gunicorn`.
|
|
|
|
### 1. Service configuration
|
|
|
|
Make a copy of the file `.env.example` as `.env` and update the following flags as needed:
|
|
|
|
| Flag | Description | Default |
|
|
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ----------- |
|
|
| `SERVICE_API_KEY` | Arbitrary API key to protect your service. Set it to a random string. | `change-me` |
|
|
| `AZURE_BASE_URL` | Your Azure OpenAI endpoint base URL (no trailing slash), e.g. `https://<resource>.openai.azure.com`. | required |
|
|
| `AZURE_API_KEY` | Azure OpenAI API key. | required |
|
|
| `AZURE_DEPLOYMENT` | Name of the Azure model deployment to use. | `gpt-5` |
|
|
| `AZURE_VERBOSITY_LEVEL` | Hint the model to be more or less expansive in its replies. Use either `high` / `medium` / `low` | `medium` |
|
|
| `AZURE_SUMMARY_LEVEL` | Set to `none` to disable summaries. You might have to disable them if your organization hasn't been approved for this feature. | `detailed` |
|
|
| `AZURE_TRUNCATION` | Truncation strategy for long inputs. Either `auto` or `disabled` | `disabled` |
|
|
|
|
Alternatively, you can pass them through the environment where you run the application.
|
|
|
|
<details>
|
|
<summary>Optional Configuration</summary>
|
|
|
|
| Flag | Description | Default |
|
|
| ------------------- | ---------------------------------------------------------------------- | -------------------- |
|
|
| `AZURE_API_VERSION` | Azure OpenAI Responses API version to call. | `2025-04-01-preview` |
|
|
| `FLASK_ENV` | Flask environment. Use `development` for dev or `production` for prod. | `production` |
|
|
| `RECORD_TRAFFIC` | Toggle writing request/response traffic to `recordings/` | `off` |
|
|
| `LOG_CONTEXT` | Enable rich pretty-printing of request context to console. | `on` |
|
|
| `LOG_COMPLETION` | Enable logging of completion responses (not yet implemented). | `on` |
|
|
|
|
</details>
|
|
|
|
### 2. Exposing the service
|
|
|
|
<details>
|
|
<summary>Why do I have to?</summary>
|
|
|
|
> Since Cursor routes requests through its external prompt-building service rather than directly from the IDE to your API, your custom endpoint must be publicly reachable on the Internet.
|
|
>
|
|
> Consider using Cloudflare because its tunnels are free and require no account.
|
|
</details>
|
|
|
|
[Install `cloudflared`](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/) and run:
|
|
|
|
```bash
|
|
cloudflared tunnel --url http://localhost:8080
|
|
```
|
|
|
|
Copy the URL of your tunnel from the output of the command. It looks something like this:
|
|
|
|
```text
|
|
+----------------------------------------------------+
|
|
| Your quick Tunnel has been created! Visit it at: |
|
|
| https://foo-bar.trycloudflare.com |
|
|
+----------------------------------------------------+
|
|
```
|
|
|
|
Then paste it into _Cursor Settings > Models > API Keys > OpenAI API Key > Override OpenAI Base URL_:
|
|
|
|

|
|
|
|
### 3. Configuring Cursor
|
|
|
|
In addition to updating the OpenAI Base URL, you need to:
|
|
|
|
1. Set _OpenAI API Key_ to the value of `SERVICE_API_KEY` in your `.env`
|
|
|
|
2. Ensure the toggles for both options are **on**, as shown in the previous image.
|
|
|
|
3. Add the custom models called exactly `gpt-high`, `gpt-medium`, and `gpt-low`, as shown in the previous image. You can also create `gpt-minimal` for minimal reasoning effort for models that support it. You don't need to remove other models.
|
|
|
|
### 4. Running the service
|
|
|
|
To run the production version of the app:
|
|
|
|
```bash
|
|
docker compose up flask-prod
|
|
```
|
|
|
|
> For instructions on how to run locally without Docker, and the different development commands, see the [Development](#development) section.
|
|
|
|
## Development
|
|
|
|
### Running locally
|
|
|
|
<details><summary>Expand</summary>
|
|
|
|
#### Bootstrap your local environment
|
|
|
|
```bash
|
|
python -m venv .venv
|
|
pip install -r requirements/dev.txt
|
|
```
|
|
|
|
#### Running the development server
|
|
|
|
```bash
|
|
flask run -p 8080
|
|
```
|
|
|
|
#### Running the production server*
|
|
|
|
```bash
|
|
export FLASK_ENV=production
|
|
export FLASK_DEBUG=0
|
|
export LOG_LEVEL=info
|
|
flask run -p 8080
|
|
```
|
|
|
|
This will only run the Flask server with the production settings. For a closer approximation of the production server running with `supervisord` and `gunicorn`, check [Running with Docker](#running-with-docker).
|
|
|
|
#### Running tests
|
|
|
|
```bash
|
|
flask test
|
|
```
|
|
|
|
To run only specific tests, you can use the pytest `-k` argument:
|
|
|
|
```bash
|
|
flask test -k ...
|
|
```
|
|
|
|
#### Running linter
|
|
|
|
```bash
|
|
flask lint
|
|
```
|
|
|
|
The `lint` command will attempt to fix any linting/style errors in the code. If you only want to know if the code will pass CI and do not wish for the linter to make changes, add the `--check` argument.
|
|
|
|
```bash
|
|
flask lint --check
|
|
```
|
|
|
|
</details>
|
|
|
|
### Running with Docker
|
|
|
|
<details><summary>Expand</summary>
|
|
|
|
#### Running the development server
|
|
|
|
```bash
|
|
docker compose up flask-dev
|
|
```
|
|
|
|
#### Running the production server
|
|
|
|
```bash
|
|
docker compose up flask-prod
|
|
```
|
|
|
|
This image runs the server through `supervisord` and `gunicorn`. See the [Production](#production) section for more details.
|
|
|
|
When running flask-prod, the production flags are set in `docker-compose.yml`:
|
|
|
|
```yml
|
|
FLASK_ENV: production
|
|
FLASK_DEBUG: 0
|
|
LOG_LEVEL: info
|
|
GUNICORN_WORKERS: 4
|
|
```
|
|
|
|
The list of `environment:` variables in the `docker-compose.yml` file takes precedence over any variables specified in `.env`.
|
|
|
|
#### Running tests
|
|
|
|
```bash
|
|
docker compose run --rm manage test
|
|
```
|
|
|
|
To run only specific tests, you can use the pytest `-k` argument:
|
|
|
|
```bash
|
|
docker compose run --rm manage test -k ...
|
|
```
|
|
|
|
#### Running linter
|
|
|
|
```bash
|
|
docker compose run --rm manage lint
|
|
```
|
|
|
|
The `lint` command will attempt to fix any linting/style errors in the code. If you only want to know if the code will pass CI and do not wish for the linter to make changes, add the `--check` argument.
|
|
|
|
```bash
|
|
docker compose run --rm manage lint --check
|
|
```
|
|
|
|
</details>
|
|
|
|
## Testing
|
|
|
|
To make the generation of test fixtures easier, the `RECORD_TRAFFIC` flag has been added, which creates files with all the incoming/outgoing traffic between this service and Cursor/Azure in the directory `recordings/`
|
|
|
|
To avoid violating Cursor's intellectual property, a redaction layer removes any sensitive data, such as: system prompts, tool names, tool descriptions, and any context containing scaffolding from Cursor's prompt-building service.
|
|
|
|
Therefore, recorded traffic can be published under `tests/recordings/` to be used as test fixtures while remaining MIT-licensed.
|
|
|
|
## Production
|
|
|
|
<details><summary>Expand</summary>
|
|
|
|
### Configure server
|
|
|
|
You might want to review and modify the following configuration files:
|
|
|
|
| File | Description |
|
|
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
| `supervisord/gunicorn.conf` | Supervisor program config for Gunicorn (bind :5000, gevent; workers/log level from env; logs to stdout/stderr). |
|
|
| `supervisord/supervisord_entrypoint.sh` | Container entrypoint that execs supervisord (prepends it when args start with -). |
|
|
| `supervisord/supervisord.conf` | Main Supervisord config: socket, logging, nodaemon; includes conf.d program configs. |
|
|
|
|
### Build, tag, and push the image
|
|
|
|
```bash
|
|
docker compose build flask-prod
|
|
docker tag app-production your-tag
|
|
docker push your-tag
|
|
```
|
|
|
|
</details> |