File size: 18,709 Bytes
f40da4b
6f82ab1
a38c9c4
 
dd2b0e2
 
 
575256f
dd2b0e2
 
 
5d3f8a0
 
79760bd
f35f6e7
747b7aa
 
 
79aeb13
9d426ed
 
747b7aa
5d3f8a0
dd2b0e2
 
e1c6f35
7042c3c
dd2b0e2
575256f
 
 
 
6f82ab1
e27d990
e1c6f35
d9d2cfb
ba8e285
d9d2cfb
6f82ab1
7042c3c
575256f
 
 
978e4a9
575256f
 
 
 
dd2b0e2
 
 
575256f
ba8e285
575256f
 
 
 
 
 
7042c3c
09e908a
f35f6e7
c224c4d
f35f6e7
e1c6f35
af36e79
e1c6f35
f8ef08b
 
e1c6f35
c7bd440
e1c6f35
 
c7bd440
e1c6f35
 
 
af36e79
c7bd440
ba8e285
e1c6f35
5d3f8a0
cd5d58c
5d3f8a0
 
e1c6f35
5d3f8a0
e1c6f35
 
af36e79
e1c6f35
f8ef08b
 
e1c6f35
c7bd440
 
e1c6f35
c7bd440
e1c6f35
 
 
c7bd440
e1c6f35
 
 
 
c7bd440
 
 
 
 
e1c6f35
 
 
af36e79
bd5398a
e1c6f35
 
 
 
 
 
 
 
 
 
 
 
51a331b
 
 
 
 
 
 
ebb7a43
5d3f8a0
 
e1c6f35
 
 
 
 
d9d2cfb
e1c6f35
f8ef08b
 
e1c6f35
c7bd440
e1c6f35
c7bd440
e1c6f35
 
09e908a
e1c6f35
d9d2cfb
e1c6f35
21cb336
d9d2cfb
09e908a
 
 
 
d9d2cfb
97a82d4
 
 
 
 
e4f065f
09e908a
af36e79
e1c6f35
f8ef08b
 
e1c6f35
c7bd440
 
f8ef08b
c7bd440
e1c6f35
 
 
af36e79
c7bd440
e1c6f35
 
 
 
 
c7bd440
e1c6f35
 
 
c7bd440
e1c6f35
 
 
 
 
dd2b0e2
 
7042c3c
dd2b0e2
 
 
 
 
 
 
978e4a9
dd2b0e2
 
978e4a9
 
dd2b0e2
 
 
 
 
 
 
 
7042c3c
dd2b0e2
5a9d4d6
 
 
 
dd2b0e2
 
018b62b
 
a38c9c4
af36e79
 
 
 
 
a38c9c4
9c755df
 
a38c9c4
dd2b0e2
a38c9c4
dd2b0e2
 
 
 
af36e79
 
 
 
 
4d5b8ce
c7bd440
a8e76c0
 
dd2b0e2
 
 
 
af36e79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c7bd440
9e038d4
af36e79
dd2b0e2
d9d2cfb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dd2b0e2
575256f
dd2b0e2
747b7aa
dd2b0e2
 
 
 
 
 
978e4a9
 
 
 
dd2b0e2
978e4a9
dd2b0e2
978e4a9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dd2b0e2
 
294b8d5
 
 
dd2b0e2
 
e1c6f35
 
d9d2cfb
e1c6f35
dd2b0e2
 
af36e79
e1c6f35
 
ba8e285
dd2b0e2
 
d9d2cfb
 
dd2b0e2
af36e79
 
 
dd2b0e2
e1c6f35
ba8e285
dd2b0e2
 
8d9811f
e1c6f35
 
dd2b0e2
 
d9d2cfb
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
#CHANGELOG: 2025-06-04
## Gradio Agents MCP Hackathon: retrofit to expose EA4ALL Agentic System Agents only
## Greetings message not working
## UI exposing too much tools, need to be refactored
from langchain.callbacks.tracers import LangChainTracer
from langchain.callbacks.tracers.langchain import wait_for_all_tracers
from langchain_core.messages import HumanMessage
from langchain_core.runnables import RunnableConfig

from ea4all.src.shared.configuration import BaseConfiguration, APM_MOCK_QNA, PMO_MOCK_QNA
from ea4all.src.ea4all_gra.configuration import AgentConfiguration as gra
from ea4all.src.ea4all_apm.graph import apm_graph
from ea4all.src.ea4all_vqa.graph import diagram_graph
from ea4all.src.ea4all_gra.graph import togaf_graph
from ea4all.src.ea4all_indexer.graph import indexer_graph
from ea4all.src.shared.utils import (
    get_relevant_questions, 
    get_vqa_examples, 
    _join_paths,
    EA4ALL_ARCHITECTURE,
    EA4ALL_PODCAST,
)

#from ea4all.src.pmo_crew.crew_runner import run_pmo_crew

from typing import AsyncGenerator
import gradio as gr
from gradio import ChatMessage
import os
import uuid
import time
from PIL import Image

from ea4all.utils.utils import (
    UIUtils,
    ea4all_agent_init, get_image,
    get_question_diagram_from_example,
    on_image_update
)

TITLE =  """
    # Title
        
    **Explore, Share, Together:** harness the value of `Enterprise Architecture in the era of Generative AI` with ready-to-use MCP Tools.\n

    ## Overview
    """

#Set LangSmith project
tracer = LangChainTracer(project_name=os.getenv('LANGCHAIN_PROJECT'))

config = RunnableConfig(
        run_name = os.getenv('LANGCHAIN_RUNNAME', "ea4all-gradio-agent-mcp-hackathon-run"), 
        tags = [os.getenv('EA4ALL_ENV', "MCP")],
        callbacks = [tracer],
        recursion_limit = 25,
        configurable = {"thread_id": uuid.uuid4()},
        #stream_mode = "messages"
)

async def call_indexer_apm(config: RunnableConfig):
    response = await indexer_graph.ainvoke(input={"docs":[]}, config=config)
    return response

#ea4all-qna-agent-conversational-with-memory
async def run_qna_agentic_system(question: str) -> AsyncGenerator[list, None]:
    """
    description: 
        Handles conversational Q&A for the Application Landscape using an agentic system.
    Args:
        question (str): The user's question or message.
        request (gr.Request): The Gradio request object for user identification.
    Returns:
        reponse: Response to user's architectural question.
    """

    format_response = ""
    chat_memory = []
    if not question:
        format_response = "Hi, how are you today? To start using the EA4ALL MCP Tool, provide the required Inputs!"
        chat_memory.append(ChatMessage(role="assistant", content=format_response))
    else:
        index = await call_indexer_apm(config) #call indexer to update the index
        response = await apm_graph.ainvoke({"question": question}, config=config)
        chat_memory.append(ChatMessage(role="assistant", content=response['generation']))

    yield chat_memory

#Trigger Solution Architecture Diagram QnA
async def run_vqa_agentic_system(question: str, diagram: str, request: gr.Request) -> AsyncGenerator[list, None]:
    """
    description: 
        Handles Visual Question Answering (VQA) for uploaded architecture diagrams.
    Args:
        question (str): User's question about the Architecture Diagram.
        diagram (str): Path to the diagram file.
    Returns:
        response: Response to user's question.
    """

    #capture user ip
    #ea4all_user = e4u.get_user_identification(request)

    """Handle file uploads and validate their types."""
    allowed_file_types = ('JPEG', 'PNG')

    message = {
        'text': question,
        'files': [diagram] if isinstance(diagram, str) else diagram
    }

    print("---CALLING VISUAL QUESTION ANSWERING AGENTIC SYSTEM---")
    print(f"Prompt: {message}")

    chat_memory = []
    if message['files'] in ([], None):
        chat_memory.append(ChatMessage(role="assistant", content="Please upload an Architecture PNG, JPEG diagram to start!"))
        yield chat_memory
    else:
        diagram = message['files'][-1] ##chat_memory[-1]['content'][-1]        
        msg = message['text'] ##chat_memory[-2]['content']
        print(f"---DIAGRAM: {diagram}---")
        try:
            if msg == "":
                msg = "Please describe this diagram."

            with Image.open(diagram) as diagram_:
                if diagram_.format not in allowed_file_types:
                    #chat_memory.append(ChatMessage(role="assistant", content="Invalid file type. Allowed file types are JPEG and PNG."))
                    print(f"---DIAGRAM: {diagram.format} is not a valid file type. Allowed file types are JPEG and PNG.---")
                #else:
                #'vqa_image = e4u.get_raw_image(diagram) #MOVED into Graph

                vqa_image = diagram
                response = await diagram_graph.ainvoke({"question":msg, "image": vqa_image}, config)
                chat_memory.append(ChatMessage(role="assistant", content=response['messages'][-1].content if len(response['messages']) else response['safety_status']['description']))
                
                yield chat_memory

        except Exception as e:
            yield (e.args[-1])

#Run Togaf Agentic System
async def run_reference_architecture_agentic_system(business_query: str) -> AsyncGenerator[list, str]:
    """
    description: 
        Generates a reference architecture blueprint based on a business requirement using the TOGAF agentic system.
    Args:
        business_query (str): Description of a business problem / requirement.
    Returns:
        response: High-level architecture blueprint and target diagram.
    """

    if len(business_query) < 20:
        agent_response = "Please provide a valid Business Requirement content to start!"
        yield([agent_response, None])
    else: 
        inputs = {"business_query": [{"role": "user", "content": business_query}]} #user response
        index = await call_indexer_apm(config) #call indexer to update the index
        response = await togaf_graph.ainvoke(
            input=inputs, 
            config=config
        ) #astream not loading the graph
        vision_target = response['vision_target']
        if isinstance(response['architecture_runway'], str):
            architecture_runway = response['architecture_runway']
        else:
            architecture_runway = None

        yield [vision_target, architecture_runway]

async def run_pmo_agentic_system(question:str) -> AsyncGenerator[list, None]:
    """
    description: 
        Answers questions about Project Portfolio Management and Architect Demand Management.
    Args:
        question (str): The user's question about project portfolio or resource management.
        chat_memory: The conversation history.
    Returns:
        response: Architect Demand Allocation Report
    """

    format_response = ""
    chat_memory = []
    if not question:
        format_response = "Hi, how are you today? To start our conversation, please chat your message!"
        chat_memory.append(ChatMessage(role="assistant", content=format_response))
        yield chat_memory

    if not chat_memory:
        chat_memory.append(ChatMessage(role="user", content=question))
        yield chat_memory

    inputs = {
        "question": question,
        "verbose": True,  # optional flags
    }
    
    #yield run_pmo_crew(inputs)

#Blocks w/ ChatInterface, BYOD, About
with gr.Blocks(title="Your ArchitectGPT",fill_height=True, fill_width=True) as ea4all_mcp:

    agentic_pmo_desc="""
        Hi, 
        Provide project resource estimation for architecture work based on business requirements, skillset, 
        architects allocation, and any other relevant information to enable successful project solution delivery."""
    
    agentic_qna_desc="""
        Hi,
        Improve Architect's ability to share knowledge, and provide valuable insights from IT landscape using natural language answering questions related to Enterprise Architecture, Technology, plus the following IT Landscape sample dataset: """

    agentic_vqa_desc="""
    Hi, 
    Gain rapid knowledge and insights translating image to meaningful description. 
    """

    agentic_togaf_desc="""
        Hi,
        in a click of button create a reference architecture that serves as a blueprint for designing and implementing IT solutions.
        Standardise, increase efficiency and productivity to architecture solution development.
        Generate context-specific reference and minimal viable architectures to support business and IT strategy and digital transformation.
        Streamline the architecture operating model, taking the best of agentic workflows and architects working together.
    """
    
    #Wrapper for functions not to be exposed by the MCP Server
    wrapper = gr.Button(visible=False) #wrapper.click(UIUtils.ea4all_about, show_api=False,)
    wrapper1 = gr.Button(visible=False) #wrapper1.click(init_dbr, show_api=False,)

    #EA4ALL-Agentic system menu
    with gr.Tabs(selected="how_to") as tabs:
        with gr.Tab(label="Architect Demand Management", visible=False):
            with gr.Tab(label="Architect Project Planning", id="pmo_qna_1"):
                ea4all_pmo_description = gr.Markdown(value=agentic_pmo_desc)
                pmo_chatbot = gr.Chatbot(
                    label="EA4ALL your AI Demand Management Architect Companion", type="messages",
                    max_height=160,
                    layout="bubble",
                )
                pmo_prompt = gr.Textbox(lines=1, show_label=False, max_lines=1, submit_btn=True, stop_btn=True,autofocus=True, placeholder="Type your message here or select an example...")
                with gr.Accordion("Open for question examples", open=False):
                    pmo_examples = gr.Dropdown(get_relevant_questions(PMO_MOCK_QNA), value=None,label="Questions", interactive=True)
                gr.ClearButton([pmo_chatbot,pmo_prompt], value="Clear", size="sm", visible=False)
            with gr.Tab(label="Project Portfolio Sample Dataset", id="id_pmo_ds"):
                pmo_df = gr.Dataframe()
        with gr.Tab(label="Application Landscape QnA"):
            with gr.Tabs() as tabs_apm_qna:
                with gr.Tab(label="Connect, Explore, Together", id="app_qna_1"):
                    ea4all_agent_metadata = gr.Markdown(value=agentic_qna_desc)
                    ea4all_chatbot = gr.Chatbot(
                        label="EA4ALL your AI Landscape Architect Companion", type="messages",
                        max_height=160,
                        layout="bubble",
                    )
                    qna_prompt = gr.Textbox(lines=1, show_label=False, max_lines=1, submit_btn=True, autofocus=True, placeholder="Type your message here or select an example...")
                    with gr.Accordion("Open for question examples", open=False):
                        qna_examples = gr.Dropdown(get_relevant_questions(APM_MOCK_QNA),label="Questions", interactive=True)
                    gr.ClearButton([ea4all_chatbot,qna_prompt, qna_examples], value="Clear", size="sm", visible=True)
                with gr.Tab(label="Sample Dataset", id="id_apm_ds"):
                    apm_df = gr.Dataframe()
        with gr.Tab(label="Diagram Question and Answering"):
            gr.Markdown(value=agentic_vqa_desc)
            ea4all_vqa = gr.Chatbot(
                label="EA4ALL your AI Multimodal Architect Companion", type="messages",
                max_height=160,
                layout="bubble",
            )
            vqa_prompt = gr.Textbox(lines=1, show_label=False, max_lines=1, submit_btn=True, stop_btn=True,autofocus=True, placeholder="Type your message here and upload your diagram...")
            vqa_image = gr.Image(
                label="Architecture Diagram",
                type="filepath",
                format="jpeg, png",
                interactive=True,
                show_download_button=False,
                show_share_button=False,
                visible=True,
            )
            #vqa_prompt = gr.MultimodalTextbox(interactive=True, show_label=False, submit_btn=True, stop_btn=True, autofocus=True, placeholder="Upload your diagram and type your message or select an example...")
            with gr.Accordion("Open for question examples", open=False):
                vqa_examples = gr.Dropdown(get_vqa_examples(), value=0,label="Diagram and Questions", interactive=True)
            gr.ClearButton([ea4all_vqa,vqa_prompt,vqa_image, vqa_examples], value="Clear", size="sm", visible=True)
        with gr.Tab(label="Reference Architecture", id="id_refarch"):
            dbr_text=gr.TextArea(label="Business Problem Sample", value="Provide a Business Problem / Requirement Specification or select an example provided.", lines=14, interactive=True)
            togaf_vision=gr.Markdown(value='### Reference Architecture: Vision and Target')
            architecture_runway=gr.Image(label="Target Architecture Runway",interactive=False,visible=False)            
            with gr.Row():
                dbr_file=gr.File(
                    value=_join_paths(BaseConfiguration.ea4all_store, gra.dbr_mock),
                    label="Business Requirement", 
                    height=35, 
                    show_label=False, 
                    file_count="single", 
                    file_types=['text'], 
                    interactive=True,
                    type='binary',
                    visible=False
                )
                dbr_run=gr.Button(scale=None,value="Run Reference Architecture")
                dbr_cls=gr.ClearButton([togaf_vision, architecture_runway])
        with gr.Tab(label="Overview", id="how_to"):
            gr.Markdown(value=TITLE)
            gr.Image(
                get_image(EA4ALL_ARCHITECTURE),
                show_download_button=False,
                container=False,
                show_share_button=False,
                )
            gr.Markdown(
                """
                - `Empower individuals with Knowledge`: understand and talk about Business and Technology strategy, IT landscape, Architectue Artefacts in a single click of button.
                - `Increase efficiency and productivity`: generate a documented architecture with diagram, model and descriptions. Accelerate Business Requirement identification and translation to Target Reference Architecture. Automated steps and reduced times for task execution.
                - `Improve agility`: plan, execute, review and iterate over EA inputs and outputs. Increase the ability to adapt, transform and execute at pace and scale in response to changes in strategy, threats and opportunities.
                - `Increase collaboration`: democratise architecture work and knowledge with anyone using natural language.

                ### Knowledge Context

                Synthetic datasets are used to exemplify the Agentic System capabilities.

                ### IT Landscape Question and Answering

                    - Application name
                        - Business fit: appropriate, inadequate, perfect
                        - Technical fit: adequate, insufficient, perfect
                        - Business_criticality: operational, medium, high, critical
                        - Roadmap: maintain, invest, divers
                        - Architect responsible
                        - Hosting: user device, on-premise, IaaS, SaaS
                        - Business capability
                        - Business domain
                        - Description  

                    
                ### Architecture Diagram Visual Question and Answering

                    - Architecture Visual Artefacts
                        - jpeg, png

                        **Disclaimer**
                                - Your data & image are not accessible or shared with anyone else nor used for training purpose.
                                - EA4ALL-VQA Agent should be used ONLY FOR Architecture Diagram images.
                                - This feature should NOT BE USED to process inappropriate content.

                ### Reference Architecture Generation

                    - Clock in/out Use-case
                """
            )

    #Avoid exposing API /Dependency?
    #dbr_text.change(wrapper1.click(init_dbr,show_api=False)) NOT working

    #Togaf upload file
    #dbr_file.clear(unload_dbr,outputs=dbr_text)
    #dbr_file.change(on_dbrtext,inputs=dbr_file,outputs=dbr_text)
    dbr_file.change(UIUtils.load_dbr,inputs=dbr_file, outputs=dbr_text, show_api=False)
    #dbr_cls.click(off_dbrtext,outputs=[dbr_text, tabs_togaf, tab_diagram])

    #Refactored ea4all_chatbot / vqa_chatbot (ChatInterface -> Chatbot)
    qna_prompt.submit(run_qna_agentic_system,[qna_prompt],ea4all_chatbot, api_name="landscape_answering_agent")
    #qna_prompt.submit(lambda: "", None, [qna_prompt])
    #ea4all_chatbot.like(fn=get_user_feedback)
    qna_examples.input(lambda value: value, qna_examples, qna_prompt, show_api=False)

    #Execute Reference Architecture
    dbr_run.click(run_reference_architecture_agentic_system,show_progress='full',inputs=[dbr_text],outputs=[togaf_vision, architecture_runway], api_name="togaf_blueprint_generation")
    architecture_runway.change(on_image_update, inputs=architecture_runway, outputs=architecture_runway, show_api=False)    

    #chat_msg = vqa_prompt.submit(UIUtils.add_message, [vqa_prompt, vqa_image], [vqa_prompt, ea4all_vqa], show_api=False)
    #bot_msg = chat_msg.then(run_vqa_agentic_system, [vqa_prompt, vqa_image], ea4all_vqa, api_name="diagram_answering_agent")
    vqa_prompt.submit(run_vqa_agentic_system,[vqa_prompt, vqa_image], ea4all_vqa, api_name="diagram_answering_agent")
    
    #ea4all_vqa.like(fn=get_user_feedback)
    vqa_examples.input(get_question_diagram_from_example, vqa_examples, outputs=[vqa_prompt, vqa_image], show_api=False)

    #Invoke CrewAI PMO Agentic System
    pmo_prompt.submit(run_pmo_agentic_system,[pmo_prompt],pmo_chatbot, api_name="architect_demand_agent", show_api=False)
    pmo_prompt.submit(lambda: "", None, [pmo_prompt], show_api=False)
    #pmo_examples.input(lambda value: value, pmo_examples, pmo_prompt)

    #Set initial state of apm and llm
    ea4all_mcp.load(ea4all_agent_init, outputs=[
        ea4all_agent_metadata,
        ea4all_chatbot, 
        ea4all_vqa, 
        pmo_chatbot, 
        apm_df, 
        pmo_df, 
        dbr_text
        ], 
        show_api=False)