Plug-In: AFM • Solar Power Example
REQUIRES: macOS 26, iOS 26, iPados 26, visionOS 26 • OmniFocus 4.8
Based upon the example in the Omni Automation API, this plug-in demonstrates interacting with the on-device Apple Foundation Model (AFM) through an Omni Automation plug-in.
It will create a new OmniFocus project containing a set of tasks representing the steps necessary to install solar power in a home.
The plug-in provides the on-device Apple Language Models (AFM) with an example JSON schema showing how to organize the response data. Once the AFM has returned the response, it is parsed to create a new multi-level project. In addition, tasks are assigned priority level though the use of mutually-exclusive tags.
Documentation
Complete documentation regarding the integration of Omni Automation with the Apple Foundation Models frameworks is available in two sections:
- Requesting and working with text-based responses, and…
- Working with schema and JSON responses
AFM • Solar Power Example
/*{
"type": "action",
"targets": ["omnifocus"],
"author": "Otto Automator",
"identifier": "com.omni-automation.of.solar-project-example",
"version": "1.1",
"description": "An example plug-in that uses the on-device Apple Language Model to generate an OmniFocus project detailing the steps required to add solar power to a home.",
"label": "AFM • Solar Project Example",
"shortLabel": "AFM • Solar Project",
"paletteLabel": "AFM • Solar Project",
"image": "apple.intelligence"
}*/
(() => {
// SCHEMA FOR THE AFM
var schemaJSON = {
arrayOf: {
name: "step-schema",
properties: [
{
name: "title",
isOptional: false
},
{
name: "description",
isOptional: false
},
{
name: "priority",
schema: {
name: "priority-schema",
anyOf: [
{
constant: "high"
},
{
constant: "low"
}
]
}
},
{
name: "substeps",
description: "A breakdown of steps.",
isOptional: true,
schema: {
arrayOf: {
referenceTo: "step-schema"
},
minimumElements: 1
}
}
]
}
}
// FUNCTION FOR CREATING PROJECT USING AFM RESPONSE
function insertJsonUnderParent(steps, parent) {
steps.forEach(step => {
title = step.title
description = step.description
priority = step.priority
console.log("Title:", title)
console.log("Description:", description)
console.log("Priority:", priority)
task = new Task(title, parent)
task.note = description
priorityTag = (priority === "low") ? lowTag:highTag
task.addTag(priorityTag)
console.log("Priority Tag:", priorityTag)
console.log("---------------------")
if (step['substeps']) {
insertJsonUnderParent(step['substeps'], task);
}
});
}
function expandNotes(object, window){
contentTree = window.content;
node = contentTree.nodeForObject(object);
node.expandNote(true);
}
const action = new PlugIn.Action(async function(selection, sender){
try {
// CREATE PROJECT
project = new Project("Thinking…")
window = document.windows[0]
window.perspective = Perspective.BuiltIn.Projects
window.focus = [project]
// QUERY THE ON-DEVICE APPLE LANGUAGE MODEL
schema = LanguageModel.Schema.fromJSON(schemaJSON)
prompt = "Provide a list of steps required to add solar power to a home."
session = new LanguageModel.Session()
options = new LanguageModel.GenerationOptions()
options.maximumResponseTokens = 4096
response = await session.respondWithSchema(prompt, schema)
responseJSON = JSON.parse(response)
console.log(JSON.stringify(response, null, 2))
// CREATE MUTUALLY-EXCLUSIVE PRIORITY TAG GROUP AS NEEDED
tagGroup = flattenedTags.byName("Priority") || new Tag("Priority")
lowTag = tagGroup.tagNamed("Low") || new Tag("Low", tagGroup.beginning)
highTag = tagGroup.tagNamed("High") || new Tag("High", tagGroup.beginning)
tagGroup.childrenAreMutuallyExclusive = true
// RENAME PROJECT
project.name = "Home Solar Project"
// CREATE PROJECT HEIRARCHY
insertJsonUnderParent(response, project)
// DISPLAY PROJECT
project.url.open()
expandNotes(project, selection.window)
}
catch(err){
if(project){
document.windows[0].focus = []
deleteObject(project)
}
if(!err.causedByUserCancelling){
console.error(err.name, err.message)
new Alert(err.name, err.message).show()
}
}
});
action.validate = function(selection, sender){
// VERIFY DEVICE IS RUNNING OS26 OR NEWER
compatibility = Device.current.operatingSystemVersion.atLeast(new Version("26"))
return compatibility
};
return action;
})();