From fa711ef8725253385f769c9f00b10dfd64f288d1 Mon Sep 17 00:00:00 2001 From: Simon Weniger Date: Tue, 19 Sep 2023 20:51:18 +0200 Subject: [PATCH] add remaining functions --- Sources/Models/Agent.swift | 2 - .../Models/AnyOfValidationErrorLocItems.swift | 16 - Sources/Models/ApiToken.swift | 23 - Sources/Models/Datasource.swift | 24 + Sources/Models/Document.swift | 66 --- Sources/Models/HTTPValidationError.swift | 20 - Sources/Models/LLM.swift | 19 + Sources/Models/Prompt.swift | 28 - Sources/Models/SignIn.swift | 22 - Sources/Models/SignUp.swift | 26 - Sources/Models/String+Error.swift | 12 - Sources/Models/Tag.swift | 30 -- Sources/Models/Tool.swift | 26 +- Sources/Models/ValidationError.swift | 24 - Sources/Models/WorkflowStep.swift | 24 + Sources/main.swift | 482 +++++++++++++++++- 16 files changed, 555 insertions(+), 289 deletions(-) delete mode 100644 Sources/Models/AnyOfValidationErrorLocItems.swift delete mode 100644 Sources/Models/ApiToken.swift create mode 100644 Sources/Models/Datasource.swift delete mode 100644 Sources/Models/Document.swift delete mode 100644 Sources/Models/HTTPValidationError.swift create mode 100644 Sources/Models/LLM.swift delete mode 100644 Sources/Models/Prompt.swift delete mode 100644 Sources/Models/SignIn.swift delete mode 100644 Sources/Models/SignUp.swift delete mode 100644 Sources/Models/String+Error.swift delete mode 100644 Sources/Models/Tag.swift delete mode 100644 Sources/Models/ValidationError.swift create mode 100644 Sources/Models/WorkflowStep.swift diff --git a/Sources/Models/Agent.swift b/Sources/Models/Agent.swift index febcc1e..a650038 100644 --- a/Sources/Models/Agent.swift +++ b/Sources/Models/Agent.swift @@ -6,8 +6,6 @@ import Foundation - - public struct Agent: Codable { public var isActive: Bool diff --git a/Sources/Models/AnyOfValidationErrorLocItems.swift b/Sources/Models/AnyOfValidationErrorLocItems.swift deleted file mode 100644 index 5d53c94..0000000 --- a/Sources/Models/AnyOfValidationErrorLocItems.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// AnyOfValidationErrorLocItems.swift -// -// Created by Simon Weniger on 09.07.23. -// - -import Foundation - - - -public struct AnyOfValidationErrorLocItems: Codable { - - - - -} diff --git a/Sources/Models/ApiToken.swift b/Sources/Models/ApiToken.swift deleted file mode 100644 index a224a0e..0000000 --- a/Sources/Models/ApiToken.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// ApiToken.swift -// -// Created by Simon Weniger on 09.07.23. -// - -import Foundation - - - -public struct ApiToken: Codable { - - public var _description: String - - public init(_description: String) { - self._description = _description - } - - public enum CodingKeys: String, CodingKey { - case _description = "description" - } - -} diff --git a/Sources/Models/Datasource.swift b/Sources/Models/Datasource.swift new file mode 100644 index 0000000..55fd644 --- /dev/null +++ b/Sources/Models/Datasource.swift @@ -0,0 +1,24 @@ +// +// Datasource.swift +// +// +// Created by Simon Weniger (Aiden Technologies) on 19.09.23. +// + +import Foundation + +public struct Datasource { + + public var name: String + public var description: String + public var type: String + public var url: String + public var metadata: [String: Any]? + + public init(name: String, description: String, type: String, url: String, metadata: [String: Any]? = nil) { + self.name = name + self.description = description + self.type = type + self.url = url + } +} diff --git a/Sources/Models/Document.swift b/Sources/Models/Document.swift deleted file mode 100644 index 0710b4b..0000000 --- a/Sources/Models/Document.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// Document.swift -// -// Created by Simon Weniger on 09.07.23. -// - -import Foundation - - - -public struct Document { - - public var type: String - public var url: String? - public var description: String? - public var content: String - public var name: String - public var authorization: Any? - public var metadata: Any? - public var fromPage: Int? - public var toPage: Int? - public var splitter: Splitter? - - public init(type: String, url: String? = nil, name: String, description: String, content: String, authorization: Any? = nil, metadata: Any? = nil, fromPage: Int? = nil, toPage: Int? = nil, splitter: Splitter? = nil) { - self.type = type - self.url = url - self.description = description - self.content = content - self.name = name - self.authorization = authorization - self.metadata = metadata - self.fromPage = fromPage - self.toPage = toPage - self.splitter = splitter - } - - public enum CodingKeys: String, CodingKey { - case type - case url - case name - case description - case content - case authorization - case metadata - case fromPage = "from_page" - case toPage = "to_page" - case splitter - } - - public struct Splitter: Codable { - public var type: String - public var chunkSize: Int - public var chunkOverlap: Int - - public init(type: String, chunkSize: Int, chunkOverlap: Int) { - self.type = type - self.chunkSize = chunkSize - self.chunkOverlap = chunkOverlap - } - public enum CodingKeys: String, CodingKey { - case type - case chunkSize = "chunk_size" - case chunkOverlap = "chunk_overlap" - } - } -} diff --git a/Sources/Models/HTTPValidationError.swift b/Sources/Models/HTTPValidationError.swift deleted file mode 100644 index 01ed838..0000000 --- a/Sources/Models/HTTPValidationError.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// HTTPValidationError.swift -// -// Created by Simon Weniger on 09.07.23. -// - -import Foundation - - - -public struct HTTPValidationError: Codable { - - public var detail: [ValidationError]? - - public init(detail: [ValidationError]? = nil) { - self.detail = detail - } - - -} diff --git a/Sources/Models/LLM.swift b/Sources/Models/LLM.swift new file mode 100644 index 0000000..14ea88a --- /dev/null +++ b/Sources/Models/LLM.swift @@ -0,0 +1,19 @@ +// +// LLM.swift +// +// +// Created by Simon Weniger (Aiden Technologies) on 19.09.23. +// +import Foundation + +public struct LLM { + public var provider: String + public var apiKey: String + public var options: [String: Any]? + + public init(provider: String, apiKey: String, options: [String: Any]? = nil) { + self.provider = provider + self.apiKey = apiKey + self.options = options + } +} diff --git a/Sources/Models/Prompt.swift b/Sources/Models/Prompt.swift deleted file mode 100644 index 3818490..0000000 --- a/Sources/Models/Prompt.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// Prompt.swift -// -// Created by Simon Weniger on 09.07.23. -// - -import Foundation - - -public struct Prompt: Decodable { - - public var name: String - public var inputVariables: [String] - public var template: String - - public enum CodingKeys: String, CodingKey { - case name = "name" - case inputVariables = "input_variables" - case template = "template" - } - - public init(name: String, inputVariables: [String], template: String) { - self.name = name - self.inputVariables = inputVariables - self.template = template - } - -} diff --git a/Sources/Models/SignIn.swift b/Sources/Models/SignIn.swift deleted file mode 100644 index 3015f84..0000000 --- a/Sources/Models/SignIn.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// SignIn.swift -// -// Created by Simon Weniger on 09.07.23. -// - -import Foundation - - - -public struct SignIn: Codable { - - public var email: String - public var password: String - - public init(email: String, password: String) { - self.email = email - self.password = password - } - - -} diff --git a/Sources/Models/SignUp.swift b/Sources/Models/SignUp.swift deleted file mode 100644 index 7f2b078..0000000 --- a/Sources/Models/SignUp.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// SignUp.swift -// -// Created by Simon Weniger on 09.07.23. -// - -import Foundation - - - -public struct SignUp { - - public var email: String - public var password: String - public var name: String? - public var metadata: Any? - - public init(email: String, password: String, name: String? = nil, metadata: Any? = nil) { - self.email = email - self.password = password - self.name = name - self.metadata = metadata - } - - -} diff --git a/Sources/Models/String+Error.swift b/Sources/Models/String+Error.swift deleted file mode 100644 index 4b53b25..0000000 --- a/Sources/Models/String+Error.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// String+Error.swift -// -// -// Created by Simon Weniger on 09.07.23. -// - -import Foundation - -extension String: LocalizedError { - public var errorDescription: String? { return self } -} diff --git a/Sources/Models/Tag.swift b/Sources/Models/Tag.swift deleted file mode 100644 index 4ee47f6..0000000 --- a/Sources/Models/Tag.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// File.swift -// -// -// Created by vonweniger on 21.07.23. -// - -import Foundation - - - -public struct Tag { - - public var name: String - public var color: String - public var userId: String? - - public init(name: String, color: String, userId: String?) { - self.name = name - self.color = color - self.userId = color - } - - public enum CodingKeys: String, CodingKey { - case name - case color - case userId - } - -} diff --git a/Sources/Models/Tool.swift b/Sources/Models/Tool.swift index b490cc0..607289a 100644 --- a/Sources/Models/Tool.swift +++ b/Sources/Models/Tool.swift @@ -1,35 +1,25 @@ // // Tool.swift // -// Created by Simon Weniger on 09.07.23. +// Created by Simon Weniger (Aiden Technologies) on 09.07.23. // import Foundation - - public struct Tool { public var name: String + public var description: String public var type: String - public var _description: String - public var authorization: Any? - public var metadata: Any? + public var metadata: [String : Any]? + public var returnDirect: Bool? - public init(name: String, type: String, _description: String, authorization: Any? = nil, metadata: Any? = nil) { + public init(name: String, description: String, type: String, metadata: [String: Any]? = nil, returnDirect: Bool? = nil) { self.name = name + self.description = description self.type = type - self._description = _description - self.authorization = authorization self.metadata = metadata + self.returnDirect = returnDirect } - - public enum CodingKeys: String, CodingKey { - case name - case type - case _description = "description" - case authorization - case metadata - } - + } diff --git a/Sources/Models/ValidationError.swift b/Sources/Models/ValidationError.swift deleted file mode 100644 index 9c91f5d..0000000 --- a/Sources/Models/ValidationError.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// ValidationError.swift -// -// Created by Simon Weniger on 09.07.23. -// - -import Foundation - - - -public struct ValidationError: Codable { - - public var loc: [AnyOfValidationErrorLocItems] - public var msg: String - public var type: String - - public init(loc: [AnyOfValidationErrorLocItems], msg: String, type: String) { - self.loc = loc - self.msg = msg - self.type = type - } - - -} diff --git a/Sources/Models/WorkflowStep.swift b/Sources/Models/WorkflowStep.swift new file mode 100644 index 0000000..e8d62b9 --- /dev/null +++ b/Sources/Models/WorkflowStep.swift @@ -0,0 +1,24 @@ +// +// Workflow.swift +// +// +// Created by Simon Weniger (Aiden Technologies) on 19.09.23. +// + +import Foundation + +public struct WorkflowStep { + + public var order: Int + public var agentId: String + public var input: String + public var output: String + + public init(order: Int, agentId: String, input: String, output: String) { + self.order = order + self.agentId = agentId + self.input = input + self.output = output + } + +} diff --git a/Sources/main.swift b/Sources/main.swift index 4cb5873..225dea9 100644 --- a/Sources/main.swift +++ b/Sources/main.swift @@ -23,7 +23,6 @@ enum SuperagentError: Error { case failedToCreate } -@available(macOS 12.0, *) public struct SuperagentSDK { public var baseUrl: String @@ -152,7 +151,7 @@ public struct SuperagentSDK { "llmModel": newAgent.llmModel, "description": newAgent.description, "avatar": newAgent.avatar ?? ""] - let data = try await request(method: .post, endpoint: "/agents/\(agentId)", data: payload) + let data = try await request(method: .patch, endpoint: "/agents/\(agentId)", data: payload) guard let responseData = data as? [String: Any], let agentData = responseData["data"] as? [String: Any] else { @@ -367,4 +366,483 @@ public struct SuperagentSDK { //Create ///Create a new LLM + public func createLLM(provider: String, apiKey: String, options: [String: Any]?) async throws -> [String: Any] { + var payload: [String: Any] = ["provider": provider, "apiKey": apiKey, "options": options as Any] + + let data = try await request(method: .post, endpoint: "/llms", data: payload) + + guard let responseData = data as? [String: Any], + let llm = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("create new llm result: \(llm)") +#endif + + return llm + } + + //Get + ///Get a single LLM + public func getLLM(llmId: String) async throws -> [String: Any] { + let data = try await request(method: .get, endpoint: "/llms/\(llmId)") + + guard let responseData = data as? [String: Any], + let llm = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("get LLM result: \(llm)") +#endif + + return llm + } + + //Update + ///Patch an LLM + public func createLLM(llmId: String ,provider: String, apiKey: String, options: [String: Any]?) async throws -> [String: Any] { + var payload: [String: Any] = ["provider": provider, "apiKey": apiKey] + + if let options = options { + payload["options"] = options + } else { + payload["options"] = nil + } + + let data = try await request(method: .patch, endpoint: "/llms/\(llmId)", data: payload) + + guard let responseData = data as? [String: Any], + let llm = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("create new llm result: \(llm)") +#endif + + return llm + } + + //MARK: - API User + + //Create + ///Create a new API user + public func createNewApiUser() async throws -> [String: Any] { + let data = try await request(method: .post, endpoint: "/api-users") + + guard let responseData = data as? [String: Any], + let success = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("create new API user result: \(success)") +#endif + + return success + } + + //Get + ///Get a single api user + public func getApiUser() async throws -> [String: Any] { + let data = try await request(method: .get, endpoint: "/api-users/me") + + guard let responseData = data as? [String: Any], + let success = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("get API user result: \(success)") +#endif + + return success + } + + //Delete + ///Delete an api user + public func deleteApiUser() async throws -> [String: Any] { + let data = try await request(method: .delete, endpoint: "/api-users/me") + + guard let responseData = data as? [String: Any], + let success = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("delete API user result: \(success)") +#endif + + return success + } + + //MARK: - Datasource + + //List + ///List all datasources + public func listDatasources() async throws -> [String: Any] { + let data = try await request(method: .get, endpoint: "/datasources") + + guard let responseData = data as? [String: Any], + let datasources = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("list datasources result: \(datasources)") +#endif + + return datasources + } + //Create + ///Create a new datasource + public func createDatasource(datasource: Datasource) async throws -> [String: Any] { + var payload: [String: Any] = ["name": datasource.name, + "description": datasource.description, + "type": datasource.type, + "url": datasource.url, + "metadata": datasource.metadata as Any] + + let data = try await request(method: .post, endpoint: "/datasources", data: payload) + + guard let responseData = data as? [String: Any], + let llm = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("create new llm result: \(llm)") +#endif + + return llm + } + + //Get + ///Get a specific datasource + public func getDatasource(datasourceId: String) async throws -> [String: Any] { + let data = try await request(method: .get, endpoint: "/datasources/\(datasourceId)") + + guard let responseData = data as? [String: Any], + let datasource = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("get datasource result: \(datasource)") +#endif + + return datasource + } + + //Update + ///Update a specific datasource + public func updateDatasource(datasourceId: String , newDatasource: Datasource) async throws -> [String: Any] { + var payload: [String: Any] = ["name": newDatasource.name, + "description": newDatasource.description, + "type": newDatasource.type, + "url": newDatasource.url] + + if let metadata = newDatasource.metadata { + payload["metadata"] = metadata + } else { + payload["metadata"] = nil + } + + let data = try await request(method: .patch, endpoint: "/datasources/\(datasourceId)", data: payload) + + guard let responseData = data as? [String: Any], + let datasource = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("update datasource result: \(datasource)") +#endif + + return datasource + } + + //Delete + ///Delete a specific datasource + public func deleteDatasource(datasourceId: String) async throws -> [String: Any] { + let data = try await request(method: .delete, endpoint: "/datasources/\(datasourceId)") + + guard let responseData = data as? [String: Any], + let success = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("delete Datasource result: \(success)") +#endif + + return success + } + + //MARK: - Tool + + //List + ///List all tools + public func listTools() async throws -> [String: Any] { + let data = try await request(method: .get, endpoint: "/tools") + + guard let responseData = data as? [String: Any], + let tools = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("list tools result: \(tools)") +#endif + + return tools + } + + //Create + ///Create a new tool + public func createTool(tool: Tool) async throws -> [String: Any] { + var payload: [String: Any] = ["name": tool.name, + "description": tool.description, + "type": tool.type, + "metadata": tool.metadata as Any, + "returnDirect": tool.returnDirect as Any] + + let data = try await request(method: .post, endpoint: "/tools", data: payload) + + guard let responseData = data as? [String: Any], + let tool = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("create new tool result: \(tool)") +#endif + + return tool + } + + //Get + ///Get a specific tool + public func getTool(toolId: String) async throws -> [String: Any] { + let data = try await request(method: .get, endpoint: "/tool/\(toolId)") + + guard let responseData = data as? [String: Any], + let tool = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("get tool result: \(tool)") +#endif + + return tool + } + + //Update + ///Update a specific tool + public func updateTool(toolId: String , newTool: Tool) async throws -> [String: Any] { + var payload: [String: Any] = ["name": newTool.name, + "description": newTool.description, + "type": newTool.type, + "metadata": newTool.metadata as Any, + "returnDirect": newTool.returnDirect as Any] + + let data = try await request(method: .post, endpoint: "/tools/\(toolId)", data: payload) + + guard let responseData = data as? [String: Any], + let tool = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("create new tool result: \(tool)") +#endif + + return tool + } + + //Delete + ///Delete a specific tool + public func deleteTool(toolId: String) async throws -> [String: Any] { + let data = try await request(method: .delete, endpoint: "/tools/\(toolId)") + + guard let responseData = data as? [String: Any], + let success = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("delete Datasource result: \(success)") +#endif + + return success + } + + //MARK: - Workflow + + //List + ///List all workflows + public func listWorkflows() async throws -> [String: Any] { + let data = try await request(method: .get, endpoint: "/workflows") + + guard let responseData = data as? [String: Any], + let workflows = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("list workflows result: \(workflows)") +#endif + + return workflows + } + + //Create + ///Create a new workflow + public func createWorkflow(name: String, description: String) async throws -> [String: Any] { + + let data = try await request(method: .post, endpoint: "workflows", data: ["name": name, "description": description]) + + guard let responseData = data as? [String: Any], + let workflow = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + + #if DEBUG + Swift.print("create Workflow result: \(workflow)") + #endif + + return workflow + } + + //Get + ///Get a single workflow + public func getWorkflow(workflowId: String) async throws -> [String: Any] { + let data = try await request(method: .get, endpoint: "workflows/\(workflowId)") + + guard let responseData = data as? [String: Any], + let workflow = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + + #if DEBUG + Swift.print("get Workflow result: \(workflow)") + #endif + + return workflow + } + + //Update + ///Patch a workflow + public func updateWorkflow(workflowId: String, newName: String, newDescription: String) async throws -> [String: Any] { + let data = try await request(method: .get, endpoint: "workflows/\(workflowId)", data: ["name": newName, "description": newDescription]) + + guard let responseData = data as? [String: Any], + let workflow = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + + #if DEBUG + Swift.print("get Workflow result: \(workflow)") + #endif + + return workflow + } + + //Delete + ///Delete a specific workflow + public func deleteWorkflow(workflowId: String) async throws -> [String: Any] { + let data = try await request(method: .delete, endpoint: "workflows/\(workflowId)") + + guard let responseData = data as? [String: Any], + let workflow = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + + #if DEBUG + Swift.print("get Workflow result: \(workflow)") + #endif + + return workflow + } + + //Invoke + ///Invoke a specific workflow + public func invokeWorkflow(workflowId: String, input: String, enableStreaming: Bool) async throws -> [String: Any] { + let data = try await request(method: .post, endpoint: "workflows/\(workflowId)", data: ["input": input, "enableStreaming": enableStreaming]) + + guard let responseData = data as? [String: Any], + let workflowResponse = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + + #if DEBUG + Swift.print("invoke Workflow result: \(workflowResponse)") + #endif + + return workflowResponse + } + + //List Steps + ///List all steps of a workflow + public func listWorkflowSteps(workflowId: String) async throws -> [String: Any] { + let data = try await request(method: .get, endpoint: "workflows/\(workflowId)/steps") + + guard let responseData = data as? [String: Any], + let workflowSteps = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + + #if DEBUG + Swift.print("list Workflow Steps result: \(workflowSteps)") + #endif + + return workflowSteps + } + + + //Add Step + ///Create a new workflow step + public func addWorkflowStep(workflowId: String ,workflowStep: WorkflowStep) async throws -> [String: Any] { + var payload: [String: Any] = ["order": workflowStep.order, + "agentId": workflowStep.agentId, + "input": workflowStep.input, + "output": workflowStep.output] + + let data = try await request(method: .post, endpoint: "/workflows/\(workflowId)/steps", data: payload) + + guard let responseData = data as? [String: Any], + let workflowStep = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + +#if DEBUG + Swift.print("add workflow step result: \(workflowStep)") +#endif + + return workflowStep + } + + //Delete Step + ///Delete a specific workflow step + public func deleteWorkflowStep(workflowId: String, stepId: String) async throws -> [String: Any] { + let data = try await request(method: .delete, endpoint: "workflows/\(workflowId)/steps\(stepId)") + + guard let responseData = data as? [String: Any], + let success = responseData["data"] as? [String: Any] else { + throw SuperagentError.failedToRetrieve + } + + #if DEBUG + Swift.print("delete workflow step result: \(success)") + #endif + + return success + + } + + +}