aristotlelib 0.1.0__tar.gz
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.
- aristotlelib-0.1.0/LICENSE +15 -0
- aristotlelib-0.1.0/PKG-INFO +259 -0
- aristotlelib-0.1.0/README.md +245 -0
- aristotlelib-0.1.0/pyproject.toml +24 -0
- aristotlelib-0.1.0/setup.cfg +4 -0
- aristotlelib-0.1.0/src/aristotlelib/__init__.py +10 -0
- aristotlelib-0.1.0/src/aristotlelib/api_request.py +129 -0
- aristotlelib-0.1.0/src/aristotlelib/local_file_utils.py +421 -0
- aristotlelib-0.1.0/src/aristotlelib/project.py +371 -0
- aristotlelib-0.1.0/src/aristotlelib.egg-info/PKG-INFO +259 -0
- aristotlelib-0.1.0/src/aristotlelib.egg-info/SOURCES.txt +12 -0
- aristotlelib-0.1.0/src/aristotlelib.egg-info/dependency_links.txt +1 -0
- aristotlelib-0.1.0/src/aristotlelib.egg-info/requires.txt +2 -0
- aristotlelib-0.1.0/src/aristotlelib.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Copyright (c) 2025 Concordance Inc. dba Harmonic. All rights reserved.
|
|
2
|
+
|
|
3
|
+
Concordance Inc. dba Harmonic grants you a limited, non-exclusive, non-transferable,
|
|
4
|
+
revocable license to install and use this software solely to access the Aristotle
|
|
5
|
+
API under a valid API key and in accordance with the Aristotle API Terms of Service
|
|
6
|
+
available at https://aristotle.harmonic.fun/api-terms-of-use
|
|
7
|
+
|
|
8
|
+
You may not modify, distribute, sublicense, or reverse-engineer this software,
|
|
9
|
+
except as expressly permitted by Concordance Inc. dba Harmonic in writing.
|
|
10
|
+
|
|
11
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
12
|
+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
13
|
+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
|
|
14
|
+
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
15
|
+
CONTRACT, TORT OR OTHERWISE, ARISING FROM OR RELATING TO THE SOFTWARE OR ITS USE.
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aristotlelib
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Aristotle SDK - Python library for automated theorem proving with Lean
|
|
5
|
+
Author: Harmonic
|
|
6
|
+
Maintainer: Harmonic
|
|
7
|
+
Project-URL: Homepage, https://aristotle.harmonic.fun
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: pydantic>=2.0.0
|
|
12
|
+
Requires-Dist: httpx>=0.24.0
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# Aristotle SDK
|
|
16
|
+
|
|
17
|
+
The Aristotle SDK is a Python library that provides tools and utilities for interacting with the Aristotle API, enabling automated theorem proving for Lean projects.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install aristotlelib
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### 1. Set up your API key
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import aristotlelib
|
|
32
|
+
|
|
33
|
+
# Set your API key
|
|
34
|
+
aristotlelib.set_api_key("your-api-key-here")
|
|
35
|
+
|
|
36
|
+
# Or set it via environment variable
|
|
37
|
+
# export ARISTOTLE_API_KEY="your-api-key-here"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. Prove a theorem from a file
|
|
41
|
+
|
|
42
|
+
The simplest way to use Aristotle is to prove a theorem from a Lean file:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import asyncio
|
|
46
|
+
|
|
47
|
+
async def main():
|
|
48
|
+
# Prove a theorem from a Lean file
|
|
49
|
+
solution_path = await aristotlelib.Project.prove_from_file("path/to/your/theorem.lean")
|
|
50
|
+
print(f"Solution saved to: {solution_path}")
|
|
51
|
+
|
|
52
|
+
asyncio.run(main())
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 3. Manual project management
|
|
56
|
+
|
|
57
|
+
For more control, you can manage projects manually:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
import asyncio
|
|
61
|
+
import aristotlelib
|
|
62
|
+
from pathlib import Path
|
|
63
|
+
|
|
64
|
+
async def main():
|
|
65
|
+
# Create a new project
|
|
66
|
+
project = await aristotlelib.Project.create()
|
|
67
|
+
print(f"Created project: {project.project_id}")
|
|
68
|
+
|
|
69
|
+
# Add context files
|
|
70
|
+
await project.add_context(["path/to/context1.lean", "path/to/context2.lean"])
|
|
71
|
+
|
|
72
|
+
# Solve with input content
|
|
73
|
+
await project.solve(input_content="theorem my_theorem : True := trivial")
|
|
74
|
+
|
|
75
|
+
# Wait for completion and get solution
|
|
76
|
+
while project.status not in [aristotlelib.ProjectStatus.COMPLETE, aristotlelib.ProjectStatus.FAILED]:
|
|
77
|
+
await asyncio.sleep(30) # Poll every 30 seconds
|
|
78
|
+
await project.refresh()
|
|
79
|
+
print(f"Status: {project.status}")
|
|
80
|
+
|
|
81
|
+
if project.status == aristotlelib.ProjectStatus.COMPLETE:
|
|
82
|
+
solution_path = await project.get_solution()
|
|
83
|
+
print(f"Solution saved to: {solution_path}")
|
|
84
|
+
|
|
85
|
+
asyncio.run(main())
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## API Reference
|
|
89
|
+
|
|
90
|
+
### Project Class
|
|
91
|
+
|
|
92
|
+
The main class for interacting with Aristotle projects.
|
|
93
|
+
|
|
94
|
+
#### `Project.create(context_file_paths=None, validate_lean_project_root=True)`
|
|
95
|
+
|
|
96
|
+
Create a new Aristotle project.
|
|
97
|
+
|
|
98
|
+
**Parameters:**
|
|
99
|
+
- `context_file_paths` (list[Path | str], optional): List of file paths to include as context
|
|
100
|
+
- `validate_lean_project_root` (bool): Whether to validate Lean project structure (recommended: True)
|
|
101
|
+
|
|
102
|
+
**Returns:** `Project` instance
|
|
103
|
+
|
|
104
|
+
#### `Project.prove_from_file(input_file_path, auto_add_imports=True, context_file_paths=None, validate_lean_project=True, polling_interval_seconds=30, max_polling_failures=3)`
|
|
105
|
+
|
|
106
|
+
Convenience method to prove a theorem from a file with automatic import resolution.
|
|
107
|
+
|
|
108
|
+
**Parameters:**
|
|
109
|
+
- `input_file_path` (Path | str): Path to the input Lean file
|
|
110
|
+
- `auto_add_imports` (bool): Automatically add imported files as context
|
|
111
|
+
- `context_file_paths` (list[Path | str], optional): Manual context files
|
|
112
|
+
- `validate_lean_project` (bool): Validate Lean project structure
|
|
113
|
+
- `polling_interval_seconds` (int): Seconds between status checks
|
|
114
|
+
- `max_polling_failures` (int): Max polling failures before giving up
|
|
115
|
+
|
|
116
|
+
**Returns:** `Path` to the solution file
|
|
117
|
+
|
|
118
|
+
#### `project.add_context(context_file_paths, batch_size=10, validate_lean_project_root=True)`
|
|
119
|
+
|
|
120
|
+
Add context files to an existing project.
|
|
121
|
+
|
|
122
|
+
**Parameters:**
|
|
123
|
+
- `context_file_paths` (list[Path | str]): Files to add as context
|
|
124
|
+
- `batch_size` (int): Files to upload per batch (max 10)
|
|
125
|
+
- `validate_lean_project_root` (bool): Validate project structure
|
|
126
|
+
|
|
127
|
+
#### `project.solve(input_file_path=None, input_content=None)`
|
|
128
|
+
|
|
129
|
+
Solve the project with either a file or text content.
|
|
130
|
+
|
|
131
|
+
**Parameters:**
|
|
132
|
+
- `input_file_path` (Path | str, optional): Path to input file
|
|
133
|
+
- `input_content` (str, optional): Text content to solve
|
|
134
|
+
|
|
135
|
+
**Note:** Exactly one of `input_file_path` or `input_content` must be provided.
|
|
136
|
+
|
|
137
|
+
#### `project.get_solution(output_path=None)`
|
|
138
|
+
|
|
139
|
+
Download the solution file.
|
|
140
|
+
|
|
141
|
+
**Parameters:**
|
|
142
|
+
- `output_path` (Path | str, optional): Where to save the solution
|
|
143
|
+
|
|
144
|
+
**Returns:** `Path` to the downloaded solution file
|
|
145
|
+
|
|
146
|
+
#### `project.refresh()`
|
|
147
|
+
|
|
148
|
+
Refresh the project status from the API.
|
|
149
|
+
|
|
150
|
+
### Project Status
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
class ProjectStatus(Enum):
|
|
154
|
+
NOT_STARTED = "NOT_STARTED"
|
|
155
|
+
QUEUED = "QUEUED"
|
|
156
|
+
IN_PROGRESS = "IN_PROGRESS"
|
|
157
|
+
COMPLETE = "COMPLETE"
|
|
158
|
+
FAILED = "FAILED"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Error Handling
|
|
162
|
+
|
|
163
|
+
The SDK provides several exception types:
|
|
164
|
+
|
|
165
|
+
- `AristotleAPIError`: API-related errors
|
|
166
|
+
- `LeanProjectError`: Lean project validation errors
|
|
167
|
+
|
|
168
|
+
## Lean Project Requirements
|
|
169
|
+
|
|
170
|
+
Aristotle works best with properly structured Lean projects. Your project should have:
|
|
171
|
+
|
|
172
|
+
- A `lakefile.lean` (Lake build system) or `leanpkg.toml` (legacy)
|
|
173
|
+
- A `lean-toolchain` file
|
|
174
|
+
- Proper import structure
|
|
175
|
+
|
|
176
|
+
The SDK will automatically:
|
|
177
|
+
- Detect your project root
|
|
178
|
+
- Validate file paths are within the project
|
|
179
|
+
- Resolve imports to include dependencies
|
|
180
|
+
- Handle file size limits (100MB max per file)
|
|
181
|
+
|
|
182
|
+
## Examples
|
|
183
|
+
|
|
184
|
+
### Basic theorem proving
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
import asyncio
|
|
188
|
+
import aristotle
|
|
189
|
+
|
|
190
|
+
async def prove_simple_theorem():
|
|
191
|
+
# Set API key
|
|
192
|
+
aristotlelib.set_api_key("your-key")
|
|
193
|
+
|
|
194
|
+
# Prove a simple theorem
|
|
195
|
+
solution = await aristotlelib.Project.prove_from_file("examples/simple.lean")
|
|
196
|
+
print(f"Proof completed: {solution}")
|
|
197
|
+
|
|
198
|
+
asyncio.run(prove_simple_theorem())
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Working with existing projects
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
import asyncio
|
|
205
|
+
import aristotle
|
|
206
|
+
|
|
207
|
+
async def work_with_existing_project():
|
|
208
|
+
# Load an existing project
|
|
209
|
+
project = await aristotlelib.Project.from_id("existing-project-id")
|
|
210
|
+
|
|
211
|
+
# Check status
|
|
212
|
+
print(f"Project status: {project.status}")
|
|
213
|
+
|
|
214
|
+
if project.status == aristotlelib.ProjectStatus.COMPLETE:
|
|
215
|
+
solution = await project.get_solution()
|
|
216
|
+
print(f"Solution available at: {solution}")
|
|
217
|
+
|
|
218
|
+
asyncio.run(work_with_existing_project())
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Listing projects
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
import asyncio
|
|
225
|
+
import aristotle
|
|
226
|
+
|
|
227
|
+
async def list_projects():
|
|
228
|
+
projects, pagination_key = await aristotlelib.Project.list_projects(limit=10)
|
|
229
|
+
|
|
230
|
+
for project in projects:
|
|
231
|
+
print(f"Project {project.project_id}: {project.status}")
|
|
232
|
+
|
|
233
|
+
# Get next page if available
|
|
234
|
+
if pagination_key:
|
|
235
|
+
more_projects, pagination_key = await aristotlelib.Project.list_projects(pagination_key=pagination_key)
|
|
236
|
+
print(f"Found {len(more_projects)} more projects")
|
|
237
|
+
|
|
238
|
+
asyncio.run(list_projects())
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Logging
|
|
242
|
+
|
|
243
|
+
The SDK uses Python's standard logging module. To see debug and info messages from the SDK, configure logging in your application:
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
import logging
|
|
247
|
+
import aristotle
|
|
248
|
+
|
|
249
|
+
# Configure logging to see SDK messages
|
|
250
|
+
logging.basicConfig(
|
|
251
|
+
level=logging.INFO,
|
|
252
|
+
format="%(levelname)s - %(name)s - %(message)s"
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
# Or configure just the aristotle logger
|
|
256
|
+
logging.getLogger('aristotle').setLevel(logging.INFO)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
This will show helpful messages to track the current status.
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Aristotle SDK
|
|
2
|
+
|
|
3
|
+
The Aristotle SDK is a Python library that provides tools and utilities for interacting with the Aristotle API, enabling automated theorem proving for Lean projects.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install aristotlelib
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
### 1. Set up your API key
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
import aristotlelib
|
|
18
|
+
|
|
19
|
+
# Set your API key
|
|
20
|
+
aristotlelib.set_api_key("your-api-key-here")
|
|
21
|
+
|
|
22
|
+
# Or set it via environment variable
|
|
23
|
+
# export ARISTOTLE_API_KEY="your-api-key-here"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 2. Prove a theorem from a file
|
|
27
|
+
|
|
28
|
+
The simplest way to use Aristotle is to prove a theorem from a Lean file:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import asyncio
|
|
32
|
+
|
|
33
|
+
async def main():
|
|
34
|
+
# Prove a theorem from a Lean file
|
|
35
|
+
solution_path = await aristotlelib.Project.prove_from_file("path/to/your/theorem.lean")
|
|
36
|
+
print(f"Solution saved to: {solution_path}")
|
|
37
|
+
|
|
38
|
+
asyncio.run(main())
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 3. Manual project management
|
|
42
|
+
|
|
43
|
+
For more control, you can manage projects manually:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import asyncio
|
|
47
|
+
import aristotlelib
|
|
48
|
+
from pathlib import Path
|
|
49
|
+
|
|
50
|
+
async def main():
|
|
51
|
+
# Create a new project
|
|
52
|
+
project = await aristotlelib.Project.create()
|
|
53
|
+
print(f"Created project: {project.project_id}")
|
|
54
|
+
|
|
55
|
+
# Add context files
|
|
56
|
+
await project.add_context(["path/to/context1.lean", "path/to/context2.lean"])
|
|
57
|
+
|
|
58
|
+
# Solve with input content
|
|
59
|
+
await project.solve(input_content="theorem my_theorem : True := trivial")
|
|
60
|
+
|
|
61
|
+
# Wait for completion and get solution
|
|
62
|
+
while project.status not in [aristotlelib.ProjectStatus.COMPLETE, aristotlelib.ProjectStatus.FAILED]:
|
|
63
|
+
await asyncio.sleep(30) # Poll every 30 seconds
|
|
64
|
+
await project.refresh()
|
|
65
|
+
print(f"Status: {project.status}")
|
|
66
|
+
|
|
67
|
+
if project.status == aristotlelib.ProjectStatus.COMPLETE:
|
|
68
|
+
solution_path = await project.get_solution()
|
|
69
|
+
print(f"Solution saved to: {solution_path}")
|
|
70
|
+
|
|
71
|
+
asyncio.run(main())
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## API Reference
|
|
75
|
+
|
|
76
|
+
### Project Class
|
|
77
|
+
|
|
78
|
+
The main class for interacting with Aristotle projects.
|
|
79
|
+
|
|
80
|
+
#### `Project.create(context_file_paths=None, validate_lean_project_root=True)`
|
|
81
|
+
|
|
82
|
+
Create a new Aristotle project.
|
|
83
|
+
|
|
84
|
+
**Parameters:**
|
|
85
|
+
- `context_file_paths` (list[Path | str], optional): List of file paths to include as context
|
|
86
|
+
- `validate_lean_project_root` (bool): Whether to validate Lean project structure (recommended: True)
|
|
87
|
+
|
|
88
|
+
**Returns:** `Project` instance
|
|
89
|
+
|
|
90
|
+
#### `Project.prove_from_file(input_file_path, auto_add_imports=True, context_file_paths=None, validate_lean_project=True, polling_interval_seconds=30, max_polling_failures=3)`
|
|
91
|
+
|
|
92
|
+
Convenience method to prove a theorem from a file with automatic import resolution.
|
|
93
|
+
|
|
94
|
+
**Parameters:**
|
|
95
|
+
- `input_file_path` (Path | str): Path to the input Lean file
|
|
96
|
+
- `auto_add_imports` (bool): Automatically add imported files as context
|
|
97
|
+
- `context_file_paths` (list[Path | str], optional): Manual context files
|
|
98
|
+
- `validate_lean_project` (bool): Validate Lean project structure
|
|
99
|
+
- `polling_interval_seconds` (int): Seconds between status checks
|
|
100
|
+
- `max_polling_failures` (int): Max polling failures before giving up
|
|
101
|
+
|
|
102
|
+
**Returns:** `Path` to the solution file
|
|
103
|
+
|
|
104
|
+
#### `project.add_context(context_file_paths, batch_size=10, validate_lean_project_root=True)`
|
|
105
|
+
|
|
106
|
+
Add context files to an existing project.
|
|
107
|
+
|
|
108
|
+
**Parameters:**
|
|
109
|
+
- `context_file_paths` (list[Path | str]): Files to add as context
|
|
110
|
+
- `batch_size` (int): Files to upload per batch (max 10)
|
|
111
|
+
- `validate_lean_project_root` (bool): Validate project structure
|
|
112
|
+
|
|
113
|
+
#### `project.solve(input_file_path=None, input_content=None)`
|
|
114
|
+
|
|
115
|
+
Solve the project with either a file or text content.
|
|
116
|
+
|
|
117
|
+
**Parameters:**
|
|
118
|
+
- `input_file_path` (Path | str, optional): Path to input file
|
|
119
|
+
- `input_content` (str, optional): Text content to solve
|
|
120
|
+
|
|
121
|
+
**Note:** Exactly one of `input_file_path` or `input_content` must be provided.
|
|
122
|
+
|
|
123
|
+
#### `project.get_solution(output_path=None)`
|
|
124
|
+
|
|
125
|
+
Download the solution file.
|
|
126
|
+
|
|
127
|
+
**Parameters:**
|
|
128
|
+
- `output_path` (Path | str, optional): Where to save the solution
|
|
129
|
+
|
|
130
|
+
**Returns:** `Path` to the downloaded solution file
|
|
131
|
+
|
|
132
|
+
#### `project.refresh()`
|
|
133
|
+
|
|
134
|
+
Refresh the project status from the API.
|
|
135
|
+
|
|
136
|
+
### Project Status
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
class ProjectStatus(Enum):
|
|
140
|
+
NOT_STARTED = "NOT_STARTED"
|
|
141
|
+
QUEUED = "QUEUED"
|
|
142
|
+
IN_PROGRESS = "IN_PROGRESS"
|
|
143
|
+
COMPLETE = "COMPLETE"
|
|
144
|
+
FAILED = "FAILED"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Error Handling
|
|
148
|
+
|
|
149
|
+
The SDK provides several exception types:
|
|
150
|
+
|
|
151
|
+
- `AristotleAPIError`: API-related errors
|
|
152
|
+
- `LeanProjectError`: Lean project validation errors
|
|
153
|
+
|
|
154
|
+
## Lean Project Requirements
|
|
155
|
+
|
|
156
|
+
Aristotle works best with properly structured Lean projects. Your project should have:
|
|
157
|
+
|
|
158
|
+
- A `lakefile.lean` (Lake build system) or `leanpkg.toml` (legacy)
|
|
159
|
+
- A `lean-toolchain` file
|
|
160
|
+
- Proper import structure
|
|
161
|
+
|
|
162
|
+
The SDK will automatically:
|
|
163
|
+
- Detect your project root
|
|
164
|
+
- Validate file paths are within the project
|
|
165
|
+
- Resolve imports to include dependencies
|
|
166
|
+
- Handle file size limits (100MB max per file)
|
|
167
|
+
|
|
168
|
+
## Examples
|
|
169
|
+
|
|
170
|
+
### Basic theorem proving
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
import asyncio
|
|
174
|
+
import aristotle
|
|
175
|
+
|
|
176
|
+
async def prove_simple_theorem():
|
|
177
|
+
# Set API key
|
|
178
|
+
aristotlelib.set_api_key("your-key")
|
|
179
|
+
|
|
180
|
+
# Prove a simple theorem
|
|
181
|
+
solution = await aristotlelib.Project.prove_from_file("examples/simple.lean")
|
|
182
|
+
print(f"Proof completed: {solution}")
|
|
183
|
+
|
|
184
|
+
asyncio.run(prove_simple_theorem())
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Working with existing projects
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
import asyncio
|
|
191
|
+
import aristotle
|
|
192
|
+
|
|
193
|
+
async def work_with_existing_project():
|
|
194
|
+
# Load an existing project
|
|
195
|
+
project = await aristotlelib.Project.from_id("existing-project-id")
|
|
196
|
+
|
|
197
|
+
# Check status
|
|
198
|
+
print(f"Project status: {project.status}")
|
|
199
|
+
|
|
200
|
+
if project.status == aristotlelib.ProjectStatus.COMPLETE:
|
|
201
|
+
solution = await project.get_solution()
|
|
202
|
+
print(f"Solution available at: {solution}")
|
|
203
|
+
|
|
204
|
+
asyncio.run(work_with_existing_project())
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Listing projects
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
import asyncio
|
|
211
|
+
import aristotle
|
|
212
|
+
|
|
213
|
+
async def list_projects():
|
|
214
|
+
projects, pagination_key = await aristotlelib.Project.list_projects(limit=10)
|
|
215
|
+
|
|
216
|
+
for project in projects:
|
|
217
|
+
print(f"Project {project.project_id}: {project.status}")
|
|
218
|
+
|
|
219
|
+
# Get next page if available
|
|
220
|
+
if pagination_key:
|
|
221
|
+
more_projects, pagination_key = await aristotlelib.Project.list_projects(pagination_key=pagination_key)
|
|
222
|
+
print(f"Found {len(more_projects)} more projects")
|
|
223
|
+
|
|
224
|
+
asyncio.run(list_projects())
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Logging
|
|
228
|
+
|
|
229
|
+
The SDK uses Python's standard logging module. To see debug and info messages from the SDK, configure logging in your application:
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
import logging
|
|
233
|
+
import aristotle
|
|
234
|
+
|
|
235
|
+
# Configure logging to see SDK messages
|
|
236
|
+
logging.basicConfig(
|
|
237
|
+
level=logging.INFO,
|
|
238
|
+
format="%(levelname)s - %(name)s - %(message)s"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# Or configure just the aristotle logger
|
|
242
|
+
logging.getLogger('aristotle').setLevel(logging.INFO)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
This will show helpful messages to track the current status.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "aristotlelib"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Aristotle SDK - Python library for automated theorem proving with Lean"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license-files = ["LICENSE"]
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "Harmonic" }]
|
|
13
|
+
maintainers = [{ name = "Harmonic" }]
|
|
14
|
+
dependencies = ["pydantic>=2.0.0", "httpx>=0.24.0"]
|
|
15
|
+
|
|
16
|
+
[project.urls]
|
|
17
|
+
Homepage = "https://aristotle.harmonic.fun"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
[tool.setuptools.packages.find]
|
|
21
|
+
where = ["src"]
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.package-dir]
|
|
24
|
+
"" = "src"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from aristotlelib.api_request import set_api_key
|
|
2
|
+
|
|
3
|
+
from aristotlelib.project import Project, ProjectStatus
|
|
4
|
+
from aristotlelib.local_file_utils import (
|
|
5
|
+
find_lean_project_root,
|
|
6
|
+
validate_local_file_paths,
|
|
7
|
+
gather_file_imports,
|
|
8
|
+
get_files_for_upload,
|
|
9
|
+
)
|
|
10
|
+
from aristotlelib.api_request import AristotleRequestClient, AristotleAPIError
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import httpx
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
API_VERSION = "1"
|
|
7
|
+
BASE_URL = f"https://aristotle.harmonic.fun/api/v{API_VERSION}"
|
|
8
|
+
DEFAULT_TIMEOUT_SECONDS = 30
|
|
9
|
+
|
|
10
|
+
API_KEY: str | None = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_api_key() -> str:
|
|
14
|
+
global API_KEY
|
|
15
|
+
api_key = API_KEY or os.environ.get("ARISTOTLE_API_KEY")
|
|
16
|
+
if api_key is None:
|
|
17
|
+
raise ValueError(
|
|
18
|
+
"API key has not been set. Call aristotle.set_api_key() or set the ARISTOTLE_API_KEY environment variable."
|
|
19
|
+
)
|
|
20
|
+
return api_key
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def set_api_key(api_key: str) -> str:
|
|
24
|
+
global API_KEY
|
|
25
|
+
API_KEY = api_key
|
|
26
|
+
return API_KEY
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AristotleRequestClient:
|
|
30
|
+
"""Async HTTP client for the Aristotle API."""
|
|
31
|
+
|
|
32
|
+
def __init__(self):
|
|
33
|
+
self._client: httpx.AsyncClient | None = None
|
|
34
|
+
|
|
35
|
+
async def __aenter__(self):
|
|
36
|
+
"""Async context manager entry."""
|
|
37
|
+
self._client = httpx.AsyncClient(timeout=DEFAULT_TIMEOUT_SECONDS)
|
|
38
|
+
return self
|
|
39
|
+
|
|
40
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
41
|
+
"""Async context manager exit."""
|
|
42
|
+
if self._client:
|
|
43
|
+
await self._client.aclose()
|
|
44
|
+
|
|
45
|
+
async def get(
|
|
46
|
+
self, endpoint: str, params: dict[str, Any] | None = None
|
|
47
|
+
) -> httpx.Response:
|
|
48
|
+
"""Make a GET request."""
|
|
49
|
+
return await self._make_request("GET", endpoint, params=params)
|
|
50
|
+
|
|
51
|
+
async def post(
|
|
52
|
+
self,
|
|
53
|
+
endpoint: str,
|
|
54
|
+
data: dict[str, Any] | None = None,
|
|
55
|
+
files: list[tuple[str, tuple[str, bytes, str]]] | None = None,
|
|
56
|
+
) -> httpx.Response:
|
|
57
|
+
"""Make a POST request."""
|
|
58
|
+
return await self._make_request("POST", endpoint, data=data, files=files)
|
|
59
|
+
|
|
60
|
+
async def put(
|
|
61
|
+
self, endpoint: str, data: dict[str, Any] | None = None
|
|
62
|
+
) -> httpx.Response:
|
|
63
|
+
"""Make a PUT request."""
|
|
64
|
+
return await self._make_request("PUT", endpoint, data=data)
|
|
65
|
+
|
|
66
|
+
async def delete(self, endpoint: str) -> httpx.Response:
|
|
67
|
+
"""Make a DELETE request."""
|
|
68
|
+
return await self._make_request("DELETE", endpoint)
|
|
69
|
+
|
|
70
|
+
async def _make_request(
|
|
71
|
+
self,
|
|
72
|
+
method: str,
|
|
73
|
+
endpoint: str,
|
|
74
|
+
data: dict[str, Any] | None = None,
|
|
75
|
+
params: dict[str, Any] | None = None,
|
|
76
|
+
files: list[tuple[str, tuple[str, bytes, str]]] | None = None,
|
|
77
|
+
) -> httpx.Response:
|
|
78
|
+
"""Make an HTTP request to the Aristotle API."""
|
|
79
|
+
url = f"{BASE_URL}/{endpoint.lstrip('/')}"
|
|
80
|
+
headers = {
|
|
81
|
+
"X-API-Key": get_api_key(),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if not self._client:
|
|
85
|
+
self._client = httpx.AsyncClient(timeout=DEFAULT_TIMEOUT_SECONDS)
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
if files:
|
|
89
|
+
# For file uploads, use multipart/form-data
|
|
90
|
+
files_data = []
|
|
91
|
+
for field_name, (file_path, file_content, content_type) in files:
|
|
92
|
+
files_data.append(
|
|
93
|
+
(field_name, (file_path, file_content, content_type))
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
response = await self._client.request(
|
|
97
|
+
method=method,
|
|
98
|
+
url=url,
|
|
99
|
+
data=data,
|
|
100
|
+
files=files_data,
|
|
101
|
+
params=params,
|
|
102
|
+
headers=headers,
|
|
103
|
+
)
|
|
104
|
+
else:
|
|
105
|
+
# For regular requests, use JSON
|
|
106
|
+
headers["Content-Type"] = "application/json"
|
|
107
|
+
response = await self._client.request(
|
|
108
|
+
method=method,
|
|
109
|
+
url=url,
|
|
110
|
+
json=data,
|
|
111
|
+
params=params,
|
|
112
|
+
headers=headers,
|
|
113
|
+
)
|
|
114
|
+
response.raise_for_status()
|
|
115
|
+
return response
|
|
116
|
+
except httpx.RequestError as e:
|
|
117
|
+
raise AristotleAPIError(f"Request failed: {str(e)}") from e
|
|
118
|
+
|
|
119
|
+
async def close(self):
|
|
120
|
+
"""Close the async client."""
|
|
121
|
+
if self._client:
|
|
122
|
+
await self._client.aclose()
|
|
123
|
+
self._client = None
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class AristotleAPIError(Exception):
|
|
127
|
+
"""Exception raised for API-related errors."""
|
|
128
|
+
|
|
129
|
+
pass
|