// Copyright © 2024 Ryan Booker. All rights reserved. import ArgumentParser import Domain import Foundation import Toolbox public struct Summarize: AsyncParsableCommand { public static let configuration = CommandConfiguration( commandName: "summarize", abstract: "Summarize something.", usage: "kg summarize \"Some text or a url to summarize.\"" ) @Argument(help: "Some text or a url to summarize") public var query: Either<URL, String> @Option(help: Engine.help) public var engine: Engine? @Option(help: SummaryType.help) public var summaryType: SummaryType? var client = Client.liveValue enum CodingKeys: CodingKey { case query, engine, summaryType } public init() { // Intentionally left empty } public mutating func run() async throws { do { let response = try await Client.liveValue.run(query: query, engine: engine, summaryType: summaryType) print(response) } catch { throw ValidationError(error.localizedDescription) } } } // MARK: - Processing extension Summarize { struct Client { var run: @Sendable ( _ query: Either<URL, String>, _ engine: Engine?, _ summaryType: SummaryType? ) async throws -> Response<Summarization> @inlinable func run( query: Either<URL, String>, engine: Engine?, summaryType: SummaryType? ) async throws -> Response<Summarization> { try await run(query, engine, summaryType) } } } extension Summarize.Client { static let liveValue = Self( run: { query, engine, summaryType in var body: [String: Any] = query.reduce(first: { ["url": $0.absoluteString] }, second: { ["text": $0] }) if let engine { body["engine"] = engine.rawValue } if let summaryType { body["summary_type"] = summaryType.rawValue } var request = try Endpoint.summarize.authorizedRequest request.httpBody = try JSONSerialization.data(withJSONObject: body) let (data, response) = try await URLSession.shared.data(for: request) do { return try JSONDecoder().decode(Response.self, from: data) } catch { #if DEBUG print(response) print(error) print("-----") #endif throw error } } ) } // MARK: - Response extension Summarize { public enum Engine: String, CaseIterable, CustomStringConvertible, ExpressibleByArgument { case cecil case agnes case daphne case muriel public var description: String { switch self { case .cecil: "Friendly, descriptive, fast summary (default)" case .agnes: "Formal, technical, analytical summary" case .daphne: "Informal, creative, friendly summary" case .muriel: "Best-in-class summary using our enterprise-grade model" } } public static var help: ArgumentHelp? { .init( allCases.map { "- \($0.rawValue): \($0.description)" }.joined(separator: "\n") + "\n" ) } public var defaultValueDescription: String { Self.cecil.rawValue } } public enum SummaryType: String, CaseIterable, CustomStringConvertible, ExpressibleByArgument { case summary case takeaway public var description: String { switch self { case .summary: "Paragraph(s) of summary prose (default)" case .takeaway: "Bulleted list of key points" } } public static var help: ArgumentHelp? { .init( allCases.map { "- \($0.rawValue): \($0.description)" }.joined(separator: "\n") + "\n" ) } public var defaultValueDescription: String { Self.summary.rawValue } } public struct Summarization: CustomStringConvertible, Decodable { public var output: String public var tokens: Int public var description: String { """ Summary: \(output) """ } } }