From cc8c81247ff2be7f35c5fdc557c60e1f4b617d8f Mon Sep 17 00:00:00 2001 From: bsorrentino Date: Sat, 23 Mar 2024 21:15:16 +0100 Subject: [PATCH] feat: complete demo using UI test process --- .../Sources/AIAgent/AgentExecutorDemo.swift | 104 ++++++++++++++++++ PlantUML/OpenAIObservableService.swift | 30 +++-- PlantUML/PlantUMLApp.swift | 5 + PlantUML/SwiftUI+PencilKit.swift | 2 +- PlantUMLAppUITests/DrawingUITests.swift | 15 ++- 5 files changed, 141 insertions(+), 15 deletions(-) create mode 100644 AIAgent/Sources/AIAgent/AgentExecutorDemo.swift diff --git a/AIAgent/Sources/AIAgent/AgentExecutorDemo.swift b/AIAgent/Sources/AIAgent/AgentExecutorDemo.swift new file mode 100644 index 0000000..7fe1ef3 --- /dev/null +++ b/AIAgent/Sources/AIAgent/AgentExecutorDemo.swift @@ -0,0 +1,104 @@ +// +// AgentExecutorDemo.swift +// +// +// Created by bsorrentino on 23/03/24. +// + +import Foundation +import OSLog +import OpenAI +import LangGraph + + +struct AgentExecutorDemoState : AgentState { + + var data: [String : Any] + + init() { + data = [:] + } + + init(_ initState: [String : Any]) { + data = initState + } + + var diagramCode:String? { + data["diagram_code"] as? String + } + +} + +public func runTranslateDrawingToPlantUMLDemo( openAI: OpenAI, + imageUrl: String, + delegate:T ) async throws -> String? { + + let workflow = GraphState { AgentExecutorState() } + + try workflow.addNode("agent_describer", action: { state in + await delegate.progress("starting analyze\ndiagram 👀") + + try await Task.sleep( nanoseconds: 5_000_000_000 ) + + await delegate.progress( "diagram processed ✅") + + return [ : ] + + }) + + try workflow.addNode("agent_generic_plantuml", action: { state in + await delegate.progress("translating diagram to\nGeneric Diagram") + + try await Task.sleep( nanoseconds: 5_000_000_000 ) + + let content = + """ + actor "User Initiating The Diagram Translation Process" as userInitiatingTheDiagramTranslationProcess<> + rectangle "Provide Diagram Image" as provideDiagramImage<> + rectangle "Process Image" as processImage<> + rectangle "Description" as description<> + rectangle "Check Type" as checkType<> + rectangle "Sequence" as sequence<> + rectangle "Generic" as generic<> + rectangle "Translate To Sequence" as translateToSequence<> + rectangle "Translate To Generic" as translateToGeneric<> + legend + - 1. The USER initiates the process. + - 2. The USER provides the diagram image. + - 3. The PROVIDED DIAGRAM IMAGE is processed. + - 4. The process results in a DESCRIPTION of the image. + - 5. The DESCRIPTION leads to a CHECK TYPE decision. + - 6. Based on the decision, if the diagram is a sequence type, it proceeds to TRANSLATE TO SEQUENCE. + - 7. If the diagram is a generic type, it proceeds to TRANSLATE TO GENERIC. + end legend + userInitiatingTheDiagramTranslationProcess --> provideDiagramImage : User provides a diagram image + provideDiagramImage --> processImage : Provided image is processed + processImage --> description : Processed image is described + description --> checkType : Description leads to checking the type + checkType --> sequence : Decision made for sequence type + checkType --> generic : Decision made for generic type + sequence --> translateToSequence : Sequence type is translated + generic --> translateToGeneric : Generic type is translated + + """ + return [ "diagram_code": content ] + + }) + + try workflow.addEdge(sourceId: "agent_generic_plantuml", targetId: END) + + try workflow.addEdge( sourceId: "agent_describer", + targetId: "agent_generic_plantuml" ) + + try workflow.setEntryPoint( "agent_describer") + + let app = try workflow.compile() + + let inputs:[String : Any] = [:] + + let response = try await app.invoke( inputs: inputs) + + return response.diagramCode +} + + diff --git a/PlantUML/OpenAIObservableService.swift b/PlantUML/OpenAIObservableService.swift index 576e8c3..c305fa3 100644 --- a/PlantUML/OpenAIObservableService.swift +++ b/PlantUML/OpenAIObservableService.swift @@ -151,16 +151,28 @@ extension OpenAIObservableService { do { - if let content = try await runTranslateDrawingToPlantUML( openAI: openAI, imageUrl: imageUrl, delegate:delegate) { - - status = .Ready - - return content - .split( whereSeparator: \.isNewline ) - .filter { $0 != "@startuml" && $0 != "@enduml" } - .joined(separator: "\n" ) + if DEMO_MODE { + if let content = try await runTranslateDrawingToPlantUMLDemo( openAI: openAI, imageUrl: imageUrl, delegate:delegate) { + + status = .Ready + + return content + .split( whereSeparator: \.isNewline ) + .filter { $0 != "@startuml" && $0 != "@enduml" } + .joined(separator: "\n" ) + } + } + else { + if let content = try await runTranslateDrawingToPlantUML( openAI: openAI, imageUrl: imageUrl, delegate:delegate) { + + status = .Ready + + return content + .split( whereSeparator: \.isNewline ) + .filter { $0 != "@startuml" && $0 != "@enduml" } + .joined(separator: "\n" ) + } } - delegate.progress("ERROR: invalid result!") status = .Error( "invalid result!" ) } diff --git a/PlantUML/PlantUMLApp.swift b/PlantUML/PlantUMLApp.swift index 7f69fc7..4e441f4 100644 --- a/PlantUML/PlantUMLApp.swift +++ b/PlantUML/PlantUMLApp.swift @@ -27,6 +27,11 @@ struct PlantUMLApp: App { // Config Settings print( "DEMO_MODE: \(DEMO_MODE)" ) print( "SAVE_DRAWING_IMAGE: \(SAVE_DRAWING_IMAGE)" ) + let documentDir = try? FileManager.default.url(for: .documentDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: false) + print( "DOCUMENT DIRECTORY\n\(String(describing: documentDir))") } var body: some Scene { diff --git a/PlantUML/SwiftUI+PencilKit.swift b/PlantUML/SwiftUI+PencilKit.swift index 5ff97d2..93b99a1 100644 --- a/PlantUML/SwiftUI+PencilKit.swift +++ b/PlantUML/SwiftUI+PencilKit.swift @@ -43,7 +43,7 @@ struct DrawingView: UIViewRepresentable { let drawing = try PKDrawing(data: data) if DEMO_MODE { - slowDrawingForDemo(drawing, timeInterval: 0.4) + slowDrawingForDemo(drawing, timeInterval: 0.2) } else { canvas.drawing = drawing diff --git a/PlantUMLAppUITests/DrawingUITests.swift b/PlantUMLAppUITests/DrawingUITests.swift index 5595d0d..671e33a 100644 --- a/PlantUMLAppUITests/DrawingUITests.swift +++ b/PlantUMLAppUITests/DrawingUITests.swift @@ -36,7 +36,7 @@ final class DrawingUITests: XCTestCase { } - func testDrawDiagram() throws { + func testDrawDiagramForDemo() throws { // UI tests must launch the application that they test. let app = XCUIApplication() @@ -59,14 +59,19 @@ final class DrawingUITests: XCTestCase { app.buttons["drawing_tools"].tap() - wait(reason: "Wait for drawing graph", timeout: 20 ) + wait(reason: "Wait for drawing graph", timeout: 38 ) app.buttons["drawing_process"].tap() - wait(reason: "Wait for process drawing", timeout: 30 ) - XCTAssertTrue( app.buttons["diagram_preview"].waitForExistence(timeout: 60) ) + XCTAssertTrue( app.buttons["cancel_processing"].waitForExistence(timeout: 10) ) - app.buttons["diagram_preview"].tap() + wait(reason: "Wait until show preview", timeout: 10 ) + + XCTAssertTrue( app.buttons["diagram_preview"].waitForExistence(timeout: 10) ) + + app.buttons["diagram_preview"].forceTap() + + wait(reason: "preview time", timeout: 20 ) } func testLaunchPerformance() throws {