Last active
December 28, 2024 17:40
-
-
Save rkmax/cef19c9dabcc5f9060ccd364d1e1bccb to your computer and use it in GitHub Desktop.
Clipboard Translator using OpenAI/Ollama API This Deno script translates text from your clipboard using LLM and copies the result back to your clipboard
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env -S deno run --allow-net --allow-env --allow-run --allow-read --allow-write | |
import { load } from "https://deno.land/[email protected]/dotenv/mod.ts"; | |
// import { copy, paste } from "https://deno.land/x/[email protected]/mod.ts"; | |
type TranslationProvider = "openai" | "ollama"; | |
// If you are not using wayland. | |
// commented out or remove the functions copy/paste | |
// and uncomment the import statement | |
export async function copy(text: string): Promise<void> { | |
const cmd = new Deno.Command("wl-copy", { | |
stdin: "piped", | |
}); | |
const process = cmd.spawn(); | |
const writer = process.stdin.getWriter(); | |
await writer.write(new TextEncoder().encode(text)); | |
await writer.close(); | |
const status = await process.status; | |
if (!status.success) { | |
throw new Error("Failed to copy to clipboard"); | |
} | |
} | |
export async function paste(): Promise<string> { | |
const cmd = new Deno.Command("wl-paste", { | |
args: ["-n"], | |
stdout: "piped", | |
}); | |
const process = cmd.spawn(); | |
const output = await process.output(); | |
const status = await process.status; | |
if (!status.success) { | |
throw new Error("Failed to paste from clipboard"); | |
} | |
return new TextDecoder().decode(output.stdout); | |
} | |
const DEFAULT_SYSTEM = | |
"Translate any user input into English, in an informal and concise way."; | |
const DEFAULT_MODEL = "gpt-4o-mini"; | |
async function notify(message: string, timeout: number = 2000): Promise<void> { | |
if (Deno.build.os === "windows") { | |
console.log(message); | |
} else { | |
const args = [ | |
"-r", | |
"417037", | |
"-t", | |
timeout.toString(), | |
"Translate", | |
message, | |
]; | |
const cmd = new Deno.Command("notify-send", { args }); | |
await cmd.output(); | |
} | |
} | |
async function translate( | |
text: string, | |
systemPrompt: string, | |
provider: TranslationProvider = "openai", | |
): Promise<string> { | |
const apiKey = Deno.env.get("OPENAI_API_KEY"); | |
const specificSystem = | |
`${systemPrompt}. Do not include any other explanation`; | |
const specificUser = `User: ${text}`; | |
if (provider === "openai" && !apiKey) { | |
throw new Error("OPENAI_API_KEY must be set in your environment"); | |
} | |
const url = provider === "ollama" | |
? "http://localhost:11434/api/generate" | |
: "https://api.openai.com/v1/chat/completions"; | |
const headers = { | |
"Content-Type": "application/json", | |
...(provider === "ollama" ? {} : { | |
"Authorization": `Bearer ${apiKey}`, | |
}), | |
}; | |
const body = provider === "ollama" | |
? JSON.stringify({ | |
prompt: `${specificSystem}\n${specificUser}`, | |
model: "llama3.1:8b", | |
stream: false, | |
}) | |
: JSON.stringify({ | |
model: DEFAULT_MODEL, | |
messages: [ | |
{ role: "system", content: systemPrompt }, | |
{ role: "user", content: text }, | |
], | |
}); | |
const response = await fetch(url, { | |
method: "POST", | |
headers, | |
body, | |
}); | |
const data = await response.json(); | |
if (provider === "ollama") { | |
return data.response; | |
} | |
return data.choices[0].message.content; | |
} | |
function sanitize(text: string): string { | |
return text.replace(/\r\n/g, "\n").replace("\r", ""); | |
} | |
async function main() { | |
await load({ export: true }); | |
try { | |
const systemPrompt = Deno.args.length > 0 | |
? Deno.args.join(" ") | |
: DEFAULT_SYSTEM; | |
await notify("Translating clipboard text..."); | |
const textFromClipboard = await paste(); | |
if (!textFromClipboard) { | |
throw new Error("No text found in clipboard."); | |
} | |
const provider: TranslationProvider = | |
Deno.env.get("TRANSLATE_PROVIDER") as TranslationProvider || "openai"; | |
const sanitizedText = sanitize(textFromClipboard); | |
const startTime = performance.now(); | |
const translatedText = await translate( | |
sanitizedText, | |
systemPrompt, | |
provider, | |
); | |
const endTime = performance.now(); | |
const duration = endTime - startTime; | |
await copy(translatedText); | |
await notify( | |
`Translation copied to clipboard. Done (${duration.toFixed(4)}ms)`, | |
5000, | |
); | |
} catch (error) { | |
if (error instanceof Error) { | |
await notify(`Error: ${error.message}`); | |
} else { | |
throw error; | |
} | |
} | |
} | |
if (import.meta.main) { | |
main(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment