File size: 3,959 Bytes
b4c62e2
c910e41
 
 
4869492
d780ea2
cd40088
d780ea2
e933edd
c910e41
4869492
c910e41
4869492
c910e41
 
 
 
 
 
 
4869492
c910e41
 
 
47e1153
6d2002f
4869492
2d596fc
c910e41
47e1153
 
 
 
 
 
 
 
4869492
6d2002f
1fb4ee6
4869492
 
c910e41
4869492
c910e41
d780ea2
4869492
c910e41
 
4869492
c910e41
4869492
c910e41
4869492
d780ea2
1fb4ee6
4869492
 
c910e41
4869492
c910e41
6d2002f
4869492
c910e41
 
4869492
 
c910e41
 
 
4869492
c910e41
d780ea2
6d2002f
c910e41
4869492
 
 
 
 
c910e41
4869492
 
c910e41
4869492
 
 
 
 
 
 
c910e41
 
 
4869492
c910e41
4869492
 
 
c910e41
4869492
c910e41
4869492
6d2002f
 
 
 
 
 
 
4869492
 
1ef5210
 
c910e41
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
import os
import uuid
from typing import List, Dict, Optional
from pydantic import BaseModel
from langgraph.graph import StateGraph
from .document_parser import parse_documents
from .requirements_extractor import extract_requirements
from .ui_generator import generate_ui_html


# ==========================================================
# 🧱 Define the State Schema (Pydantic model)
# ==========================================================
class PipelineState(BaseModel):
    files: Optional[List[str]] = None
    text: Optional[str] = None
    requirements: Optional[Dict] = None
    html: Optional[str] = None
    public_url: Optional[str] = None


# ==========================================================
# πŸ“„ Node 1 β€” Document Parser
# ==========================================================
async def node_parse_docs(state: PipelineState):
    print("πŸ“„ [Document Parser] Extracting text...")

    result = await parse_documents(state.files)

    # handle both string or dict outputs
    if isinstance(result, dict):
        text = result.get("text", "").strip()
    else:
        text = str(result).strip()

    if not text:
        raise ValueError("No text extracted from documents.")

    return {"text": text}


# ==========================================================
# 🧠 Node 2 β€” Requirements Extractor
# ==========================================================
async def node_requirements(state: PipelineState, config=None) -> Dict:
    print("🧩 [Requirements Agent] Extracting structured info...")

    if not state.text:
        raise ValueError("Missing 'text' field in state for requirement extraction.")

    requirements = await extract_requirements(state.text)
    if not requirements:
        raise ValueError("Requirements extraction returned empty.")

    return {"requirements": requirements}


# ==========================================================
# 🎨 Node 3 β€” UI Generator
# ==========================================================
async def node_ui_generator(state: PipelineState, config=None) -> Dict:
    print("🎨 [UI Generator] Creating PowerApps-style interface...")

    if not state.requirements:
        raise ValueError("Missing 'requirements' in state for UI generation.")

    # Load reference design
    ref_path = os.path.join("templates", "demo_qms_design.html")
    if not os.path.exists(ref_path):
        raise FileNotFoundError(f"Reference HTML not found at {ref_path}")

    with open(ref_path, "r", encoding="utf-8") as f:
        reference_html = f.read()

    ui = await generate_ui_html(state.requirements, reference_html)
    html = ui.get("html", "")

    if not html or "<html" not in html:
        raise ValueError("Generated HTML invalid or empty.")

    # Save to static/outputs/
    os.makedirs("static/outputs", exist_ok=True)
    session_id = uuid.uuid4().hex
    html_path = os.path.join("static/outputs", f"{session_id}.html")
    with open(html_path, "w", encoding="utf-8") as f:
        f.write(html)

    base_url = os.environ.get("BASE_PUBLIC_URL", "").rstrip("/")
    if not base_url:
        base_url = "http://localhost:7860"

    public_url = f"{base_url}/static/outputs/{session_id}.html"

    print(f"βœ… [UI Generator] Generated UI available at: {public_url}")

    return {"html": html, "public_url": public_url}


# ==========================================================
# πŸ”— Build the LangGraph
# ==========================================================
graph = StateGraph(PipelineState)

graph.add_node("parse_docs", node_parse_docs)
graph.add_node("requirements", node_requirements)
graph.add_node("ui_generator", node_ui_generator)

graph.set_entry_point("parse_docs")
graph.add_edge("parse_docs", "requirements")
graph.add_edge("requirements", "ui_generator")
graph.set_finish_point("ui_generator")

graph = graph.compile()

print("βœ… LangGraph pipeline loaded successfully: Document β†’ Requirements β†’ UI Generator")