Skip to content

Assistant improvements#209

Merged
mpivchev merged 13 commits intomainfrom
assistant-improvements
Feb 13, 2026
Merged

Assistant improvements#209
mpivchev merged 13 commits intomainfrom
assistant-improvements

Conversation

@mpivchev
Copy link
Collaborator

@mpivchev mpivchev commented Feb 9, 2026

No description provided.

Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>
Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>
Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>
Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>
Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>
Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>
Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>
Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>
@mpivchev mpivchev linked an issue Feb 9, 2026 that may be closed by this pull request
6 tasks
@mpivchev mpivchev marked this pull request as ready for review February 12, 2026 09:19
@mpivchev mpivchev enabled auto-merge (squash) February 12, 2026 09:20
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Assistant V2 client APIs by migrating parsing from SwiftyJSON to Codable, converting several endpoints to async/await-style APIs, and adding new Assistant chat/session endpoints.

Changes:

  • Replace SwiftyJSON parsing for Assistant V2 task types/tasks with Codable response models.
  • Refactor Assistant V2 endpoints to async functions and add new Assistant chat/session APIs.
  • Introduce new v2 chat models (ChatMessage, conversations, session status/task models).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
Sources/NextcloudKit/NextcloudKit+AssistantV2.swift Refactors V2 assistant endpoints to async and adds chat-related endpoints; switches to Codable decoding.
Sources/NextcloudKit/NKInterceptor.swift Modifies Alamofire interceptor adapt signature (currently breaks protocol conformance).
Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift Adds Codable OCS wrapper + updates task type model to support decoding.
Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift Adds Codable OCS wrappers for task list/task response and removes SwiftyJSON parsing helpers.
Sources/NextcloudKit/Models/Assistant/v2/Chat.swift Adds new Codable chat-related models used by the new Assistant chat endpoints.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 311 to 313
let result = try? decoder.decode([ChatMessage].self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, chatMessages: result, responseData: response, error: .success))
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same pattern here: try? decode + always returning .success means unexpected payloads become silent successes with chatMessages: nil. Prefer do/catch and return an error (e.g. .invalidData) when decoding fails (and confirm whether an OCS envelope is expected for this endpoint).

Suggested change
let result = try? decoder.decode([ChatMessage].self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, chatMessages: result, responseData: response, error: .success))
do {
let result = try decoder.decode([ChatMessage].self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, chatMessages: result, responseData: response, error: .success))
}
} catch {
options.queue.async {
continuation.resume(returning: (account: account, chatMessages: nil, responseData: response, error: .invalidData))
}

Copilot uses AI. Check for mistakes.
Comment on lines 416 to 418
let result = try? decoder.decode(CreateConversation.self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, conversation: result, responseData: response, error: .success))
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decoding CreateConversation is optional and decode failures still return .success with conversation: nil. Prefer explicit decoding with do/catch and return .invalidData (and/or log) when decoding fails to avoid silent failures.

Suggested change
let result = try? decoder.decode(CreateConversation.self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, conversation: result, responseData: response, error: .success))
do {
let result = try decoder.decode(CreateConversation.self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, conversation: result, responseData: response, error: .success))
}
} catch {
options.queue.async {
continuation.resume(returning: (account: account, conversation: nil, responseData: response, error: .invalidData))
}

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +15
public struct OCSTaskTypesOCS: Codable {
public let data: OCSTaskTypesData
}

Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new OCSTaskTypesResponse omits the standard OCS meta block (statuscode/message). Since OCS endpoints can return HTTP 200 with meta.statuscode indicating an error, callers can’t validate the OCS-level status with this model. Consider adding a meta field to the wrapper (and checking it in the request code) to preserve previous behavior.

Suggested change
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?
}

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +31
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
}
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OCSTaskListResponse / OCSTaskResponse wrappers don’t include the OCS meta block. If the server returns an OCS error with HTTP 200, the client can’t detect it via these models. Add meta (statuscode/message) and validate it in the calling code to avoid reporting false successes.

Copilot uses AI. Check for mistakes.
}

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) {
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RequestInterceptor.adapt must take an Alamofire.Session parameter. Changing the signature to AssistantSession breaks protocol conformance and will not compile. Use Session (or qualify as Alamofire.Session) and avoid name collisions with the new AssistantSession model type.

Suggested change
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) {

Copilot uses AI. Check for mistakes.
Comment on lines 469 to 471
let result = try? decoder.decode(ChatMessage.self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, chatMessage: result, responseData: response, error: .success))
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decoding failures are currently treated as .success with chatMessage: nil. This should be surfaced as an error (e.g. .invalidData) and/or logged so clients can react appropriately when the server payload changes or is malformed.

Suggested change
let result = try? decoder.decode(ChatMessage.self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, chatMessage: result, responseData: response, error: .success))
do {
let result = try decoder.decode(ChatMessage.self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, chatMessage: result, responseData: response, error: .success))
}
} catch {
options.queue.async {
continuation.resume(returning: (account: account, chatMessage: nil, responseData: response, error: .invalidData))
}

Copilot uses AI. Check for mistakes.
Comment on lines 520 to 522
let result = try? decoder.decode(SessionTask.self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, sessionTask: result, responseData: response, error: .success))
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SessionTask decoding uses try? and the function always returns .success, so decode failures become silent successes (sessionTask: nil). Handle decode errors explicitly (return .invalidData and/or log the decoding error).

Suggested change
let result = try? decoder.decode(SessionTask.self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, sessionTask: result, responseData: response, error: .success))
do {
let result = try decoder.decode(SessionTask.self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, sessionTask: result, responseData: response, error: .success))
}
} catch {
let decodingError = NKError(error: error, afResponse: response, responseData: data)
options.queue.async {
continuation.resume(returning: (account: account, sessionTask: nil, responseData: response, error: decodingError))
}

Copilot uses AI. Check for mistakes.
Comment on lines +62 to +65
} else {
options.queue.async {
continuation.resume(returning: (account: account, types: nil, responseData: response, error: .success))
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On JSON decoding failure, this returns error: .success with types: nil, which will silently hide server/schema issues. Return an error (e.g. .invalidData) and/or log the decoding error so callers can handle it.

Copilot uses AI. Check for mistakes.
Comment on lines +168 to +170
let result = try? decoder.decode(OCSTaskListResponse.self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, tasks: result.map { TaskList(tasks: $0.ocs.data.tasks) }, responseData: response, error: .success))
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written, decoding OCSTaskListResponse can fail and the function will still return error: .success (tasks will be nil). Treat decode failures as errors (e.g. .invalidData) and consider validating the OCS meta.statuscode in the body as well.

Suggested change
let result = try? decoder.decode(OCSTaskListResponse.self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, tasks: result.map { TaskList(tasks: $0.ocs.data.tasks) }, responseData: response, error: .success))
do {
let result = try decoder.decode(OCSTaskListResponse.self, from: data)
let statusCode = result.ocs.meta.statuscode
if statusCode < 200 || statusCode >= 300 {
options.queue.async {
continuation.resume(returning: (account: account, tasks: nil, responseData: response, error: .invalidData))
}
} else {
let taskList = TaskList(tasks: result.ocs.data.tasks)
options.queue.async {
continuation.resume(returning: (account: account, tasks: taskList, responseData: response, error: .success))
}
}
} catch {
options.queue.async {
continuation.resume(returning: (account: account, tasks: nil, responseData: response, error: .invalidData))
}

Copilot uses AI. Check for mistakes.
Comment on lines 360 to 362
let result = try? decoder.decode(ChatMessage.self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, chatMessage: result, responseData: response, error: .success))
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If decoding the created ChatMessage fails, this still returns .success with chatMessage: nil. Return .invalidData (and/or log) on decode failures so callers can distinguish server errors/contract changes from a real success.

Suggested change
let result = try? decoder.decode(ChatMessage.self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, chatMessage: result, responseData: response, error: .success))
do {
let result = try decoder.decode(ChatMessage.self, from: data)
options.queue.async {
continuation.resume(returning: (account: account, chatMessage: result, responseData: response, error: .success))
}
} catch {
options.queue.async {
continuation.resume(returning: (account: account, chatMessage: nil, responseData: response, error: .invalidData))
}

Copilot uses AI. Check for mistakes.
Signed-off-by: Milen Pivchev <milen.pivchev@gmail.com>
@mpivchev mpivchev disabled auto-merge February 13, 2026 10:51
@mpivchev mpivchev merged commit 99d564f into main Feb 13, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Chat with AI

1 participant