agentr 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.
- agentr/__init__.py +2 -0
- agentr/cli.py +29 -0
- agentr/py.typed +0 -0
- agentr/server.py +0 -0
- agentr/utils/openapi.py +185 -0
- agentr-0.1.0.dist-info/METADATA +8 -0
- agentr-0.1.0.dist-info/RECORD +9 -0
- agentr-0.1.0.dist-info/WHEEL +4 -0
- agentr-0.1.0.dist-info/entry_points.txt +2 -0
agentr/__init__.py
ADDED
agentr/cli.py
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
import typer
|
2
|
+
import yaml
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
app = typer.Typer()
|
6
|
+
|
7
|
+
@app.command()
|
8
|
+
def version():
|
9
|
+
"""Show the version of the CLI"""
|
10
|
+
print("agentr version 0.1.0")
|
11
|
+
|
12
|
+
@app.command()
|
13
|
+
def generate(schema_path: Path = typer.Option(..., "--schema", "-s")):
|
14
|
+
"""Generate API client from OpenAPI schema"""
|
15
|
+
if not schema_path.exists():
|
16
|
+
typer.echo(f"Error: Schema file {schema_path} does not exist", err=True)
|
17
|
+
raise typer.Exit(1)
|
18
|
+
from .utils.openapi import generate_api_client, load_schema
|
19
|
+
|
20
|
+
try:
|
21
|
+
schema = load_schema(schema_path)
|
22
|
+
except Exception as e:
|
23
|
+
typer.echo(f"Error loading schema: {e}", err=True)
|
24
|
+
raise typer.Exit(1)
|
25
|
+
code = generate_api_client(schema)
|
26
|
+
print(code)
|
27
|
+
|
28
|
+
if __name__ == "__main__":
|
29
|
+
app()
|
agentr/py.typed
ADDED
File without changes
|
agentr/server.py
ADDED
File without changes
|
agentr/utils/openapi.py
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
import json
|
2
|
+
import yaml
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
def load_schema(path: Path):
|
6
|
+
if path.suffix == '.yaml':
|
7
|
+
type = 'yaml'
|
8
|
+
else:
|
9
|
+
type = 'json'
|
10
|
+
with open(path, 'r') as f:
|
11
|
+
if type == 'yaml':
|
12
|
+
return yaml.safe_load(f)
|
13
|
+
else:
|
14
|
+
return json.load(f)
|
15
|
+
|
16
|
+
def generate_api_client(schema):
|
17
|
+
"""
|
18
|
+
Generate a Python API client class from an OpenAPI schema.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
schema (dict): The OpenAPI schema as a dictionary.
|
22
|
+
|
23
|
+
Returns:
|
24
|
+
str: A string containing the Python code for the API client class.
|
25
|
+
"""
|
26
|
+
methods = []
|
27
|
+
|
28
|
+
# Iterate over paths and their operations
|
29
|
+
for path, path_info in schema.get('paths', {}).items():
|
30
|
+
for method in path_info:
|
31
|
+
if method in ['get', 'post', 'put', 'delete', 'patch', 'options', 'head']:
|
32
|
+
operation = path_info[method]
|
33
|
+
method_code = generate_method_code(path, method, operation)
|
34
|
+
methods.append(method_code)
|
35
|
+
|
36
|
+
# Construct the class code
|
37
|
+
class_code = (
|
38
|
+
"import requests\n\n"
|
39
|
+
"class APIClient:\n"
|
40
|
+
" def __init__(self, base_url):\n"
|
41
|
+
" self.base_url = base_url\n\n" +
|
42
|
+
'\n\n'.join(methods)
|
43
|
+
)
|
44
|
+
return class_code
|
45
|
+
|
46
|
+
def generate_method_code(path, method, operation):
|
47
|
+
"""
|
48
|
+
Generate the code for a single API method.
|
49
|
+
|
50
|
+
Args:
|
51
|
+
path (str): The API path (e.g., '/users/{user_id}').
|
52
|
+
method (str): The HTTP method (e.g., 'get').
|
53
|
+
operation (dict): The operation details from the schema.
|
54
|
+
|
55
|
+
Returns:
|
56
|
+
str: The Python code for the method.
|
57
|
+
"""
|
58
|
+
# Determine function name
|
59
|
+
if 'operationId' in operation:
|
60
|
+
func_name = operation['operationId']
|
61
|
+
else:
|
62
|
+
# Generate name from path and method
|
63
|
+
path_parts = path.strip('/').split('/')
|
64
|
+
name_parts = [method]
|
65
|
+
for part in path_parts:
|
66
|
+
if part.startswith('{') and part.endswith('}'):
|
67
|
+
name_parts.append('by_' + part[1:-1])
|
68
|
+
else:
|
69
|
+
name_parts.append(part)
|
70
|
+
func_name = '_'.join(name_parts).replace('-', '_').lower()
|
71
|
+
|
72
|
+
# Get parameters and request body
|
73
|
+
parameters = operation.get('parameters', [])
|
74
|
+
has_body = 'requestBody' in operation
|
75
|
+
body_required = has_body and operation['requestBody'].get('required', False)
|
76
|
+
|
77
|
+
# Build function arguments
|
78
|
+
args = []
|
79
|
+
for param in parameters:
|
80
|
+
if param.get('required', False):
|
81
|
+
args.append(param['name'])
|
82
|
+
else:
|
83
|
+
args.append(f"{param['name']}=None")
|
84
|
+
if has_body:
|
85
|
+
args.append('body' if body_required else 'body=None')
|
86
|
+
signature = f"def {func_name}(self, {', '.join(args)}):"
|
87
|
+
|
88
|
+
# Build method body
|
89
|
+
body_lines = []
|
90
|
+
|
91
|
+
# Path parameters
|
92
|
+
path_params = [p for p in parameters if p['in'] == 'path']
|
93
|
+
path_params_dict = ', '.join([f"'{p['name']}': {p['name']}" for p in path_params])
|
94
|
+
body_lines.append(f" path_params = {{{path_params_dict}}}")
|
95
|
+
|
96
|
+
# Query parameters
|
97
|
+
query_params = [p for p in parameters if p['in'] == 'query']
|
98
|
+
query_params_items = ', '.join([f"('{p['name']}', {p['name']})" for p in query_params])
|
99
|
+
body_lines.append(
|
100
|
+
f" query_params = {{k: v for k, v in [{query_params_items}] if v is not None}}"
|
101
|
+
)
|
102
|
+
|
103
|
+
# Format URL
|
104
|
+
body_lines.append(f" url = f\"{{self.base_url}}{path}\".format_map(path_params)")
|
105
|
+
|
106
|
+
# Make HTTP request
|
107
|
+
method_func = method.lower()
|
108
|
+
if has_body:
|
109
|
+
body_lines.append(" if body is not None:")
|
110
|
+
body_lines.append(f" response = requests.{method_func}(url, params=query_params, json=body)")
|
111
|
+
body_lines.append(" else:")
|
112
|
+
body_lines.append(f" response = requests.{method_func}(url, params=query_params)")
|
113
|
+
else:
|
114
|
+
body_lines.append(f" response = requests.{method_func}(url, params=query_params)")
|
115
|
+
|
116
|
+
# Handle response
|
117
|
+
body_lines.append(" response.raise_for_status()")
|
118
|
+
body_lines.append(" return response.json()")
|
119
|
+
|
120
|
+
return signature + '\n' + '\n'.join(body_lines)
|
121
|
+
|
122
|
+
# Example usage
|
123
|
+
if __name__ == "__main__":
|
124
|
+
# Sample OpenAPI schema
|
125
|
+
schema = {
|
126
|
+
"paths": {
|
127
|
+
"/users": {
|
128
|
+
"get": {
|
129
|
+
"summary": "Get a list of users",
|
130
|
+
"parameters": [
|
131
|
+
{
|
132
|
+
"name": "limit",
|
133
|
+
"in": "query",
|
134
|
+
"required": False,
|
135
|
+
"schema": {"type": "integer"}
|
136
|
+
}
|
137
|
+
],
|
138
|
+
"responses": {
|
139
|
+
"200": {
|
140
|
+
"description": "A list of users",
|
141
|
+
"content": {"application/json": {"schema": {"type": "array"}}}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
},
|
145
|
+
"post": {
|
146
|
+
"summary": "Create a user",
|
147
|
+
"requestBody": {
|
148
|
+
"required": True,
|
149
|
+
"content": {
|
150
|
+
"application/json": {
|
151
|
+
"schema": {
|
152
|
+
"type": "object",
|
153
|
+
"properties": {"name": {"type": "string"}}
|
154
|
+
}
|
155
|
+
}
|
156
|
+
}
|
157
|
+
},
|
158
|
+
"responses": {
|
159
|
+
"201": {"description": "User created"}
|
160
|
+
}
|
161
|
+
}
|
162
|
+
},
|
163
|
+
"/users/{user_id}": {
|
164
|
+
"get": {
|
165
|
+
"summary": "Get a user by ID",
|
166
|
+
"parameters": [
|
167
|
+
{
|
168
|
+
"name": "user_id",
|
169
|
+
"in": "path",
|
170
|
+
"required": True,
|
171
|
+
"schema": {"type": "string"}
|
172
|
+
}
|
173
|
+
],
|
174
|
+
"responses": {
|
175
|
+
"200": {"description": "User details"}
|
176
|
+
}
|
177
|
+
}
|
178
|
+
}
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
|
183
|
+
schema = load_schema('openapi.yaml')
|
184
|
+
code = generate_api_client(schema)
|
185
|
+
print(code)
|
@@ -0,0 +1,9 @@
|
|
1
|
+
agentr/__init__.py,sha256=LOWhgQayrQV7f5ro4rlBJ_6WevhbWIbjAOHnqP7b_-4,30
|
2
|
+
agentr/cli.py,sha256=6vm9USDbtWBMuvnrBmSHehqGgTsAGpXmNWBxMNWIu-8,776
|
3
|
+
agentr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
+
agentr/server.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
agentr/utils/openapi.py,sha256=yjiPYs-_JsYQ7T3aPh7oimHUCf8pMblIR8IPCaAeNCg,6279
|
6
|
+
agentr-0.1.0.dist-info/METADATA,sha256=1juL-iAF7qM0JpG8WdSzMyFreouhD2m2BWyg6z_mScU,218
|
7
|
+
agentr-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
8
|
+
agentr-0.1.0.dist-info/entry_points.txt,sha256=13fGFeVhgF6_8T-VFiIkNxYO7gDQaUwwTcUNWdvaLQg,42
|
9
|
+
agentr-0.1.0.dist-info/RECORD,,
|