openapi-spec-generator 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openapi_generator/__init__.py +1 -0
- openapi_generator/ai_client.py +22 -0
- openapi_generator/cli.py +31 -0
- openapi_generator/file_collector.py +37 -0
- openapi_generator/generator.py +49 -0
- openapi_spec_generator-0.1.0.dist-info/METADATA +98 -0
- openapi_spec_generator-0.1.0.dist-info/RECORD +10 -0
- openapi_spec_generator-0.1.0.dist-info/WHEEL +5 -0
- openapi_spec_generator-0.1.0.dist-info/entry_points.txt +2 -0
- openapi_spec_generator-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
def call_ai(prompt: str, api_key: str, provider: str) -> str:
|
|
2
|
+
if provider == "claude":
|
|
3
|
+
import anthropic
|
|
4
|
+
|
|
5
|
+
client = anthropic.Anthropic(api_key=api_key)
|
|
6
|
+
with client.messages.stream(
|
|
7
|
+
model="claude-sonnet-4-6",
|
|
8
|
+
max_tokens=4096,
|
|
9
|
+
messages=[{"role": "user", "content": prompt}],
|
|
10
|
+
) as stream:
|
|
11
|
+
return stream.get_final_message().content[0].text
|
|
12
|
+
elif provider == "openai":
|
|
13
|
+
import openai
|
|
14
|
+
|
|
15
|
+
client = openai.OpenAI(api_key=api_key)
|
|
16
|
+
response = client.chat.completions.create(
|
|
17
|
+
model="gpt-4o",
|
|
18
|
+
messages=[{"role": "user", "content": prompt}],
|
|
19
|
+
)
|
|
20
|
+
return response.choices[0].message.content
|
|
21
|
+
else:
|
|
22
|
+
raise ValueError(f"Unknown provider '{provider}'. Choose 'claude' or 'openai'.")
|
openapi_generator/cli.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import click
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from .generator import generate_spec
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@click.command()
|
|
8
|
+
@click.argument("path", type=click.Path(exists=True, file_okay=False, path_type=Path))
|
|
9
|
+
@click.option("--output", "-o", default="openapi.json", show_default=True, help="Output file path.")
|
|
10
|
+
@click.option("--title", default="API", show_default=True, help="API title.")
|
|
11
|
+
@click.option("--version", "api_version", default="1.0.0", show_default=True, help="API version.")
|
|
12
|
+
@click.option(
|
|
13
|
+
"--api-key",
|
|
14
|
+
envvar="OPENAPI_GEN_API_KEY",
|
|
15
|
+
required=True,
|
|
16
|
+
help="AI provider API key (or set OPENAPI_GEN_API_KEY).",
|
|
17
|
+
)
|
|
18
|
+
@click.option(
|
|
19
|
+
"--provider",
|
|
20
|
+
type=click.Choice(["claude", "openai"]),
|
|
21
|
+
default="claude",
|
|
22
|
+
show_default=True,
|
|
23
|
+
help="AI provider to use.",
|
|
24
|
+
)
|
|
25
|
+
def main(path: Path, output: str, title: str, api_version: str, api_key: str, provider: str) -> None:
|
|
26
|
+
"""Generate an OpenAPI specification for a REST API project directory."""
|
|
27
|
+
click.echo(f"Scanning {path} ...")
|
|
28
|
+
spec = generate_spec(path, title=title, api_version=api_version, api_key=api_key, provider=provider)
|
|
29
|
+
output_path = Path(output)
|
|
30
|
+
output_path.write_text(json.dumps(spec, indent=2), encoding="utf-8")
|
|
31
|
+
click.echo(f"Wrote JSON spec to {output_path}")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
_INCLUDE_EXTENSIONS = {".py", ".js", ".ts", ".go", ".java", ".rb", ".php", ".cs"}
|
|
5
|
+
_SKIP_DIRS = {"node_modules", ".git", "__pycache__", "venv", ".venv", "dist", "build", ".eggs"}
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def collect_files(project_dir: Path, max_bytes: int = 400_000) -> list[tuple[str, str]]:
|
|
9
|
+
"""Walk *project_dir* and return (relative_path, content) for each source file.
|
|
10
|
+
|
|
11
|
+
Stops collecting once the total accumulated size exceeds *max_bytes* to
|
|
12
|
+
avoid exceeding AI model context limits.
|
|
13
|
+
"""
|
|
14
|
+
results: list[tuple[str, str]] = []
|
|
15
|
+
total = 0
|
|
16
|
+
|
|
17
|
+
for root, dirs, files in os.walk(project_dir):
|
|
18
|
+
dirs[:] = [d for d in dirs if d not in _SKIP_DIRS]
|
|
19
|
+
|
|
20
|
+
for filename in sorted(files):
|
|
21
|
+
if Path(filename).suffix not in _INCLUDE_EXTENSIONS:
|
|
22
|
+
continue
|
|
23
|
+
|
|
24
|
+
abs_path = Path(root) / filename
|
|
25
|
+
try:
|
|
26
|
+
content = abs_path.read_text(encoding="utf-8")
|
|
27
|
+
except (UnicodeDecodeError, OSError):
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
total += len(content.encode("utf-8"))
|
|
31
|
+
if total > max_bytes:
|
|
32
|
+
return results
|
|
33
|
+
|
|
34
|
+
rel_path = abs_path.relative_to(project_dir).as_posix()
|
|
35
|
+
results.append((rel_path, content))
|
|
36
|
+
|
|
37
|
+
return results
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import re
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from .file_collector import collect_files
|
|
6
|
+
from .ai_client import call_ai
|
|
7
|
+
|
|
8
|
+
_SYSTEM_PROMPT = (
|
|
9
|
+
"You are an expert OpenAPI specification writer. "
|
|
10
|
+
"Given source files from a REST API project, produce a complete, valid "
|
|
11
|
+
"OpenAPI 3.0.3 specification in JSON. "
|
|
12
|
+
"Respond with ONLY the raw JSON — no markdown fences, no explanation, no commentary."
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _build_prompt(files: list[tuple[str, str]], title: str, api_version: str) -> str:
|
|
17
|
+
file_blocks = "\n\n".join(
|
|
18
|
+
f"### {path}\n{content}" for path, content in files
|
|
19
|
+
)
|
|
20
|
+
return (
|
|
21
|
+
f"{_SYSTEM_PROMPT}\n\n"
|
|
22
|
+
f"API title: {title}\n"
|
|
23
|
+
f"API version: {api_version}\n\n"
|
|
24
|
+
f"Source files:\n\n{file_blocks}"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _parse_json(raw: str) -> dict:
|
|
29
|
+
text = raw.strip()
|
|
30
|
+
text = re.sub(r"^```(?:json)?\s*", "", text)
|
|
31
|
+
text = re.sub(r"\s*```$", "", text)
|
|
32
|
+
try:
|
|
33
|
+
return json.loads(text)
|
|
34
|
+
except json.JSONDecodeError as exc:
|
|
35
|
+
raise ValueError(f"AI response was not valid JSON:\n{raw[:500]}") from exc
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def generate_spec(
|
|
39
|
+
project_dir: Path,
|
|
40
|
+
*,
|
|
41
|
+
title: str = "API",
|
|
42
|
+
api_version: str = "1.0.0",
|
|
43
|
+
api_key: str,
|
|
44
|
+
provider: str = "claude",
|
|
45
|
+
) -> dict:
|
|
46
|
+
files = collect_files(project_dir)
|
|
47
|
+
prompt = _build_prompt(files, title, api_version)
|
|
48
|
+
raw = call_ai(prompt, api_key, provider)
|
|
49
|
+
return _parse_json(raw)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openapi-spec-generator
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Generate OpenAPI specifications for any REST API project directory.
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: click>=8.0
|
|
9
|
+
Provides-Extra: claude
|
|
10
|
+
Requires-Dist: anthropic>=0.25; extra == "claude"
|
|
11
|
+
Provides-Extra: openai
|
|
12
|
+
Requires-Dist: openai>=1.0; extra == "openai"
|
|
13
|
+
Provides-Extra: all
|
|
14
|
+
Requires-Dist: anthropic>=0.25; extra == "all"
|
|
15
|
+
Requires-Dist: openai>=1.0; extra == "all"
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
19
|
+
|
|
20
|
+
# OpenAPI Generator
|
|
21
|
+
|
|
22
|
+
A pip-installable CLI tool that uses AI to generate OpenAPI 3.0.3 specifications for any REST API project directory.
|
|
23
|
+
|
|
24
|
+
## Overview
|
|
25
|
+
|
|
26
|
+
Point this tool at any REST API project and it will scan the source files, send them to an AI model (Claude or OpenAI), and produce a valid OpenAPI 3.0.3 JSON specification.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# With Claude support (default)
|
|
32
|
+
pip install "openapi-generator[claude]"
|
|
33
|
+
|
|
34
|
+
# With OpenAI support
|
|
35
|
+
pip install "openapi-generator[openai]"
|
|
36
|
+
|
|
37
|
+
# Both providers
|
|
38
|
+
pip install "openapi-generator[all]"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
openapi-gen <path-to-project> --api-key <your-api-key>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The API key can also be provided via the `OPENAPI_GEN_API_KEY` environment variable:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
export OPENAPI_GEN_API_KEY=sk-ant-...
|
|
51
|
+
openapi-gen <path-to-project>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Options
|
|
55
|
+
|
|
56
|
+
| Option | Default | Description |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `<path>` | _(required)_ | Path to the REST API project directory |
|
|
59
|
+
| `--api-key` | `OPENAPI_GEN_API_KEY` env var | AI provider API key |
|
|
60
|
+
| `--provider` | `claude` | AI provider: `claude` or `openai` |
|
|
61
|
+
| `--output`, `-o` | `openapi.json` | Output file path |
|
|
62
|
+
| `--title` | `API` | API title in the spec |
|
|
63
|
+
| `--version` | `1.0.0` | API version in the spec |
|
|
64
|
+
|
|
65
|
+
### Examples
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Generate a spec using Claude (default)
|
|
69
|
+
export OPENAPI_GEN_API_KEY=sk-ant-...
|
|
70
|
+
openapi-gen ./my-api
|
|
71
|
+
|
|
72
|
+
# Generate a spec using OpenAI
|
|
73
|
+
openapi-gen ./my-api --provider openai --api-key sk-...
|
|
74
|
+
|
|
75
|
+
# Specify title, version, and output path
|
|
76
|
+
openapi-gen ./my-api --title "My REST API" --version "2.0.0" --output ./docs/openapi.json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Output
|
|
80
|
+
|
|
81
|
+
The generated file follows the [OpenAPI 3.0.3 specification](https://spec.openapis.org/oas/v3.0.3) and can be used directly with tools like Swagger UI, Postman, or any OpenAPI-compatible client generator.
|
|
82
|
+
|
|
83
|
+
## Supported Languages
|
|
84
|
+
|
|
85
|
+
The tool scans files with the following extensions: `.py`, `.js`, `.ts`, `.go`, `.java`, `.rb`, `.php`, `.cs`
|
|
86
|
+
|
|
87
|
+
## Development
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
git clone https://github.com/your-org/openapi-generator
|
|
91
|
+
cd openapi-generator
|
|
92
|
+
pip install -e ".[dev]"
|
|
93
|
+
pytest
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
openapi_generator/__init__.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
|
|
2
|
+
openapi_generator/ai_client.py,sha256=LaQx54jXZRDJauXQ8vFrt8e8FYemcnTXAfU6XIwTO0g,820
|
|
3
|
+
openapi_generator/cli.py,sha256=TuNjNwLauYYKm0QPA1kY1KAENvRR81NCbezPECsOULs,1257
|
|
4
|
+
openapi_generator/file_collector.py,sha256=k74-ZtjyrB7Ojz-nYyVKMYCBJCuIm-n_fqBvGkXp3Ig,1268
|
|
5
|
+
openapi_generator/generator.py,sha256=myz-MHVHXzka7jnrKb6PFmTeroFCJznrb-G2Wb8gteM,1408
|
|
6
|
+
openapi_spec_generator-0.1.0.dist-info/METADATA,sha256=LafjnZWoBsW6AlG7z-jOj1aI2Q763Gb6ptLPx_kxmcE,2773
|
|
7
|
+
openapi_spec_generator-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
8
|
+
openapi_spec_generator-0.1.0.dist-info/entry_points.txt,sha256=hFqC1pfJAr_WhteyW7DzVwhWdMfvo8g3Qb7p-u5udCE,59
|
|
9
|
+
openapi_spec_generator-0.1.0.dist-info/top_level.txt,sha256=YODEqCx34asgokWpD2p-Sm7tbt4Fw0Vk8W3eKfcvPAw,18
|
|
10
|
+
openapi_spec_generator-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
openapi_generator
|