mirror of
https://github.com/danielmiessler/fabric
synced 2024-11-08 07:11:06 +00:00
Added server infra.
This commit is contained in:
parent
a345feab9c
commit
f1c7ff022b
30
infrastructure/client/extract_wisdom.py
Normal file
30
infrastructure/client/extract_wisdom.py
Normal file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import sys
|
||||
import json
|
||||
import requests
|
||||
|
||||
|
||||
def send_request(prompt):
|
||||
url = "http://hostorip.tld:13337/extwis"
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "eJ4f1e0b-25wO-47f9-97ec-6b5335b2",
|
||||
}
|
||||
data = json.dumps({"input": prompt})
|
||||
response = requests.post(url, headers=headers, data=data)
|
||||
|
||||
try:
|
||||
print(response.json()["response"])
|
||||
except KeyError:
|
||||
print("Error: The API response does not contain a 'response' key.")
|
||||
print("Received response:", response.json())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
prompt = " ".join(sys.argv[1:])
|
||||
send_request(prompt)
|
||||
else:
|
||||
prompt = sys.stdin.read()
|
||||
send_request(prompt)
|
5
infrastructure/server/fabric_api_keys.json
Normal file
5
infrastructure/server/fabric_api_keys.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"/extwis": {
|
||||
"eJ4f1e0b-25wO-47f9-97ec-6b5335b2": "Daniel Miessler"
|
||||
}
|
||||
}
|
216
infrastructure/server/fabric_api_server.py
Normal file
216
infrastructure/server/fabric_api_server.py
Normal file
@ -0,0 +1,216 @@
|
||||
# Imports
|
||||
import openai
|
||||
import json
|
||||
from flask import Flask, request, jsonify
|
||||
from functools import wraps
|
||||
import re
|
||||
import requests
|
||||
|
||||
## Define Flask app
|
||||
app = Flask(__name__)
|
||||
|
||||
##################################################
|
||||
##################################################
|
||||
#
|
||||
# ⚠️ CAUTION: This is an HTTP-only server!
|
||||
#
|
||||
# If you don't know what you're doing, don't run
|
||||
#
|
||||
##################################################
|
||||
##################################################
|
||||
|
||||
## Setup
|
||||
|
||||
## Did I mention this is HTTP only? Don't run this on the public internet.
|
||||
|
||||
## Set authentication on your APIs
|
||||
## Let's at least have some kind of auth
|
||||
|
||||
# Read API tokens from the apikeys.json file
|
||||
with open("fabric_api_keys.json", "r") as tokens_file:
|
||||
valid_tokens = json.load(tokens_file)
|
||||
|
||||
|
||||
# The function to check if the token is valid
|
||||
def auth_required(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
# Get the authentication token from request header
|
||||
auth_token = request.headers.get("Authorization", "")
|
||||
|
||||
# Remove any bearer token prefix if present
|
||||
if auth_token.lower().startswith("bearer "):
|
||||
auth_token = auth_token[7:]
|
||||
|
||||
# Get API endpoint from request
|
||||
endpoint = request.path
|
||||
|
||||
# Check if token is valid
|
||||
user = check_auth_token(auth_token, endpoint)
|
||||
if user == "Unauthorized: You are not authorized for this API":
|
||||
return jsonify({"error": user}), 401
|
||||
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return decorated_function
|
||||
|
||||
|
||||
# Check for a valid token/user for the given route
|
||||
def check_auth_token(token, route):
|
||||
# Check if token is valid for the given route and return corresponding user
|
||||
if route in valid_tokens and token in valid_tokens[route]:
|
||||
return valid_tokens[route][token]
|
||||
else:
|
||||
return "Unauthorized: You are not authorized for this API"
|
||||
|
||||
|
||||
# Define the allowlist of characters
|
||||
ALLOWLIST_PATTERN = re.compile(r"^[a-zA-Z0-9\s.,;:!?\-]+$")
|
||||
|
||||
|
||||
# Sanitize the content, sort of. Prompt injection is the main threat so this isn't a huge deal
|
||||
def sanitize_content(content):
|
||||
return "".join(char for char in content if ALLOWLIST_PATTERN.match(char))
|
||||
|
||||
|
||||
# Pull the URL content's from the GitHub repo
|
||||
def fetch_content_from_url(url):
|
||||
try:
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
sanitized_content = sanitize_content(response.text)
|
||||
return sanitized_content
|
||||
except requests.RequestException as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
# Set your OpenAI API key
|
||||
with open("openai.key", "r") as key_file:
|
||||
openai.api_key = key_file.read().strip()
|
||||
|
||||
## APIs
|
||||
|
||||
|
||||
# /extwis
|
||||
@app.route("/extwis", methods=["POST"])
|
||||
@auth_required # Require authentication
|
||||
def extwis():
|
||||
data = request.get_json()
|
||||
|
||||
# Warn if there's no input
|
||||
if "input" not in data:
|
||||
return jsonify({"error": "Missing input parameter"}), 400
|
||||
|
||||
# Get data from client
|
||||
input_data = data["input"]
|
||||
|
||||
# Set the system and user URLs
|
||||
system_url = "https://raw.githubusercontent.com/danielmiessler/fabric/main/patterns/extract_wisdom/system.md"
|
||||
user_url = "https://raw.githubusercontent.com/danielmiessler/fabric/main/patterns/extract_wisdom/user.md"
|
||||
|
||||
# Fetch the prompt content
|
||||
system_content = fetch_content_from_url(system_url)
|
||||
user_file_content = fetch_content_from_url(user_url)
|
||||
|
||||
# Build the API call
|
||||
system_message = {"role": "system", "content": system_content}
|
||||
user_message = {"role": "user", "content": user_file_content + "\n" + input_data}
|
||||
messages = [system_message, user_message]
|
||||
try:
|
||||
response = openai.ChatCompletion.create(
|
||||
model="gpt-4-1106-preview",
|
||||
messages=messages,
|
||||
temperature=0.0,
|
||||
top_p=1,
|
||||
frequency_penalty=0.1,
|
||||
presence_penalty=0.1,
|
||||
)
|
||||
assistant_message = response["choices"][0]["message"]["content"]
|
||||
return jsonify({"response": assistant_message})
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
|
||||
# /labelandrate
|
||||
@app.route("/labelandrate", methods=["POST"])
|
||||
def labelandrate():
|
||||
data = request.get_json()
|
||||
|
||||
if "input" not in data:
|
||||
return jsonify({"error": "Missing input parameter"}), 400
|
||||
|
||||
input_data = data["input"]
|
||||
system_message = {
|
||||
"role": "system",
|
||||
"content": """
|
||||
|
||||
You are an ultra-wise and brilliant classifier and judge of content. You label content with a a comma-separated list of single-word labels and then give it a quality rating.
|
||||
|
||||
Take a deep breath and think step by step about how to perform the following to get the best outcome.
|
||||
|
||||
STEPS:
|
||||
|
||||
1. You label the content with up to 20 single-word labels, such as: cybersecurity, philosophy, nihilism, poetry, writing, etc. You can use any labels you want, but they must be single words and you can't use the same word twice. This goes in a section called LABELS:.
|
||||
|
||||
2. You then rate the content based on the number of ideas in the input (below ten is bad, between 11 and 20 is good, and above 25 is excellent) combined with how well it matches the THEMES of: human meaning, the future of AI, mental models, abstract thinking, unconvential thinking, meaning in a post-ai world, continuous improvement, reading, art, books, and related topics.
|
||||
|
||||
You use the following rating levels:
|
||||
|
||||
S Tier (Must Consume Original Content Immediately): 18+ ideas and/or STRONG theme matching with the themes in STEP #2.
|
||||
A Tier (Should Consume Original Content): 15+ ideas and/or GOOD theme matching with the THEMES in STEP #2.
|
||||
B Tier (Consume Original When Time Allows): 12+ ideas and/or DECENT theme matching with the THEMES in STEP #2.
|
||||
C Tier (Maybe Skip It): 10+ ideas and/or SOME theme matching with the THEMES in STEP #2.
|
||||
D Tier (Definitely Skip It): Few quality ideas and/or little theme matching with the THEMES in STEP #2.
|
||||
|
||||
Also provide a score between 1 and 100 for the overall quality ranking, where 100 is a perfect match with the highest number of high quality ideas, and 1 is the worst match with a low number of the worst ideas.
|
||||
|
||||
The output should look like the following:
|
||||
|
||||
LABELS:
|
||||
|
||||
Cybersecurity, Writing, Running, Copywriting
|
||||
|
||||
RATING:
|
||||
|
||||
S Tier: (Must Consume Original Content Immediately)
|
||||
|
||||
Explanation: $$Explanation in 5 short bullets for why you gave that rating.$$
|
||||
|
||||
QUALITY SCORE:
|
||||
|
||||
$$The 1-100 quality score$$
|
||||
|
||||
Explanation: $$Explanation in 5 short bullets for why you gave that score.$$
|
||||
|
||||
""",
|
||||
}
|
||||
user_message = {
|
||||
"role": "user",
|
||||
"content": """
|
||||
|
||||
CONTENT:
|
||||
|
||||
""",
|
||||
}
|
||||
|
||||
messages = [system_message, {"role": "user", "content": input_data}]
|
||||
|
||||
try:
|
||||
response = openai.ChatCompletion.create(
|
||||
model="gpt-4-1106-preview",
|
||||
messages=messages,
|
||||
temperature=0.0,
|
||||
top_p=1,
|
||||
frequency_penalty=0.1,
|
||||
presence_penalty=0.1,
|
||||
)
|
||||
|
||||
assistant_message = response["choices"][0]["message"]["content"]
|
||||
return jsonify({"response": assistant_message})
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
|
||||
# Run the application
|
||||
if __name__ == "__main__":
|
||||
app.run(host="1.1.1.1", port=13337, debug=True)
|
Binary file not shown.
After Width: | Height: | Size: 2.6 MiB |
@ -0,0 +1,5 @@
|
||||
{
|
||||
"/extwis": {
|
||||
"eJ4f1e0b-25wO-47f9-97ec-6b5335b2": "Daniel Miessler"
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
from flask import Flask, render_template, request, redirect, url_for, flash, session
|
||||
import requests
|
||||
import json
|
||||
from flask import send_from_directory
|
||||
|
||||
##################################################
|
||||
##################################################
|
||||
#
|
||||
# ⚠️ CAUTION: This is an HTTP-only server!
|
||||
#
|
||||
# If you don't know what you're doing, don't run
|
||||
#
|
||||
##################################################
|
||||
##################################################
|
||||
|
||||
|
||||
def send_request(prompt, endpoint):
|
||||
base_url = "http://hostorip.tld:13337"
|
||||
url = f"{base_url}{endpoint}"
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "eJ4f1e0b-25wO-47f9-97ec-6b5335b2",
|
||||
}
|
||||
data = json.dumps({"input": prompt})
|
||||
response = requests.post(url, headers=headers, data=data, verify=False)
|
||||
|
||||
try:
|
||||
return response.json()["response"]
|
||||
except KeyError:
|
||||
return f"Error: You're not authorized for this application."
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = "your_secret_key"
|
||||
|
||||
|
||||
@app.route("/favicon.ico")
|
||||
def favicon():
|
||||
return send_from_directory(
|
||||
os.path.join(app.root_path, "static"),
|
||||
"favicon.ico",
|
||||
mimetype="image/vnd.microsoft.icon",
|
||||
)
|
||||
|
||||
|
||||
@app.route("/", methods=["GET", "POST"])
|
||||
def index():
|
||||
if request.method == "POST":
|
||||
prompt = request.form.get("prompt")
|
||||
endpoint = request.form.get("api")
|
||||
response = send_request(prompt=prompt, endpoint=endpoint)
|
||||
return render_template("index.html", response=response)
|
||||
return render_template("index.html", response=None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="172.30.0.176", port=13338, debug=True)
|
BIN
infrastructure/server/fabric_web_interface/favicon.ico
Normal file
BIN
infrastructure/server/fabric_web_interface/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.6 MiB |
BIN
infrastructure/server/fabric_web_interface/static/favicon.ico
Normal file
BIN
infrastructure/server/fabric_web_interface/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,64 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
|
||||
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
|
||||
<title>fabric</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="https://beehiiv-images-production.s3.amazonaws.com/uploads/asset/file/971f362a-f3fa-427f-b619-7e04cc135d17/fabric-logo-miessler-transparent.png?t=1704525002" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.16/dist/tailwind.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="bg-gray-900 text-white min-h-screen">
|
||||
<div class="container mx-auto py-10 px-4">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<!-- Add this line inside the div with class "flex justify-between items-center mb-6" -->
|
||||
<p><img src="static/fabric-logo-miessler-transparent.png" alt="fabric logo" class="h-20 w-auto mr-2"></p>
|
||||
<h1 class="text-4xl font-bold"><code>fabric</code></h1>
|
||||
|
||||
</div>
|
||||
<p>Enter your content and the API you want to send it to.</p>
|
||||
<br />
|
||||
<form method="POST" class="space-y-4">
|
||||
<div>
|
||||
<label for="prompt" class="block text-sm font-medium">Content:</label>
|
||||
<input type="text" id="prompt" name="prompt" required class="w-full px-3 py-2 border border-gray-300 rounded-md text-black">
|
||||
</div>
|
||||
<div>
|
||||
<label for="api" class="block text-sm font-medium">API:</label>
|
||||
<select id="api" name="api" class="w-full px-3 py-2 border border-gray-300 rounded-md text-black">
|
||||
<option value="/extwis">/extwis</option>
|
||||
<!-- Add more API endpoints here... -->
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-md text-white font-medium">Submit</button>
|
||||
</form>
|
||||
{% if response %}
|
||||
<div class="mt-8">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-2xl font-bold">Response:</h2>
|
||||
<button id="copy-button" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-md">Copy</button>
|
||||
</div>
|
||||
<pre id="response-output" class="bg-gray-800 p-4 rounded-md whitespace-pre-wrap">{{ response }}</pre>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<script>
|
||||
document.getElementById("api").addEventListener("change", function() {
|
||||
document.getElementById("response-output").textContent = "";
|
||||
});
|
||||
|
||||
document.getElementById("copy-button").addEventListener("click", function() {
|
||||
const responseOutput = document.getElementById("response-output");
|
||||
const range = document.createRange();
|
||||
range.selectNode(responseOutput);
|
||||
window.getSelection().removeAllRanges();
|
||||
window.getSelection().addRange(range);
|
||||
document.execCommand("copy");
|
||||
window.getSelection().removeAllRanges();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user