diff --git a/README.md b/README.md index 0f1ffd5..6450947 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ When you select text with the mouse or through the keyboard (cmd+A, cmd+shift+ar 1. Allows for the customization of operation lists for different applications. (This can be configured in "Settings - Applications") -2. Supports customizing the addresses and keys for OpenAI and Gemini API. The translation and inquiry GPT functions depend on this. +2. Supports customizing the addresses and keys for OpenAI and Gemini API. The translation and inquiry GPT functions depend on this. It also supports OpenAI's function calling. You can have GPT perform searches, fetch web content, write emails, or control your macOS, and virtually anything else. 3. Supports custom extensions. # Supported Applications @@ -85,18 +85,25 @@ actions: | action.meta | object | Meta information of the Action | | action.meta.title | string | Action title. Used to display the name of the operation when the mouse hovers over the toolbar. | | action.meta.icon | string | The setup is the same as info.icon. It is used for display on the toolbar. | -| action.meta.identifier | string | action's id, unique identifier | +| action.meta.identifier | string | action's id, unique identifier. | | action.meta.after | string | Handling after the action is executed. Required. Supports configuration of empty (`""`), `paste`, `copy`, `show`. | | action.meta.regex | string | Regular expressions, used to match selected text, only display action when a match occurs. Optional values. | -| action.url | object | Action of URL type | +| action.url | object | Action of URL type. | | action.url.url | string | A link that, upon clicking (action), will open this link. It supports schemes to open other apps. For example, `https://www.google.com.hk/search?q={selected.text} `for conducting a Google search. Or open `things:///add?title={selected.text}&show-quick-entry=true` to add a task in Things3. `{selected.text}` is used to replace the selected text. | -| action.service | object | Action of service type | +| action.service | object | Action of service type. | | action.service.name | string | Service Name怂For example, `Make Sticky` creates a new note (note application). | -| action.keycombo | object | Shortcut key type action | +| action.keycombo | object | Shortcut key type action. | | action.keycombo.keycombo | string | Shortcut keys, such as "cmd i", etc. Support for function keys like "cmd", "shift", "ctrl", "option", "fn", "caps", as well as lowercase letters, numbers, symbols, and other key positions. Key positioning support is not yet complete, pending further testing and improvement. | +| action.keycombo.keycombos | string list | A list of Shortcut keys. Only one of keycombo or keycombos can be set in action.keycombo.| | action.gpt | object | To interact with GPT, such as OpenAI (3.5 turbo model) or Gemini, you need to configure the relevant API key in the settings. | | action.gpt.prompt | string | GPT prompt words, such as `enriching and refining the following content. The content reads: {selected.text}.` Use `{selected.text}` to replace the selected text. | -| action.runCommand | object | Execute a command | +| action.gpt.tools | tool list | GPT function calling definition. A list of tool. | +| tool.name | string | GPT function name. | +| tool.description| string | GPT function description. | +| tool.parameters| string | JSON schema of GPT function parameters.| +| tool.command| string list | When call the function, run the command. | +| tool.showResult| boolean | Whether show function result in GUI window. | +| action.runCommand | object | Execute a command. | | action.runCommand.command | string | Command and parameter list. The working directory during command execution is the plugin directory. The environment variables currently provided include: `SELECTED_TEXT` and `SELECTED_BUNDLEID`, which represent the currently selected text and the current application, respectively. | Each action can and must be configured with only one of the following: action.url, action.service, action.keycombo, action.gpt, or action.runCommand. diff --git a/Selected.xcodeproj/project.pbxproj b/Selected.xcodeproj/project.pbxproj index 6d1b4da..ea9279b 100644 --- a/Selected.xcodeproj/project.pbxproj +++ b/Selected.xcodeproj/project.pbxproj @@ -759,7 +759,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 9; + CURRENT_PROJECT_VERSION = 10; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "Selected/Preview\\ Content"; DEVELOPMENT_TEAM = K38MUDUK3K; @@ -775,7 +775,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = "0.2.1-beta"; + MARKETING_VERSION = 0.2.1; PRODUCT_BUNDLE_IDENTIFIER = io.kitool.Selected.dev; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -792,7 +792,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 9; + CURRENT_PROJECT_VERSION = 10; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "Selected/Preview\\ Content"; DEVELOPMENT_TEAM = K38MUDUK3K; @@ -808,7 +808,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = "0.2.1-beta"; + MARKETING_VERSION = 0.2.1; PRODUCT_BUNDLE_IDENTIFIER = io.kitool.Selected; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/Selected/Plugin/GPTAction.swift b/Selected/Plugin/GPTAction.swift index 27b9b07..1e7dd34 100644 --- a/Selected/Plugin/GPTAction.swift +++ b/Selected/Plugin/GPTAction.swift @@ -9,7 +9,7 @@ import Foundation class GptAction: Decodable{ var prompt: String - var tool: [FunctionDefinition]? + var tools: [FunctionDefinition]? init(prompt: String) { self.prompt = prompt @@ -25,8 +25,8 @@ class GptAction: Decodable{ }) } else { var chatService: AIChatService = ChatService(prompt: prompt) - if let tool = tool { - chatService = OpenAIService(prompt: prompt, functionDef: tool) + if let tools = tools { + chatService = OpenAIService(prompt: prompt, tools: tools) } return PerformAction( diff --git a/Selected/Plugin/PluginInfo.swift b/Selected/Plugin/PluginInfo.swift index 86c178b..9ece1fc 100644 --- a/Selected/Plugin/PluginInfo.swift +++ b/Selected/Plugin/PluginInfo.swift @@ -214,13 +214,13 @@ class PluginManager: ObservableObject { runCommand.pluginPath = extensionsDir.appendingPathComponent(pluginDir, isDirectory: true).path } - if let gpt = action.gpt, var tools = gpt.tool { + if let gpt = action.gpt, var tools = gpt.tools { tools = tools.map { tool in var mutTool = tool mutTool.workdir = extensionsDir.appendingPathComponent(pluginDir, isDirectory: true).path return mutTool } - gpt.tool = tools + gpt.tools = tools } if let regex = action.meta.regex { diff --git a/Selected/Service/AI.swift b/Selected/Service/AI.swift index 225bdbe..c0e8cf7 100644 --- a/Selected/Service/AI.swift +++ b/Selected/Service/AI.swift @@ -106,12 +106,12 @@ class OpenAIService: AIChatService{ var openAI: OpenAIPrompt - init(prompt: String, functionDef: [FunctionDefinition]? = nil) { + init(prompt: String, tools: [FunctionDefinition]? = nil) { var fcs = [FunctionDefinition]() - if let def = functionDef { - fcs.append(contentsOf: def) + if let tools = tools { + fcs.append(contentsOf: tools) } - openAI = OpenAIPrompt(prompt: prompt, function: fcs) + openAI = OpenAIPrompt(prompt: prompt, tools: fcs) } func chat(content: String, options: [String:String], completion: @escaping (_: Int, _: ResponseMessage) -> Void) async -> Void{ diff --git a/Selected/Service/OpenAI.swift b/Selected/Service/OpenAI.swift index 8ec4498..43f2a9a 100644 --- a/Selected/Service/OpenAI.swift +++ b/Selected/Service/OpenAI.swift @@ -59,16 +59,16 @@ typealias FunctionParameters = ChatQuery.ChatCompletionToolParam.FunctionDefinit struct OpenAIPrompt { let prompt: String - var function: [FunctionDefinition]? + var tools: [FunctionDefinition]? let openAI: OpenAI var query: ChatQuery - init(prompt: String, function: [FunctionDefinition]? = nil) { + init(prompt: String, tools: [FunctionDefinition]? = nil) { self.prompt = prompt - self.function = function + self.tools = tools let configuration = OpenAI.Configuration(token: Defaults[.openAIAPIKey] , host: Defaults[.openAIAPIHost] , timeoutInterval: 60.0) self.openAI = OpenAI(configuration: configuration) - self.query = OpenAIPrompt.createQuery(function: function) + self.query = OpenAIPrompt.createQuery(functions: tools) } @@ -99,11 +99,11 @@ struct OpenAIPrompt { } - private static func createQuery(function: [FunctionDefinition]?) -> ChatQuery { + private static func createQuery(functions: [FunctionDefinition]?) -> ChatQuery { var tools: [ChatQuery.ChatCompletionToolParam]? = nil - if let function = function { + if let functions = functions { var _tools: [ChatQuery.ChatCompletionToolParam] = [.init(function: dalle3Def)] - for fc in function { + for fc in functions { let fc = ChatQuery.ChatCompletionToolParam.FunctionDefinition( name: fc.name, description: fc.description, @@ -245,7 +245,7 @@ struct OpenAIPrompt { index: inout Int, toolCallsDict: [Int: ChatCompletionMessageToolCallParam], completion: @escaping (_: Int, _: ResponseMessage) -> Void) async -> [ChatQuery.ChatCompletionMessageParam] { - guard let fcs = function else { + guard let fcs = tools else { return [] }