Skip to content

Commit 2eef1d0

Browse files
committed
api
1 parent 0bfeae0 commit 2eef1d0

File tree

4 files changed

+363
-1
lines changed

4 files changed

+363
-1
lines changed

app.py

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from dotenv import load_dotenv
33
import time
44
import traceback
5-
from flask import Flask, render_template, request, jsonify, send_file, Response
5+
from flask import Flask, render_template, request, jsonify, send_file, Response, abort
66
import openai
77
from together import Together
88
from reportlab.lib.pagesizes import letter
@@ -16,6 +16,7 @@
1616
from queue import Queue
1717
import sqlite3
1818
from datetime import datetime
19+
import secrets
1920

2021
load_dotenv() # Load environment variables from .env file
2122

@@ -44,6 +45,11 @@ def init_db():
4445
title TEXT,
4546
filepath TEXT,
4647
timestamp TEXT)''')
48+
c.execute('''CREATE TABLE IF NOT EXISTS api_keys
49+
(id INTEGER PRIMARY KEY AUTOINCREMENT,
50+
ip TEXT,
51+
api_key TEXT UNIQUE,
52+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')
4753
conn.commit()
4854
conn.close()
4955

@@ -324,5 +330,92 @@ def download_saved_pdf(pdf_id):
324330
app.logger.error(traceback.format_exc())
325331
return jsonify({'error': str(e)}), 500
326332

333+
def generate_api_key():
334+
return secrets.token_urlsafe(32)
335+
336+
@app.route('/generate-api-key', methods=['POST'])
337+
def create_api_key():
338+
ip = request.remote_addr
339+
api_key = generate_api_key()
340+
341+
conn = sqlite3.connect('pdfs.db')
342+
c = conn.cursor()
343+
c.execute("INSERT INTO api_keys (ip, api_key) VALUES (?, ?)", (ip, api_key))
344+
conn.commit()
345+
conn.close()
346+
347+
return jsonify({'api_key': api_key})
348+
349+
@app.route('/api/generate-book', methods=['POST'])
350+
async def api_generate_book():
351+
api_key = request.headers.get('X-API-Key')
352+
if not api_key:
353+
abort(401, description="API key is missing")
354+
355+
conn = sqlite3.connect('pdfs.db')
356+
c = conn.cursor()
357+
c.execute("SELECT ip FROM api_keys WHERE api_key = ?", (api_key,))
358+
result = c.fetchone()
359+
conn.close()
360+
361+
if not result:
362+
abort(401, description="Invalid API key")
363+
364+
api = request.json.get('api')
365+
model = request.json.get('model')
366+
topic = request.json.get('topic')
367+
language = request.json.get('language')
368+
target_word_count = request.json.get('word_count')
369+
370+
if not all([api, model, topic, language, target_word_count]):
371+
abort(400, description="Missing required parameters")
372+
373+
try:
374+
target_word_count = int(target_word_count)
375+
except ValueError:
376+
abort(400, description="Invalid word count")
377+
378+
current_word_count = 0
379+
book_content = []
380+
chapter_count = 0
381+
382+
tasks = []
383+
while current_word_count < target_word_count:
384+
is_new_chapter = (chapter_count == 0) or (current_word_count > 0 and current_word_count % 3000 < 500)
385+
386+
if is_new_chapter:
387+
chapter_count += 1
388+
tasks = []
389+
while current_word_count < target_word_count:
390+
is_new_chapter = (chapter_count == 0) or (current_word_count > 0 and current_word_count % 3000 < 500)
391+
392+
if is_new_chapter:
393+
chapter_count += 1
394+
task = asyncio.create_task(generate_chunk(api, model, topic, current_word_count, language, is_new_chapter=True))
395+
else:
396+
task = asyncio.create_task(generate_chunk(api, model, topic, current_word_count, language))
397+
398+
tasks.append(task)
399+
current_word_count += 500 # Approximate word count per chunk
400+
401+
if len(tasks) >= 5 or current_word_count >= target_word_count:
402+
chunks = await asyncio.gather(*tasks)
403+
for chunk in chunks:
404+
book_content.append(chunk)
405+
tasks = []
406+
await asyncio.sleep(1) # Small delay to avoid rate limits
407+
408+
formatted_book = "\n\n".join(book_content)
409+
actual_word_count = len(formatted_book.split())
410+
411+
return jsonify({
412+
'content': formatted_book,
413+
'word_count': actual_word_count
414+
})
415+
416+
@app.route('/api')
417+
def api_page():
418+
return render_template('api.html')
419+
327420
if __name__ == "__main__":
328421
app.run(debug=True, host="0.0.0.0", port=5151)

pdfs.db

8 KB
Binary file not shown.

templates/api.html

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>API Access - Book Generator</title>
7+
<style>
8+
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap');
9+
10+
:root {
11+
--primary-color: #6200ea;
12+
--secondary-color: #b388ff;
13+
--background-color: #1a237e;
14+
--text-color: #ffffff;
15+
--accent-color: #00bcd4;
16+
}
17+
18+
body {
19+
font-family: 'Poppins', sans-serif;
20+
line-height: 1.6;
21+
color: var(--text-color);
22+
background-color: var(--background-color);
23+
margin: 0;
24+
padding: 0;
25+
transition: background-color 0.3s ease;
26+
}
27+
28+
header {
29+
background-color: var(--primary-color);
30+
padding: 1rem;
31+
text-align: center;
32+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
33+
}
34+
35+
h1 {
36+
margin: 0;
37+
font-weight: 600;
38+
letter-spacing: 1px;
39+
}
40+
41+
nav ul {
42+
padding: 0;
43+
list-style-type: none;
44+
margin-top: 1rem;
45+
}
46+
47+
nav ul li {
48+
display: inline;
49+
margin-right: 1rem;
50+
}
51+
52+
nav ul li a {
53+
color: var(--text-color);
54+
text-decoration: none;
55+
font-weight: 300;
56+
transition: color 0.3s ease;
57+
}
58+
59+
nav ul li a:hover {
60+
color: var(--accent-color);
61+
}
62+
63+
main {
64+
padding: 2rem;
65+
max-width: 800px;
66+
margin: 0 auto;
67+
}
68+
69+
section {
70+
background-color: rgba(255, 255, 255, 0.05);
71+
border-radius: 8px;
72+
padding: 2rem;
73+
margin-bottom: 2rem;
74+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
75+
transition: transform 0.3s ease, box-shadow 0.3s ease;
76+
}
77+
78+
section:hover {
79+
transform: translateY(-5px);
80+
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
81+
}
82+
83+
h2 {
84+
color: var(--secondary-color);
85+
margin-top: 0;
86+
}
87+
88+
h3 {
89+
color: var(--accent-color);
90+
}
91+
92+
button {
93+
background-color: var(--accent-color);
94+
color: var(--background-color);
95+
border: none;
96+
padding: 12px 24px;
97+
border-radius: 30px;
98+
cursor: pointer;
99+
font-size: 1rem;
100+
font-weight: 600;
101+
transition: background-color 0.3s ease, transform 0.2s ease;
102+
}
103+
104+
button:hover {
105+
background-color: #00acc1;
106+
transform: scale(1.05);
107+
}
108+
109+
#apiKeyDisplay {
110+
margin-top: 1rem;
111+
padding: 1rem;
112+
background-color: rgba(0, 0, 0, 0.2);
113+
border-radius: 4px;
114+
font-family: 'Courier New', monospace;
115+
transition: opacity 0.3s ease;
116+
}
117+
118+
pre {
119+
background-color: rgba(0, 0, 0, 0.3);
120+
padding: 1rem;
121+
border-radius: 4px;
122+
overflow-x: auto;
123+
font-family: 'Courier New', monospace;
124+
}
125+
126+
table {
127+
width: 100%;
128+
border-collapse: collapse;
129+
margin-top: 1rem;
130+
}
131+
132+
th, td {
133+
padding: 0.75rem;
134+
text-align: left;
135+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
136+
}
137+
138+
th {
139+
background-color: rgba(0, 0, 0, 0.2);
140+
font-weight: 600;
141+
}
142+
143+
tr:hover {
144+
background-color: rgba(255, 255, 255, 0.05);
145+
}
146+
147+
@media (max-width: 600px) {
148+
main {
149+
padding: 1rem;
150+
}
151+
152+
section {
153+
padding: 1.5rem;
154+
}
155+
156+
table, th, td {
157+
font-size: 0.9rem;
158+
}
159+
}
160+
</style>
161+
</head>
162+
<body>
163+
<header>
164+
<h1>API Access</h1>
165+
<nav>
166+
<ul>
167+
<li><a href="{{ url_for('hello') }}">Home</a></li>
168+
<li><a href="{{ url_for('about') }}">About</a></li>
169+
<li><a href="{{ url_for('api_page') }}">API</a></li>
170+
</ul>
171+
</nav>
172+
</header>
173+
<main>
174+
<section>
175+
<h2>Generate API Key</h2>
176+
<button id="generateApiKey">Generate API Key</button>
177+
<div id="apiKeyDisplay"></div>
178+
</section>
179+
<section>
180+
<h2>API Documentation</h2>
181+
<h3>Endpoint: https://tryBookAI.com/api/generate-book</h3>
182+
<p><strong>Method:</strong> POST</p>
183+
<p><strong>Headers:</strong></p>
184+
<ul>
185+
<li>Content-Type: application/json</li>
186+
<li>X-API-Key: Your API Key</li>
187+
</ul>
188+
<p><strong>Request Body:</strong></p>
189+
<pre>
190+
{
191+
"api": "openai" or "together",
192+
"model": "model name",
193+
"topic": "book topic",
194+
"language": "language",
195+
"word_count": number of words
196+
}
197+
</pre>
198+
<p><strong>Example Response:</strong></p>
199+
<pre>
200+
{
201+
"message": "Book generation started",
202+
"status": "processing"
203+
}
204+
</pre>
205+
</section>
206+
<section>
207+
<h2>API Pricing</h2>
208+
<p>Our API pricing is based on the model used and the number of tokens processed. Here's a breakdown of the costs:</p>
209+
<table>
210+
<thead>
211+
<tr>
212+
<th>API</th>
213+
<th>Model</th>
214+
<th>Price per 1K tokens</th>
215+
</tr>
216+
</thead>
217+
<tbody>
218+
<tr>
219+
<td>OpenAI</td>
220+
<td>GPT-3.5</td>
221+
<td>$0.002</td>
222+
</tr>
223+
<tr>
224+
<td>OpenAI</td>
225+
<td>GPT-4</td>
226+
<td>$0.03</td>
227+
</tr>
228+
<tr>
229+
<td>Together AI</td>
230+
<td>Llama-2-70b</td>
231+
<td>$0.0008</td>
232+
</tr>
233+
<tr>
234+
<td>Together AI</td>
235+
<td>Llama-2-13b</td>
236+
<td>$0.0006</td>
237+
</tr>
238+
</tbody>
239+
</table>
240+
<h3>Token Estimation</h3>
241+
<p>On average, 1 token is approximately 4 characters or 0.75 words. For precise pricing, we recommend using our token calculator tool.</p>
242+
<h3>Billing</h3>
243+
<p>You will only be charged for the tokens used in generating the book. The API tracks token usage and bills accordingly. Detailed usage reports are available in your account dashboard.</p>
244+
</section>
245+
</main>
246+
<script>
247+
document.getElementById('generateApiKey').addEventListener('click', function() {
248+
fetch('/generate-api-key', {
249+
method: 'POST',
250+
})
251+
.then(response => response.json())
252+
.then(data => {
253+
const apiKeyDisplay = document.getElementById('apiKeyDisplay');
254+
apiKeyDisplay.innerHTML = `Your API Key: <strong>${data.api_key}</strong>`;
255+
apiKeyDisplay.style.opacity = '0';
256+
setTimeout(() => {
257+
apiKeyDisplay.style.opacity = '1';
258+
}, 100);
259+
})
260+
.catch(error => {
261+
console.error('Error:', error);
262+
document.getElementById('apiKeyDisplay').innerHTML = 'Error generating API key';
263+
});
264+
});
265+
</script>
266+
</body>
267+
</html>

0 commit comments

Comments
 (0)