Skip to content

Instantly share code, notes, and snippets.

@rcarmo
Last active February 28, 2024 19:57
Show Gist options
  • Save rcarmo/f96c659f149e357e1091cbfe352af6d4 to your computer and use it in GitHub Desktop.
Save rcarmo/f96c659f149e357e1091cbfe352af6d4 to your computer and use it in GitHub Desktop.
macOS Services in the style of NotesOllama
# Drop this into Automator using Python 3 as the shell
from sys import stdin
from json import dumps, loads
from urllib.parse import urlencode
from urllib.request import Request, urlopen
AZURE_ENDPOINT="getyourowndeployment.openai.azure.com"
OPENAI_API_KEY="getyourownkey"
OPENAI_API_VERSION="2023-05-15"
DEPLOYMENT_NAME="default" # I've deployed chatgpt35-turbo under this label
url = f"https://{AZURE_ENDPOINT}/openai/deployments/{DEPLOYMENT_NAME}/chat/completions?api-version={OPENAI_API_VERSION}"
headers = {"api-key": OPENAI_API_KEY, "content-type": "application/json; charset=UTF-8"}
prompts = [ # shamelessly stolen from https://github.com/andersrex/notesollama/blob/main/NotesOllama/Menu/commands.swift (MIT licensed)
{
"name": "Summarize selection",
"prompt": "Act as a writer. Summarize the text in a view sentences highlighting the key takeaways. Output only the text and nothing else, do not chat, no preamble, get to the point."
},
{
"name": "Explain selection",
"prompt": "Act as a writer. Explain the text in simple and concise terms keeping the same meaning. Output only the text and nothing else, do not chat, no preamble, get to the point."
},
{
"name": "Expand selection",
"prompt": "Act as a writer. Expand the text by adding more details while keeping the same meaning. Output only the text and nothing else, do not chat, no preamble, get to the point."
},
{
"name": "Answer selection",
"prompt": "Act as a writer. Answer the question in the text in simple and concise terms. Output only the text and nothing else, do not chat, no preamble, get to the point."
},
{
"name": "Rewrite selection (formal)",
"prompt": "Act as a writer. Rewrite the text in a more formal style while keeping the same meaning. Output only the text and nothing else, do not chat, no preamble, get to the point."
},
{
"name": "Rewrite selection (casual)",
"prompt": "Act as a writer. Rewrite the text in a more casual style while keeping the same meaning. Output only the text and nothing else, do not chat, no preamble, get to the point."
},
{
"name": "Rewrite selection (active voice)",
"prompt": "Act as a writer. Rewrite the text in with an active voice while keeping the same meaning. Output only the text and nothing else, do not chat, no preamble, get to the point."
},
{
"name": "Rewrite selection (bullet points)",
"prompt": "Act as a writer. Rewrite the text into bullet points while keeping the same meaning. Output only the text and nothing else, do not chat, no preamble, get to the point."
},
{
"name": "Caption selection",
"prompt": "Act as a writer. Create only one single heading for the whole text that is giving a good understanding of what the reader can expect. Output only the caption and nothing else, do not chat, no preamble, get to the point. Your format should be ## Caption."
}
]
prompt = prompts[0]["prompt"] # adjust the index to select a different prompt
data = {
"temperature": 0.4,
"messages": [{
"role": "system",
"content": prompt
},{
"role": "user",
"content": stdin.read()
}]#,{
# role: "assistant",
# Use this to guide the LLM if you need JSON formatting
# content: ""
#}
}
req = Request(url, data=dumps(data).encode(), method="POST", headers=headers)
with urlopen(req) as response:
print(loads(response.read().decode())["choices"][0]["message"]["content"])
function run(input, parameters) {
// This script is a simple example of how to use the Azure OpenAI API to generate text
// from a macOS system service written as a JavaScript for Automation (JXA) script
// You can drop this into a Shortcuts "Run JavaScript" action (which will only work on a Mac)
// or use it as a starting point for a more complex system service
ObjC.import('Foundation')
ObjC.import('Cocoa')
let app = Application.currentApplication();
app.includeStandardAdditions = true
let AZURE_ENDPOINT = "my_endpoint.openai.azure.com",
DEPLOYMENT_NAME = "my_deployment",
// You should store your keys in the macOS Keychain and retrieve them from there:
OPENAI_API_KEY = app.doShellScript(`security find-generic-password -w -s ${AZURE_ENDPOINT} -a ${DEPLOYMENT_NAME}`)
OPENAI_API_VERSION = "2023-05-15",
url = `https://${AZURE_ENDPOINT}/openai/deployments/${DEPLOYMENT_NAME}/chat/completions?api-version=${OPENAI_API_VERSION}`,
postData = {
"temperature": 0.4,
"messages": [{
"role": "system",
"content": "Act as a writer. Expand the text by adding more details while keeping the same meaning. Output only the text and nothing else, do not chat, no preamble, get to the point."
,
}, {
"role": "user",
"content": input.join("\n")
}]/*,{
role: "assistant",
Use this if you need JSON formatting
content: ""
*/
},
request = $.NSMutableURLRequest.requestWithURL($.NSURL.URLWithString(url));
request.setHTTPMethod("POST");
request.setHTTPBody($.NSString.alloc.initWithUTF8String(JSON.stringify(postData)).dataUsingEncoding($.NSUTF8StringEncoding));
request.setValueForHTTPHeaderField("application/json; charset=UTF-8", "Content-Type");
request.setValueForHTTPHeaderField(OPENAI_API_KEY, "api-key");
let error = $(),
response = $(),
data = $.NSURLConnection.sendSynchronousRequestReturningResponseError(request, response, error);
if (error[0]) {
return "Error: " + error[0].localizedDescription;
} else {
var json = JSON.parse($.NSString.alloc.initWithDataEncoding(data, $.NSUTF8StringEncoding).js);
if (json.error) {
return json.error.message;
} else {
return json.choices[0].message.content;
}
}
}
import Cocoa
let url = URL(string:"https://endpoint_name.openai.azure.com/openai/deployments/deployment_name/chat/completions?api-version=2023-05-15")!,
prompt = "Do what you are told. No more, no less."
struct Role: Codable {
let role: String
let content: String
}
struct Completion: Codable {
let temperature: Float
let messages: [Role]
}
struct Response: Codable {
let id: String
let object: String
let created: Int
let model: String
let choices: [Choice]
let usage: Usage
}
struct Choice: Codable {
let finish_reason: String
let index: Int
let message: Message
}
struct Message: Codable {
let role: String
let content: String
}
struct Usage: Codable {
let prompt_tokens: Int
let completion_tokens: Int
let total_tokens: Int
}
let data = Completion(
temperature: 0.4,
messages: [
Role(role:"system", content: prompt),
Role(role:"user", content: "Say Hello")
]
)
let encoder = JSONEncoder()
if let jsonData = try? encoder.encode(data),
let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
var request = URLRequest(url: url);
request.httpMethod = "POST"
request.httpBody = jsonData
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.setValue("an_api_key", forHTTPHeaderField: "api-key")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let decoder = JSONDecoder()
do {
let response = try decoder.decode(Response.self, from: data)
print(response.choices[0].message.content)
} catch {
print("Error decoding JSON: \(error)")
}
}
// note that this also makes it hard to handle control flow
task.resume()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment