Skip to content

Commit b90a42a

Browse files
committed
fix openapi documentation for endpoints to make it more LLM friendly, fix content of SwaggerUI H1, make the ingest POST endpoint accept JSON headers instead of weird application/x-www-form which's better for LLM and API integration
1 parent bfef41c commit b90a42a

File tree

5 files changed

+45
-96
lines changed

5 files changed

+45
-96
lines changed

src/server/main.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,9 @@
4949
async def health_check() -> dict[str, str]:
5050
"""Health check endpoint to verify that the server is running.
5151
52-
Returns
53-
-------
54-
dict[str, str]
55-
A JSON object with a "status" key indicating the server's health status.
52+
**Returns**
53+
54+
- **dict[str, str]**: A JSON object with a "status" key indicating the server's health status.
5655
5756
"""
5857
return {"status": "healthy"}

src/server/routers/ingest.py

Lines changed: 13 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
from fastapi import APIRouter, Request, status
44
from fastapi.responses import JSONResponse
55

6-
from server.form_types import IntForm, OptStrForm, StrForm
7-
from server.models import IngestErrorResponse, IngestRequest, IngestSuccessResponse, PatternType
6+
from server.models import IngestErrorResponse, IngestRequest, IngestSuccessResponse
87
from server.query_processor import process_query
98
from server.server_utils import limiter
109

@@ -21,50 +20,25 @@
2120
)
2221
@limiter.limit("10/minute")
2322
async def api_ingest(
24-
request: Request, # noqa: ARG001 (unused) pylint: disable=unused-argument
25-
input_text: StrForm,
26-
max_file_size: IntForm,
27-
pattern_type: StrForm = "exclude",
28-
pattern: StrForm = "",
29-
token: OptStrForm = None,
23+
request: Request, # noqa: ARG001
24+
ingest_request: IngestRequest,
3025
) -> JSONResponse:
3126
"""Ingest a Git repository and return processed content.
3227
33-
This endpoint processes a Git repository by cloning it, analyzing its structure,
28+
**This endpoint processes a Git repository by cloning it, analyzing its structure,**
3429
and returning a summary with the repository's content. The response includes
3530
file tree structure, processed content, and metadata about the ingestion.
3631
37-
Parameters
38-
----------
39-
request : Request
40-
FastAPI request object
41-
input_text : StrForm
42-
Git repository URL or slug to ingest
43-
max_file_size : IntForm
44-
Maximum file size slider position (0-500) for filtering files
45-
pattern_type : StrForm
46-
Type of pattern to use for file filtering ("include" or "exclude")
47-
pattern : StrForm
48-
Glob/regex pattern string for file filtering
49-
token : OptStrForm
50-
GitHub personal access token (PAT) for accessing private repositories
32+
**Parameters**
5133
52-
Returns
53-
-------
54-
JSONResponse
55-
Success response with ingestion results or error response with appropriate HTTP status code
34+
- **ingest_request** (`IngestRequest`): Pydantic model containing ingestion parameters
5635
57-
"""
58-
try:
59-
# Validate input using Pydantic model
60-
ingest_request = IngestRequest(
61-
input_text=input_text,
62-
max_file_size=max_file_size,
63-
pattern_type=PatternType(pattern_type),
64-
pattern=pattern,
65-
token=token,
66-
)
36+
**Returns**
6737
38+
- **JSONResponse**: Success response with ingestion results or error response with appropriate HTTP status code
39+
40+
""" # pylint: disable=unused-argument
41+
try:
6842
result = await process_query(
6943
input_text=ingest_request.input_text,
7044
slider_position=ingest_request.max_file_size,
@@ -90,7 +64,7 @@ async def api_ingest(
9064
# Handle validation errors with 400 status code
9165
error_response = IngestErrorResponse(
9266
error=f"Validation error: {ve!s}",
93-
repo_url=input_text,
67+
repo_url=ingest_request.input_text,
9468
)
9569
return JSONResponse(
9670
status_code=status.HTTP_400_BAD_REQUEST,
@@ -101,7 +75,7 @@ async def api_ingest(
10175
# Handle unexpected errors with 500 status code
10276
error_response = IngestErrorResponse(
10377
error=f"Internal server error: {exc!s}",
104-
repo_url=input_text,
78+
repo_url=ingest_request.input_text,
10579
)
10680
return JSONResponse(
10781
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,

src/server/templates/swagger_ui.jinja

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,14 @@
22
{% block title %}API Docs{% endblock %}
33
{% block content %}
44
<div class="mb-8">
5-
<div class="relative w-full mx-auto flex sm:flex-row flex-col justify-center items-start sm:items-center">
6-
<svg class="h-auto w-16 sm:w-20 md:w-24 flex-shrink-0 p-2 md:relative sm:absolute lg:absolute left-0 lg:-translate-x-full lg:ml-32 md:translate-x-10 sm:-translate-y-16 md:-translate-y-0 -translate-x-2 lg:-translate-y-10"
7-
viewBox="0 0 91 98"
8-
fill="none"
9-
xmlns="http://www.w3.org/2000/svg">
10-
<path d="m35.878 14.162 1.333-5.369 1.933 5.183c4.47 11.982 14.036 21.085 25.828 24.467l5.42 1.555-5.209 2.16c-11.332 4.697-19.806 14.826-22.888 27.237l-1.333 5.369-1.933-5.183C34.56 57.599 24.993 48.496 13.201 45.114l-5.42-1.555 5.21-2.16c11.331-4.697 19.805-14.826 22.887-27.237Z" fill="#FE4A60" stroke="#000" stroke-width="3.445">
11-
</path>
12-
<path d="M79.653 5.729c-2.436 5.323-9.515 15.25-18.341 12.374m9.197 16.336c2.6-5.851 10.008-16.834 18.842-13.956m-9.738-15.07c-.374 3.787 1.076 12.078 9.869 14.943M70.61 34.6c.503-4.21-.69-13.346-9.49-16.214M14.922 65.967c1.338 5.677 6.372 16.756 15.808 15.659M18.21 95.832c-1.392-6.226-6.54-18.404-15.984-17.305m12.85-12.892c-.41 3.771-3.576 11.588-12.968 12.681M18.025 96c.367-4.21 3.453-12.905 12.854-14" stroke="#000" stroke-width="2.548" stroke-linecap="round">
13-
</path>
14-
</svg>
15-
<h1 class="text-4xl sm:text-5xl sm:pt-20 lg:pt-5 md:text-6xl lg:text-7xl font-bold tracking-tighter w-full inline-block text-left md:text-center relative">
16-
Gitingest API
17-
<br>
18-
Documentation
19-
</h1>
20-
<svg class="w-16 lg:w-20 h-auto lg:absolute flex-shrink-0 right-0 bottom-0 md:block hidden translate-y-10 md:translate-y-20 lg:translate-y-4 lg:-translate-x-12 -translate-x-10"
21-
viewBox="0 0 92 80"
22-
fill="none"
23-
xmlns="http://www.w3.org/2000/svg">
24-
<path d="m35.213 16.953.595-5.261 2.644 4.587a35.056 35.056 0 0 0 26.432 17.33l5.261.594-4.587 2.644A35.056 35.056 0 0 0 48.23 63.28l-.595 5.26-2.644-4.587a35.056 35.056 0 0 0-26.432-17.328l-5.261-.595 4.587-2.644a35.056 35.056 0 0 0 17.329-26.433Z" fill="#5CF1A4" stroke="#000" stroke-width="2.868" class="">
25-
</path>
26-
<path d="M75.062 40.108c1.07 5.255 1.072 16.52-7.472 19.54m7.422-19.682c1.836 2.965 7.643 8.14 16.187 5.121-8.544 3.02-8.207 15.23-6.971 20.957-1.97-3.343-8.044-9.274-16.588-6.254M12.054 28.012c1.34-5.22 6.126-15.4 14.554-14.369M12.035 28.162c-.274-3.487-2.93-10.719-11.358-11.75C9.104 17.443 14.013 6.262 15.414.542c.226 3.888 2.784 11.92 11.212 12.95" stroke="#000" stroke-width="2.319" stroke-linecap="round">
27-
</path>
28-
</svg>
5+
<div class="relative w-full flex sm:flex-row flex-col justify-center sm:items-center">
6+
{# Title & Sparkles #}
7+
<h1 class="landing-page-title">Gitingest API</h1>
8+
<img src="/static/svg/sparkle-red.svg" class="sparkle-red no-drag">
9+
<img src="/static/svg/sparkle-green.svg" class="sparkle-green no-drag">
2910
</div>
30-
<p class="text-gray-600 text-lg max-w-2xl mx-auto text-center mt-8">
31-
Turn any Git repository into a simple text digest of its codebase.
32-
</p>
33-
<p class="text-gray-600 text-lg max-w-2xl mx-auto text-center mt-0">
34-
This is useful for feeding a codebase into any LLM.
35-
</p>
11+
<p class="intro-text mt-8">Turn any Git repository into a simple text digest of its codebase.</p>
12+
<p class="intro-text mt-0">This is useful for feeding a codebase into any LLM.</p>
3613
</div>
3714
<div class="bg-[#fff4da] rounded-xl border-[3px] border-gray-900 p-4 md:p-8 relative z-20">
3815
<div id="swagger-ui"></div>

src/static/js/utils.js

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -72,24 +72,19 @@ function handleSubmit(event, showLoadingSpinner = false) {
7272
const submitButton = form.querySelector('button[type="submit"]');
7373
if (!submitButton) return;
7474

75-
const formData = new FormData(form);
76-
77-
// Update file size
75+
// Collect form values into a plain object
76+
let data = {};
77+
const inputText = form.querySelector('[name="input_text"]');
78+
const token = form.querySelector('[name="token"]');
7879
const slider = document.getElementById('file_size');
79-
if (slider) {
80-
formData.delete('max_file_size');
81-
formData.append('max_file_size', slider.value);
82-
}
83-
84-
// Update pattern type and pattern
8580
const patternType = document.getElementById('pattern_type');
8681
const pattern = document.getElementById('pattern');
87-
if (patternType && pattern) {
88-
formData.delete('pattern_type');
89-
formData.delete('pattern');
90-
formData.append('pattern_type', patternType.value);
91-
formData.append('pattern', pattern.value);
92-
}
82+
83+
if (inputText) data.input_text = inputText.value;
84+
if (token) data.token = token.value;
85+
if (slider) data.max_file_size = slider.value;
86+
if (patternType) data.pattern_type = patternType.value;
87+
if (pattern) data.pattern = pattern.value;
9388

9489
const originalContent = submitButton.innerHTML;
9590

@@ -107,8 +102,12 @@ function handleSubmit(event, showLoadingSpinner = false) {
107102
submitButton.classList.add('bg-[#ffb14d]');
108103
}
109104

110-
// Submit the form to /api/ingest
111-
fetch('/api/ingest', {method: 'POST', body: formData})
105+
// Submit the form to /api/ingest as JSON
106+
fetch('/api/ingest', {
107+
method: 'POST',
108+
headers: { 'Content-Type': 'application/json' },
109+
body: JSON.stringify(data)
110+
})
112111
.then(response => response.json())
113112
.then(data => {
114113
// Hide loading overlay

tests/test_flow_integration.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ async def test_remote_repository_analysis(request: pytest.FixtureRequest) -> Non
5656
"token": "",
5757
}
5858

59-
response = client.post("/api/ingest", data=form_data)
59+
response = client.post("/api/ingest", json=form_data)
6060
assert response.status_code == status.HTTP_200_OK, f"Form submission failed: {response.text}"
6161

6262
# Check that response is JSON
@@ -81,7 +81,7 @@ async def test_invalid_repository_url(request: pytest.FixtureRequest) -> None:
8181
"token": "",
8282
}
8383

84-
response = client.post("/api/ingest", data=form_data)
84+
response = client.post("/api/ingest", json=form_data)
8585
# Should return 400 for invalid repository
8686
assert response.status_code == status.HTTP_400_BAD_REQUEST, f"Request failed: {response.text}"
8787

@@ -104,7 +104,7 @@ async def test_large_repository(request: pytest.FixtureRequest) -> None:
104104
"token": "",
105105
}
106106

107-
response = client.post("/api/ingest", data=form_data)
107+
response = client.post("/api/ingest", json=form_data)
108108
assert response.status_code == status.HTTP_200_OK, f"Request failed: {response.text}"
109109

110110
response_data = response.json()
@@ -128,7 +128,7 @@ def make_request() -> None:
128128
"pattern": "",
129129
"token": "",
130130
}
131-
response = client.post("/api/ingest", data=form_data)
131+
response = client.post("/api/ingest", json=form_data)
132132
assert response.status_code == status.HTTP_200_OK, f"Request failed: {response.text}"
133133

134134
response_data = response.json()
@@ -156,7 +156,7 @@ async def test_large_file_handling(request: pytest.FixtureRequest) -> None:
156156
"token": "",
157157
}
158158

159-
response = client.post("/api/ingest", data=form_data)
159+
response = client.post("/api/ingest", json=form_data)
160160
assert response.status_code == status.HTTP_200_OK, f"Request failed: {response.text}"
161161

162162
response_data = response.json()
@@ -179,7 +179,7 @@ async def test_repository_with_patterns(request: pytest.FixtureRequest) -> None:
179179
"token": "",
180180
}
181181

182-
response = client.post("/api/ingest", data=form_data)
182+
response = client.post("/api/ingest", json=form_data)
183183
assert response.status_code == status.HTTP_200_OK, f"Request failed: {response.text}"
184184

185185
response_data = response.json()

0 commit comments

Comments
 (0)