Skip to content

Commit 17254d0

Browse files
committed
Add server mode with FastAPI
1 parent 7d0dd29 commit 17254d0

File tree

7 files changed

+157
-2
lines changed

7 files changed

+157
-2
lines changed

Diff for: aider/args.py

+17
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,23 @@ def get_parser(default_config_files, git_root):
616616
default=False,
617617
help="Enable automatic copy/paste of chat between aider and web UI (default: False)",
618618
)
619+
group.add_argument(
620+
"--server",
621+
action="store_true",
622+
help="Start aider as a FastAPI server (default: False)",
623+
default=False,
624+
)
625+
group.add_argument(
626+
"--host",
627+
default="127.0.0.1",
628+
help="Specify the host to bind the server to (only used with --server, default: 127.0.0.1)",
629+
)
630+
group.add_argument(
631+
"--port",
632+
type=int,
633+
default=8000,
634+
help="Specify the port to bind the server to (only used with --server, default: 8000)",
635+
)
619636
group.add_argument(
620637
"--apply",
621638
metavar="FILE",

Diff for: aider/main.py

+9
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,15 @@ def get_io(pretty):
660660
analytics.event("exit", reason="GUI session ended")
661661
return
662662

663+
if args.server and not return_coder:
664+
from aider.server import start_server
665+
666+
io.tool_output("Starting aider API server...")
667+
analytics.event("server session")
668+
start_server(argv, host=args.host, port=args.port)
669+
analytics.event("exit", reason="Server session ended")
670+
return
671+
663672
if args.verbose:
664673
for fname in loaded_dotenvs:
665674
io.tool_output(f"Loaded {fname}")

Diff for: aider/server.py

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import asyncio
2+
import traceback
3+
4+
import uvicorn
5+
from fastapi import FastAPI, HTTPException
6+
from fastapi.responses import StreamingResponse
7+
from pydantic import BaseModel
8+
9+
from aider.main import main as cli_main
10+
11+
12+
class ChatRequest(BaseModel):
13+
message: str
14+
stream: bool = True
15+
16+
17+
class ChatResponse(BaseModel):
18+
content: str
19+
20+
21+
async def process_generator(generator):
22+
"""Convert the synchronous generator to an async one."""
23+
for chunk in generator:
24+
yield chunk
25+
await asyncio.sleep(0)
26+
27+
28+
def create_app(coder):
29+
"""Create a FastAPI application with a coder instance."""
30+
app = FastAPI(title="Aider API")
31+
32+
@app.post("/chat")
33+
async def chat(request: ChatRequest):
34+
"""Process a chat message and return the response from the coder."""
35+
try:
36+
if request.stream:
37+
generator = coder.run_stream(request.message)
38+
return StreamingResponse(process_generator(generator), media_type="text/plain")
39+
else:
40+
response = coder.run(with_message=request.message)
41+
return ChatResponse(content=response)
42+
except Exception as e:
43+
error_detail = f"{str(e)}\n{traceback.format_exc()}"
44+
raise HTTPException(status_code=500, detail=error_detail)
45+
46+
return app
47+
48+
49+
def start_server(argv, host="127.0.0.1", port=8000):
50+
"""Start the FastAPI server with the given coder."""
51+
coder = cli_main(argv=argv, return_coder=True)
52+
app = create_app(coder)
53+
# Required to force the coder to return text from run and run_stream
54+
coder.stream = True
55+
coder.pretty = False
56+
uvicorn.run(app, host=host, port=port)

Diff for: pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ dev = { file = "requirements/requirements-dev.txt" }
3333
help = { file = "requirements/requirements-help.txt" }
3434
browser = { file = "requirements/requirements-browser.txt" }
3535
playwright = { file = "requirements/requirements-playwright.txt" }
36+
server = { file = "requirements/requirements-server.txt" }
3637

3738
[tool.setuptools]
3839
include-package-data = true

Diff for: requirements/common-constraints.txt

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# This file was autogenerated by uv via the following command:
2-
# uv pip compile --no-strip-extras --output-file=requirements/common-constraints.txt requirements/requirements.in requirements/requirements-browser.in requirements/requirements-dev.in requirements/requirements-help.in requirements/requirements-playwright.in
2+
# uv pip compile --no-strip-extras --output-file=requirements/common-constraints.txt requirements/requirements.in requirements/requirements-browser.in requirements/requirements-dev.in requirements/requirements-help.in requirements/requirements-playwright.in requirements/requirements-server.in
33
aiohappyeyeballs==2.6.1
44
# via aiohttp
55
aiohttp==3.11.16
@@ -17,6 +17,7 @@ anyio==4.9.0
1717
# via
1818
# httpx
1919
# openai
20+
# starlette
2021
# watchfiles
2122
attrs==25.3.0
2223
# via
@@ -59,6 +60,7 @@ click==8.1.8
5960
# pip-tools
6061
# streamlit
6162
# typer
63+
# uvicorn
6264
codespell==2.4.1
6365
# via -r requirements/requirements-dev.in
6466
cogapp==3.4.1
@@ -93,6 +95,8 @@ distro==1.9.0
9395
# via
9496
# openai
9597
# posthog
98+
fastapi==0.115.12
99+
# via -r requirements/requirements-server.in
96100
filelock==3.18.0
97101
# via
98102
# huggingface-hub
@@ -156,7 +160,9 @@ grpcio==1.71.0
156160
grpcio-status==1.71.0
157161
# via google-api-core
158162
h11==0.14.0
159-
# via httpcore
163+
# via
164+
# httpcore
165+
# uvicorn
160166
httpcore==1.0.7
161167
# via httpx
162168
httpx==0.28.1
@@ -363,6 +369,7 @@ pycparser==2.22
363369
pydantic==2.11.2
364370
# via
365371
# banks
372+
# fastapi
366373
# litellm
367374
# llama-index-core
368375
# openai
@@ -479,6 +486,8 @@ soupsieve==2.6
479486
# via beautifulsoup4
480487
sqlalchemy[asyncio]==2.0.40
481488
# via llama-index-core
489+
starlette==0.46.1
490+
# via fastapi
482491
streamlit==1.44.1
483492
# via -r requirements/requirements-browser.in
484493
sympy==1.13.3
@@ -532,6 +541,7 @@ typing-extensions==4.13.1
532541
# altair
533542
# anyio
534543
# beautifulsoup4
544+
# fastapi
535545
# huggingface-hub
536546
# llama-index-core
537547
# openai
@@ -560,6 +570,8 @@ urllib3==2.3.0
560570
# requests
561571
uv==0.6.12
562572
# via -r requirements/requirements-dev.in
573+
uvicorn==0.34.0
574+
# via -r requirements/requirements-server.in
563575
virtualenv==20.30.0
564576
# via pre-commit
565577
watchfiles==1.0.4

Diff for: requirements/requirements-server.in

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
fastapi
2+
uvicorn

Diff for: requirements/requirements-server.txt

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# This file was autogenerated by uv via the following command:
2+
# uv pip compile --no-strip-extras --constraint=requirements/common-constraints.txt --output-file=requirements/requirements-server.txt requirements/requirements-server.in
3+
annotated-types==0.7.0
4+
# via
5+
# -c requirements/common-constraints.txt
6+
# pydantic
7+
anyio==4.9.0
8+
# via
9+
# -c requirements/common-constraints.txt
10+
# starlette
11+
click==8.1.8
12+
# via
13+
# -c requirements/common-constraints.txt
14+
# uvicorn
15+
fastapi==0.115.12
16+
# via
17+
# -c requirements/common-constraints.txt
18+
# -r requirements/requirements-server.in
19+
h11==0.14.0
20+
# via
21+
# -c requirements/common-constraints.txt
22+
# uvicorn
23+
idna==3.10
24+
# via
25+
# -c requirements/common-constraints.txt
26+
# anyio
27+
pydantic==2.11.2
28+
# via
29+
# -c requirements/common-constraints.txt
30+
# fastapi
31+
pydantic-core==2.33.1
32+
# via
33+
# -c requirements/common-constraints.txt
34+
# pydantic
35+
sniffio==1.3.1
36+
# via
37+
# -c requirements/common-constraints.txt
38+
# anyio
39+
starlette==0.46.1
40+
# via
41+
# -c requirements/common-constraints.txt
42+
# fastapi
43+
typing-extensions==4.13.1
44+
# via
45+
# -c requirements/common-constraints.txt
46+
# anyio
47+
# fastapi
48+
# pydantic
49+
# pydantic-core
50+
# typing-inspection
51+
typing-inspection==0.4.0
52+
# via
53+
# -c requirements/common-constraints.txt
54+
# pydantic
55+
uvicorn==0.34.0
56+
# via
57+
# -c requirements/common-constraints.txt
58+
# -r requirements/requirements-server.in

0 commit comments

Comments
 (0)