This project transforms Google's stateful, autonomous Jules API (a long-polling software engineering agent) into a high-throughput, stateless OpenAI-compatible REST API (/v1/chat/completions).
By acting as a proxy layer, this application allows you to use Jules as a standard Large Language Model (LLM) drop-in replacement for any tool, framework, or application that expects standard OpenAI JSON schemas.
Jules is natively designed to clone GitHub repositories, generate PRs, and wait for human plan approval. This proxy tames that behavior by intentionally initiating a repoless session (omitting the sourceContext parameter). It injects a strict system "jailbreak" prompt that forces Jules to abandon its agentic tendencies and return raw JSON exclusively.
Google's distributed infrastructure relies on eventual consistency. When a session is created via POST /v1alpha/sessions, it takes a moment to propagate to the activities.list datastore. This proxy is built to asynchronously catch the initial 404 Not Found polling errors, gracefully back off, and retry until the session becomes available.
Because Jules is operating in a repoless state without files to edit, he functions like a standard chatbot. The proxy intelligently detects the agentMessaged event in the activity log and immediately terminates the polling loop, extracting the text output and wrapping it in the standard OpenAI response schema.
- Python 3.9+
- A valid Google API key with access to the Jules API (
jules.googleapis.com)
-
Clone the repository and enter the directory:
git clone <repository_url> cd fastapi-jules-wrapper
-
Create a virtual environment and install dependencies:
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate pip install -r requirements.txt
-
Configure your Environment: Copy the example environment file and add your API key:
cp .env.example .env # Edit .env and set JULES_API_KEY="your-api-key" export JULES_API_KEY="your-api-key"
Start the FastAPI application using Uvicorn. For production, you can increase the number of workers to handle more concurrent requests.
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4You should see the server start up successfully on http://127.0.0.1:8000.
You can interact with the proxy exactly as you would with the OpenAI API.
We have provided a built-in async Python test client to verify the connection and polling mechanics.
In a separate terminal, run:
python test_client.pyThis will send a sample payload to the proxy, wait for Jules to process it, and print the raw JSON response along with the extracted text.
You can send a direct REST request to the proxy:
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "jules-proxy-model",
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "Write a one sentence summary of what a proxy server does."
}
],
"temperature": 0.7
}'Because the API perfectly mimics the /v1/chat/completions schema, you can use the official OpenAI Python or Node.js SDKs by simply changing the base_url.
from openai import OpenAI
client = OpenAI(
api_key="dummy-key-not-needed",
base_url="http://localhost:8000/v1"
)
response = client.chat.completions.create(
model="jules-proxy-model",
messages=[
{"role": "user", "content": "Hello!"}
]
)
print(response.choices[0].message.content)While the default /v1/chat/completions endpoint operates statelessly (creating a new session for every request), you can manually interact with the same Jules session to send follow-up prompts and create an experience similar to the OpenAI Assistants API.
Here is the step-by-step guide on how to do it:
When you make a request to the proxy, it returns a standard OpenAI JSON response. Look at the id field.
{
"id": "chatcmpl-9002916533543203582",
"object": "chat.completion"
}The proxy embeds the actual Jules Session ID into this field. In this example, your session ID is sessions/9002916533543203582.
You can send a follow-up message directly to the Jules API using the sendMessage endpoint. You do not need to use the proxy for this step.
curl -X POST "https://jules.googleapis.com/v1alpha/sessions/9002916533543203582:sendMessage" \
-H "Content-Type: application/json" \
-H "x-goog-api-key: YOUR_JULES_API_KEY" \
-d '{
"message": "That was great! Now write a second stanza."
}'Once the message is sent, Jules will process the new prompt and append his response to the activity log. You can manually poll the activities endpoint to grab his reply:
curl -X GET "https://jules.googleapis.com/v1alpha/sessions/9002916533543203582/activities?pageSize=50" \
-H "x-goog-api-key: YOUR_JULES_API_KEY"Look through the JSON response for the latest activity containing an agentMessaged block to find your result!
If a request fails or times out, check the Uvicorn terminal output. The proxy logs the exact REST requests, 404 eventual-consistency retries, and the raw JSON of the activities array received from the Google API to help you debug.