mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-18 10:01:23 +01:00
feat: ai generator - add support for claude
This commit is contained in:
parent
91dc16878e
commit
dc4bdb2eaa
1 changed files with 135 additions and 47 deletions
|
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
const GPT_MODELS = ["gpt-4o-mini", "chatgpt-4o-latest", "gpt-4o", "gpt-4-turbo", "gpt-4", "gpt-3.5-turbo"];
|
||||
const LLMS = ["gpt-4o-mini", "chatgpt-4o-latest", "gpt-4o", "gpt-4-turbo", "gpt-4", "gpt-3.5-turbo", "claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307", "claude-3-5-sonnet-20240620"];
|
||||
const SYSTEM_MESSAGE = "I'm working on my fantasy map.";
|
||||
|
||||
function geneateWithAi(defaultPrompt, onApply) {
|
||||
|
|
@ -23,12 +23,27 @@ function geneateWithAi(defaultPrompt, onApply) {
|
|||
Close: function () {
|
||||
$(this).dialog("close");
|
||||
}
|
||||
},
|
||||
open: function() {
|
||||
initialize();
|
||||
},
|
||||
close: function() {
|
||||
const helpLink = byId("aiGeneratorKey").nextElementSibling;
|
||||
helpLink.removeEventListener("mouseover", showDataTip);
|
||||
}
|
||||
});
|
||||
|
||||
if (modules.geneateWithAi) return;
|
||||
modules.geneateWithAi = true;
|
||||
|
||||
function initialize() {
|
||||
byId("aiGeneratorModel").addEventListener("change", function(e) {
|
||||
updateKeyHelp(e.target.value);
|
||||
});
|
||||
|
||||
updateValues();
|
||||
}
|
||||
|
||||
function updateValues() {
|
||||
byId("aiGeneratorResult").value = "";
|
||||
byId("aiGeneratorPrompt").value = defaultPrompt;
|
||||
|
|
@ -36,13 +51,34 @@ function geneateWithAi(defaultPrompt, onApply) {
|
|||
|
||||
const select = byId("aiGeneratorModel");
|
||||
select.options.length = 0;
|
||||
GPT_MODELS.forEach(model => select.options.add(new Option(model, model)));
|
||||
select.value = localStorage.getItem("fmg-ai-model") || GPT_MODELS[0];
|
||||
LLMS.forEach(model => select.options.add(new Option(model, model)));
|
||||
select.value = localStorage.getItem("fmg-ai-model") || LLMS[0];
|
||||
|
||||
updateKeyHelp(select.value);
|
||||
}
|
||||
|
||||
function updateKeyHelp(model) {
|
||||
const keyInput = byId("aiGeneratorKey");
|
||||
const helpLink = keyInput.nextElementSibling;
|
||||
|
||||
helpLink.removeEventListener("mouseover", showDataTip);
|
||||
|
||||
if (model.includes("claude")) {
|
||||
keyInput.placeholder = "Enter Anthropic API key";
|
||||
helpLink.href = "https://console.anthropic.com/account/keys";
|
||||
helpLink.dataset.tip = "Get the key at Anthropic's website. The key will be stored in your browser and send to Anthropic API directly. The Map Generator doesn't store the key or any generated data";
|
||||
} else {
|
||||
keyInput.placeholder = "Enter OpenAI API key";
|
||||
helpLink.href = "https://platform.openai.com/account/api-keys";
|
||||
helpLink.dataset.tip = "Get the key at OpenAI website. The key will be stored in your browser and send to OpenAI API directly. The Map Generator doesn't store the key or any generated data";
|
||||
}
|
||||
|
||||
helpLink.addEventListener("mouseover", showDataTip);
|
||||
}
|
||||
|
||||
async function generate(button) {
|
||||
const key = byId("aiGeneratorKey").value;
|
||||
if (!key) return tip("Please enter an OpenAI API key", true, "error", 4000);
|
||||
if (!key) return tip("Please enter an API key", true, "error", 4000);
|
||||
localStorage.setItem("fmg-ai-kl", key);
|
||||
|
||||
const model = byId("aiGeneratorModel").value;
|
||||
|
|
@ -58,53 +94,105 @@ function geneateWithAi(defaultPrompt, onApply) {
|
|||
resultArea.value = "";
|
||||
resultArea.disabled = true;
|
||||
|
||||
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${key}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model,
|
||||
messages: [
|
||||
{role: "system", content: SYSTEM_MESSAGE},
|
||||
{role: "user", content: prompt}
|
||||
],
|
||||
temperature: 1.2,
|
||||
stream: true // Enable streaming
|
||||
})
|
||||
});
|
||||
if (model.includes("claude")) {
|
||||
const baseUrl = "https://api.anthropic.com/v1/messages";
|
||||
|
||||
if (!response.ok) {
|
||||
const json = await response.json();
|
||||
throw new Error(json?.error?.message || "Failed to generate");
|
||||
}
|
||||
const response = await fetch(baseUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-api-key": key,
|
||||
"anthropic-version": "2023-06-01",
|
||||
"anthropic-dangerous-direct-browser-access": "true"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model,
|
||||
messages: [{role: "user", content: prompt}],
|
||||
stream: true,
|
||||
max_tokens: 4096
|
||||
})
|
||||
});
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
let buffer = "";
|
||||
|
||||
while (true) {
|
||||
const {done, value} = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, {stream: true});
|
||||
const lines = buffer.split("\n");
|
||||
|
||||
for (let i = 0; i < lines.length - 1; i++) {
|
||||
const line = lines[i].trim();
|
||||
if (line.startsWith("data: ") && line !== "data: [DONE]") {
|
||||
try {
|
||||
const jsonData = JSON.parse(line.slice(6));
|
||||
const content = jsonData.choices[0].delta.content;
|
||||
if (content) resultArea.value += content;
|
||||
} catch (jsonError) {
|
||||
console.warn("Failed to parse JSON:", jsonError, "Line:", line);
|
||||
}
|
||||
}
|
||||
if (!response.ok) {
|
||||
const json = await response.json();
|
||||
throw new Error(json?.error?.message || "Failed to generate with Claude");
|
||||
}
|
||||
|
||||
buffer = lines[lines.length - 1];
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
let buffer = "";
|
||||
|
||||
while (true) {
|
||||
const {done, value} = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, {stream: true});
|
||||
const lines = buffer.split("\n");
|
||||
|
||||
for (let i = 0; i < lines.length - 1; i++) {
|
||||
const line = lines[i].trim();
|
||||
if (line.startsWith("data: ")) {
|
||||
try {
|
||||
const jsonData = JSON.parse(line.slice(6));
|
||||
const content = jsonData.delta?.text;
|
||||
if (content) resultArea.value += content;
|
||||
} catch (jsonError) {
|
||||
console.warn("Failed to parse Claude JSON:", jsonError, "Line:", line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer = lines[lines.length - 1];
|
||||
}
|
||||
} else {
|
||||
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${key}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model,
|
||||
messages: [
|
||||
{role: "system", content: SYSTEM_MESSAGE},
|
||||
{role: "user", content: prompt}
|
||||
],
|
||||
temperature: 1.2,
|
||||
stream: true
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const json = await response.json();
|
||||
throw new Error(json?.error?.message || "Failed to generate");
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
let buffer = "";
|
||||
|
||||
while (true) {
|
||||
const {done, value} = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, {stream: true});
|
||||
const lines = buffer.split("\n");
|
||||
|
||||
for (let i = 0; i < lines.length - 1; i++) {
|
||||
const line = lines[i].trim();
|
||||
if (line.startsWith("data: ") && line !== "data: [DONE]") {
|
||||
try {
|
||||
const jsonData = JSON.parse(line.slice(6));
|
||||
const content = jsonData.choices[0].delta.content;
|
||||
if (content) resultArea.value += content;
|
||||
} catch (jsonError) {
|
||||
console.warn("Failed to parse JSON:", jsonError, "Line:", line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer = lines[lines.length - 1];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return tip(error.message, true, "error", 4000);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue