-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreadwise.ts
More file actions
153 lines (128 loc) · 5.05 KB
/
readwise.ts
File metadata and controls
153 lines (128 loc) · 5.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import OpenAI from "openai";
import readlineSync from "readline-sync";
import axios from "axios";
import * as dotenv from "dotenv";
// Load environment variables
dotenv.config();
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
const READWISE_API_KEY = process.env.READWISE_API_KEY;
if (!OPENAI_API_KEY || !READWISE_API_KEY) {
console.error("❌ Missing required API keys.");
process.exit(1);
}
// Initialize OpenAI API
const openai = new OpenAI({ apiKey: OPENAI_API_KEY });
async function askGPT(prompt: string): Promise<{ answer: string; keyTakeaways: string[]; title: string; memoryHook: string }> {
console.log("🔍 Sending prompt to OpenAI...");
try {
const response = await openai.chat.completions.create({
model: "gpt-4-turbo",
response_format: { type: "json_object" }, // Ensure structured JSON output
messages: [
{
role: "system",
content: "You are an AI assistant that formats responses based on user-provided instructions. Always refer to the prompt for guidance on how the answer should be structured and what constraints to follow.",
},
{
role: "user",
content: `Provide a structured JSON response for this query, follow the instructions closely:
- full_answer: A detailed but concise response to the query, no more than a paragraph.
- title: A title for the response, that will be used as the header in a Readwise entry.
- key_takeaways: An array of 3-5 key bullet points summarizing the answer.
- memory_hook: A visualization or mnemonic to help recall the information.
Query: "${prompt}"
`,
},
],
max_tokens: 700,
});
const jsonData = response.choices[0]?.message?.content;
if (!jsonData) throw new Error("No structured response received.");
const parsedResponse = JSON.parse(jsonData);
console.log("✅ Response received from OpenAI.", parsedResponse);
return {
answer: parsedResponse.full_answer || "N/A",
title: parsedResponse.title || "N/A",
keyTakeaways: parsedResponse.key_takeaways || [],
memoryHook: parsedResponse.memory_hook || "N/A",
};
} catch (error) {
console.error("❌ Error fetching response from OpenAI:", error);
return { answer: "Error: Could not retrieve response.", keyTakeaways: ["N/A"], memoryHook: "N/A", title: "N/A" };
}
}
function generateRecallNotes(title: string, keyTakeaways: string[], memoryHook: string) {
const formattedTakeaways = keyTakeaways.map((point) => `- ${point}`).join("\n");
return {
title,
note: `🔑 Key Takeaways:\n${formattedTakeaways}\n\n🧠 Memory Hook:\n${memoryHook}`,
};
}
async function sendToReadwise(title: string, answer: string, keyTakeaways: string[], memoryHook: string) {
console.log("📤 Sending to Readwise API...");
const recallData = generateRecallNotes(title, keyTakeaways, memoryHook);
const payload = {
highlights: [
{
text: answer,
title: recallData.title,
source_url: "https://chat.openai.com",
note: recallData.note,
author: "OpenAI",
},
],
};
try {
const response = await axios.post("https://readwise.io/api/v2/highlights/", payload, {
headers: {
Authorization: `Token ${READWISE_API_KEY}`,
"Content-Type": "application/json",
},
});
if (response.status === 200) {
console.log("✅ Successfully sent to Readwise!");
} else {
console.error("❌ Failed to send to Readwise:", response.data);
}
} catch (error) {
console.error("❌ Error sending to Readwise:", error.response?.data || error.message);
}
}
async function getUserDecision(title: string, answer: string, keyTakeaways: string[], memoryHook: string) {
const recallData = generateRecallNotes(title, keyTakeaways, memoryHook);
while (true) {
console.log("\n📌 **Optimized Highlight for Readwise:**");
console.log("📖 Title:", recallData.title);
console.log(recallData.note);
const action = readlineSync.question("Do you want to (k)eep, (r)etry, or (c)ancel? ").toLowerCase();
if (action === "k") {
console.log("📥 Keeping the response and sending to Readwise...");
await sendToReadwise(title, answer, keyTakeaways, memoryHook);
break;
} else if (action === "r") {
console.log("🔄 Retrying...");
return true;
} else if (action === "c") {
console.log("🚫 Cancelled.");
break;
} else {
console.log("❌ Invalid input. Please enter 'k' for keep, 'r' for retry, or 'c' for cancel.");
}
}
return false;
}
async function main() {
const prompt = process.argv.slice(2).join(" ");
if (!prompt) {
console.error("❌ Please provide a prompt.");
process.exit(1);
}
console.log(`📜 Received prompt: "${prompt}"`);
let retry = true;
while (retry) {
const { answer, title, keyTakeaways, memoryHook } = await askGPT(prompt);
console.log("\n📝 GPT Response:\n" + answer + "\n");
retry = await getUserDecision(title, answer, keyTakeaways, memoryHook);
}
}
main().catch(console.error);