-
Notifications
You must be signed in to change notification settings - Fork 9
Assistant improvements #209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1a09887
06c8f7c
dc83f45
25e0335
00d580c
7f7f5f6
4045ea5
af2095d
e8d30ac
08eefb8
7f82439
7fa7e17
c382804
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| // SPDX-FileCopyrightText: Nextcloud GmbH | ||
| // SPDX-FileCopyrightText: 2026 Milen Pivchev | ||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||
|
|
||
| import Foundation | ||
|
|
||
| // MARK: - ChatMessage | ||
|
|
||
| public struct AssistantChatMessage: Codable, Identifiable, Equatable { | ||
| public let id: Int | ||
| public let sessionId: Int | ||
| public let role: String | ||
| public let content: String | ||
| public let timestamp: Int | ||
|
|
||
| public var isFromHuman: Bool { | ||
| role == "human" | ||
| } | ||
|
|
||
| public init(id: Int, sessionId: Int, role: String, content: String, timestamp: Int) { | ||
| self.id = id | ||
| self.sessionId = sessionId | ||
| self.role = role | ||
| self.content = content | ||
| self.timestamp = timestamp | ||
| } | ||
|
|
||
| enum CodingKeys: String, CodingKey { | ||
| case id | ||
| case sessionId = "session_id" | ||
| case role | ||
| case content | ||
| case timestamp | ||
| } | ||
| } | ||
|
|
||
| // MARK: - ChatMessageRequest | ||
|
|
||
| public struct AssistantChatMessageRequest: Encodable { | ||
| public let sessionId: Int | ||
| public let role: String | ||
| public let content: String | ||
| public let timestamp: Int | ||
| public let firstHumanMessage: Bool | ||
|
|
||
| public init(sessionId: Int, role: String, content: String, timestamp: Int, firstHumanMessage: Bool) { | ||
| self.sessionId = sessionId | ||
| self.role = role | ||
| self.content = content | ||
| self.timestamp = timestamp | ||
| self.firstHumanMessage = firstHumanMessage | ||
| } | ||
|
|
||
| var bodyMap: [String: Any] { | ||
| return [ | ||
| "sessionId": sessionId, | ||
| "role": role, | ||
| "content": content, | ||
| "timestamp": timestamp | ||
| ] | ||
| } | ||
|
|
||
| enum CodingKeys: String, CodingKey { | ||
| case sessionId | ||
| case role | ||
| case content | ||
| case timestamp | ||
| case firstHumanMessage | ||
| } | ||
| } | ||
|
|
||
| // MARK: - Session | ||
|
|
||
| public struct AssistantConversation: Codable, Equatable, Hashable { | ||
| public let id: Int | ||
| public let userId: String? | ||
| private let title: String? | ||
| public let timestamp: Int | ||
|
|
||
| enum CodingKeys: String, CodingKey { | ||
| case id | ||
| case userId = "user_id" | ||
| case title | ||
| case timestamp | ||
| } | ||
|
|
||
| public var validTitle: String { | ||
| return title ?? createTitle() | ||
|
|
||
| func createTitle() -> String { | ||
| let date = Date(timeIntervalSince1970: TimeInterval(timestamp)) | ||
| let formatter = DateFormatter() | ||
| formatter.locale = .current | ||
| formatter.timeZone = .current | ||
| formatter.dateFormat = "MMM d yyyy, HH:mm" | ||
| return formatter.string(from: date) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // MARK: - CreateConversation | ||
|
|
||
| public struct AssistantCreatedConversation: Codable, Equatable { | ||
| public let conversation: AssistantConversation | ||
|
|
||
| enum CodingKeys: String, CodingKey { | ||
| case conversation = "session" | ||
| } | ||
| } | ||
|
|
||
| // MARK: - Session | ||
|
|
||
| public struct AssistantSession: Codable, Equatable { | ||
| public let messageTaskId: Int? | ||
| public let titleTaskId: Int? | ||
| public let sessionTitle: String? | ||
| public let sessionAgencyPendingActions: String? | ||
| public let taskId: Int? | ||
|
|
||
| enum CodingKeys: String, CodingKey { | ||
| case messageTaskId | ||
| case titleTaskId | ||
| case sessionTitle | ||
| case sessionAgencyPendingActions | ||
| case taskId | ||
| } | ||
| } | ||
|
|
||
| // MARK: - SessionTask | ||
|
|
||
| public struct AssistantSessionTask: Codable, Equatable { | ||
| public let taskId: Int | ||
|
|
||
| enum CodingKeys: String, CodingKey { | ||
| case taskId | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,30 +2,41 @@ | |
| // SPDX-FileCopyrightText: 2025 Milen Pivchev | ||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||
|
|
||
| import SwiftyJSON | ||
| import Foundation | ||
|
|
||
| // MARK: - OCS Response Wrappers | ||
|
|
||
| public struct OCSTaskListResponse: Codable { | ||
| public let ocs: OCSTaskListOCS | ||
|
|
||
| public struct OCSTaskListOCS: Codable { | ||
| public let data: OCSTaskListData | ||
| } | ||
|
|
||
| public struct OCSTaskListData: Codable { | ||
| public let tasks: [AssistantTask] | ||
| } | ||
| } | ||
|
|
||
| public struct OCSTaskResponse: Codable { | ||
| public let ocs: OCSTaskOCS | ||
|
|
||
| public struct OCSTaskOCS: Codable { | ||
| public let data: OCSTaskData | ||
| } | ||
|
|
||
| public struct OCSTaskData: Codable { | ||
| public let task: AssistantTask | ||
| } | ||
| } | ||
|
Comment on lines
+9
to
+31
|
||
|
|
||
| // MARK: - Task Models | ||
|
|
||
| public struct TaskList: Codable { | ||
| public var tasks: [AssistantTask] | ||
|
|
||
| static func deserialize(from data: JSON) -> TaskList? { | ||
| let tasks = data.arrayValue.map { taskJson in | ||
| AssistantTask( | ||
| id: taskJson["id"].int64Value, | ||
| type: taskJson["type"].string, | ||
| status: taskJson["status"].string, | ||
| userId: taskJson["userId"].string, | ||
| appId: taskJson["appId"].string, | ||
| input: TaskInput(input: taskJson["input"]["input"].string), | ||
| output: TaskOutput(output: taskJson["output"]["output"].string), | ||
| completionExpectedAt: taskJson["completionExpectedAt"].int, | ||
| progress: taskJson["progress"].int, | ||
| lastUpdated: taskJson["lastUpdated"].int, | ||
| scheduledAt: taskJson["scheduledAt"].int, | ||
| endedAt: taskJson["endedAt"].int | ||
| ) | ||
| } | ||
|
|
||
| return TaskList(tasks: tasks) | ||
| public init(tasks: [AssistantTask]) { | ||
| self.tasks = tasks | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -57,25 +68,6 @@ public struct AssistantTask: Codable { | |
| self.scheduledAt = scheduledAt | ||
| self.endedAt = endedAt | ||
| } | ||
|
|
||
| static func deserialize(from data: JSON) -> AssistantTask? { | ||
| let task = AssistantTask( | ||
| id: data["id"].int64Value, | ||
| type: data["type"].string, | ||
| status: data["status"].string, | ||
| userId: data["userId"].string, | ||
| appId: data["appId"].string, | ||
| input: TaskInput(input: data["input"]["input"].string), | ||
| output: TaskOutput(output: data["output"]["output"].string), | ||
| completionExpectedAt: data["completionExpectedAt"].int, | ||
| progress: data["progress"].int, | ||
| lastUpdated: data["lastUpdated"].int, | ||
| scheduledAt: data["scheduledAt"].int, | ||
| endedAt: data["endedAt"].int | ||
| ) | ||
|
|
||
| return task | ||
| } | ||
| } | ||
|
|
||
| public struct TaskInput: Codable { | ||
|
|
@@ -93,5 +85,3 @@ public struct TaskOutput: Codable { | |
| self.output = output | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,44 +2,34 @@ | |||||||||||||||||||||||||||
| // SPDX-FileCopyrightText: 2025 Milen Pivchev | ||||||||||||||||||||||||||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import SwiftyJSON | ||||||||||||||||||||||||||||
| import Foundation | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // MARK: - OCS Response Wrapper | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public struct OCSTaskTypesResponse: Codable { | ||||||||||||||||||||||||||||
| public let ocs: OCSTaskTypesOCS | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public struct OCSTaskTypesOCS: Codable { | ||||||||||||||||||||||||||||
| public let data: OCSTaskTypesData | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
Comment on lines
+12
to
+15
|
||||||||||||||||||||||||||||
| public struct OCSTaskTypesOCS: Codable { | |
| public let data: OCSTaskTypesData | |
| } | |
| public struct OCSTaskTypesOCS: Codable { | |
| public let meta: OCSMeta? | |
| public let data: OCSTaskTypesData | |
| } | |
| public struct OCSMeta: Codable { | |
| public let status: String? | |
| public let statuscode: Int? | |
| public let message: String? | |
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -12,7 +12,7 @@ final class NKInterceptor: RequestInterceptor, Sendable { | |||||
| self.nkCommonInstance = nkCommonInstance | ||||||
| } | ||||||
|
|
||||||
| func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) { | ||||||
| func adapt(_ urlRequest: URLRequest, for session: AssistantSession, completion: @escaping (Result<URLRequest, Error>) -> Void) { | ||||||
|
||||||
| func adapt(_ urlRequest: URLRequest, for session: AssistantSession, completion: @escaping (Result<URLRequest, Error>) -> Void) { | |
| func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ChatMessageRequestincludesfirstHumanMessage, butbodyMap(used as the request payload) omits it. Either includefirstHumanMessageinbodyMapor remove the property to avoid a mismatch between the model and what the API actually sends.