Merge branch 'validation_fix' of personal:logspace-ai/langflow into validation_fix

This commit is contained in:
anovazzi1 2023-06-13 18:45:57 -03:00
commit 1a6406e394
7 changed files with 61 additions and 40 deletions

View file

@ -6,7 +6,8 @@ from fastapi import (
WebSocketException, WebSocketException,
status, status,
) )
from fastapi.responses import StreamingResponse, JSONResponse from fastapi.responses import StreamingResponse
from langflow.api.v1.schemas import BuiltResponse, InitResponse
from langflow.chat.manager import ChatManager from langflow.chat.manager import ChatManager
from langflow.graph.graph.base import Graph from langflow.graph.graph.base import Graph
@ -31,7 +32,7 @@ async def chat(client_id: str, websocket: WebSocket):
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc)) await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc))
@router.post("/build/init") @router.post("/build/init", response_model=InitResponse)
async def init_build(graph_data: dict): async def init_build(graph_data: dict):
"""Initialize the build by storing graph data and returning a unique session ID.""" """Initialize the build by storing graph data and returning a unique session ID."""
@ -39,19 +40,21 @@ async def init_build(graph_data: dict):
flow_data_store[flow_id] = graph_data flow_data_store[flow_id] = graph_data
return JSONResponse(content={"flowId": flow_id}) return InitResponse(flowId=flow_id)
@router.get("/build/{flow_id}/status") @router.get("/build/{flow_id}/status", response_model=BuiltResponse)
async def build_status(flow_id: str): async def build_status(flow_id: str):
"""Check the flow_id is in the flow_data_store.""" """Check the flow_id is in the flow_data_store."""
try: try:
if flow_id in flow_data_store and not isinstance( built = flow_id in flow_data_store and not isinstance(
flow_data_store[flow_id], dict flow_data_store[flow_id], dict
): )
return JSONResponse(content={"built": True})
else: return BuiltResponse(
return JSONResponse(content={"built": False}) built=built,
)
except Exception as exc: except Exception as exc:
logger.error(exc) logger.error(exc)
return HTTPException(status_code=500, detail=str(exc)) return HTTPException(status_code=500, detail=str(exc))

View file

@ -68,3 +68,11 @@ class FileResponse(ChatMessage):
if v not in ["image", "csv"]: if v not in ["image", "csv"]:
raise ValueError("data_type must be image or csv") raise ValueError("data_type must be image or csv")
return v return v
class InitResponse(BaseModel):
flowId: str
class BuiltResponse(BaseModel):
built: bool

View file

@ -50,7 +50,7 @@ export default function App() {
// Initialize state variable for the version // Initialize state variable for the version
const [version, setVersion] = useState(""); const [version, setVersion] = useState("");
useEffect(() => { useEffect(() => {
fetch("/version") fetch("api/v1/version")
.then((res) => res.json()) .then((res) => res.json())
.then((data) => { .then((data) => {
setVersion(data.version); setVersion(data.version);

View file

@ -1,16 +1,13 @@
import { useState, useContext, useRef, useEffect } from "react"; import { useState, useContext } from "react";
import { Transition } from "@headlessui/react"; import { Transition } from "@headlessui/react";
import { Bars3CenterLeftIcon } from "@heroicons/react/24/outline";
import { Zap } from "lucide-react"; import { Zap } from "lucide-react";
import { nodeColors, validateNodes } from "../../../utils"; import { validateNodes } from "../../../utils";
import { PopUpContext } from "../../../contexts/popUpContext";
import ChatModal from "../../../modals/chatModal";
import { FlowType } from "../../../types/flow"; import { FlowType } from "../../../types/flow";
import Loading from "../../../components/ui/loading"; import Loading from "../../../components/ui/loading";
import { useSSE } from "../../../contexts/SSEContext"; import { useSSE } from "../../../contexts/SSEContext";
import axios from "axios";
import { typesContext } from "../../../contexts/typesContext"; import { typesContext } from "../../../contexts/typesContext";
import { alertContext } from "../../../contexts/alertContext"; import { alertContext } from "../../../contexts/alertContext";
import { postBuildInit } from "../../../controllers/API";
export default function BuildTrigger({ export default function BuildTrigger({
open, open,
@ -26,21 +23,24 @@ export default function BuildTrigger({
const [isBuilding, setIsBuilding] = useState(false); const [isBuilding, setIsBuilding] = useState(false);
const { updateSSEData } = useSSE(); const { updateSSEData } = useSSE();
const {reactFlowInstance} = useContext(typesContext); const { reactFlowInstance } = useContext(typesContext);
const {setErrorData} = useContext(alertContext) const { setErrorData } = useContext(alertContext);
async function handleBuild(flow: FlowType) { async function handleBuild(flow: FlowType) {
const errors = validateNodes(reactFlowInstance) const errors = validateNodes(reactFlowInstance);
if(errors.length > 0) { if (errors.length > 0) {
setErrorData({title: "Oops! Looks like you missed something", list: errors}) setErrorData({
return title: "Oops! Looks like you missed something",
list: errors,
});
return;
} }
const minimumLoadingTime = 200; // in milliseconds const minimumLoadingTime = 200; // in milliseconds
const startTime = Date.now(); const startTime = Date.now();
setIsBuilding(true); setIsBuilding(true);
try { try {
const allNodesValid = await streamNodeData(`/build/init`, flow); const allNodesValid = await streamNodeData(flow);
await enforceMinimumLoadingTime(startTime, minimumLoadingTime); await enforceMinimumLoadingTime(startTime, minimumLoadingTime);
setIsBuilt(allNodesValid); setIsBuilt(allNodesValid);
} catch (error) { } catch (error) {
@ -50,15 +50,15 @@ export default function BuildTrigger({
} }
} }
async function streamNodeData(apiUrl: string, flow: FlowType) { async function streamNodeData(flow: FlowType) {
// Step 1: Make a POST request to send the flow data and receive a unique session ID // Step 1: Make a POST request to send the flow data and receive a unique session ID
const response = await axios.post(apiUrl, flow); const response = await postBuildInit(flow);
const { flowId } = response.data; const { flowId } = response.data;
// Step 2: Use the session ID to establish an SSE connection using EventSource // Step 2: Use the session ID to establish an SSE connection using EventSource
let validationResults = []; let validationResults = [];
let finished = false; let finished = false;
apiUrl = `/build/stream/${flowId}`; const apiUrl = `/build/stream/${flowId}`;
const eventSource = new EventSource(apiUrl); const eventSource = new EventSource(apiUrl);
try{ try{
eventSource.onmessage = (event) => { eventSource.onmessage = (event) => {
@ -134,7 +134,9 @@ export default function BuildTrigger({
<div className={`fixed right-4` + (isBuilt ? " bottom-20" : " bottom-4")}> <div className={`fixed right-4` + (isBuilt ? " bottom-20" : " bottom-4")}>
<div <div
className="border flex justify-center align-center py-1 px-3 w-12 h-12 rounded-full bg-gradient-to-r from-blue-700 via-blue-600 to-blue-500 dark:border-gray-600 cursor-pointer" className="border flex justify-center align-center py-1 px-3 w-12 h-12 rounded-full bg-gradient-to-r from-blue-700 via-blue-600 to-blue-500 dark:border-gray-600 cursor-pointer"
onClick={() => {if(!isBuilding) handleBuild(flow)}} onClick={() => {
if (!isBuilding) handleBuild(flow);
}}
> >
<button> <button>
<div className="flex gap-3 items-center"> <div className="flex gap-3 items-center">

View file

@ -2,29 +2,30 @@ import {
BuildStatusTypeAPI, BuildStatusTypeAPI,
PromptTypeAPI, PromptTypeAPI,
errorsTypeAPI, errorsTypeAPI,
InitTypeAPI,
} from "./../../types/api/index"; } from "./../../types/api/index";
import { APIObjectType, sendAllProps } from "../../types/api/index"; import { APIObjectType, sendAllProps } from "../../types/api/index";
import axios, { AxiosResponse } from "axios"; import axios, { AxiosResponse } from "axios";
import { FlowType } from "../../types/flow"; import { FlowType } from "../../types/flow";
export async function getAll(): Promise<AxiosResponse<APIObjectType>> { export async function getAll(): Promise<AxiosResponse<APIObjectType>> {
return await axios.get(`/all`); return await axios.get(`/api/v1/all`);
} }
export async function sendAll(data: sendAllProps) { export async function sendAll(data: sendAllProps) {
return await axios.post(`/predict`, data); return await axios.post(`/api/v1/predict`, data);
} }
export async function checkCode( export async function checkCode(
code: string code: string
): Promise<AxiosResponse<errorsTypeAPI>> { ): Promise<AxiosResponse<errorsTypeAPI>> {
return await axios.post("/validate/code", { code }); return await axios.post("/api/v1/validate/code", { code });
} }
export async function checkPrompt( export async function checkPrompt(
template: string template: string
): Promise<AxiosResponse<PromptTypeAPI>> { ): Promise<AxiosResponse<PromptTypeAPI>> {
return await axios.post("/validate/prompt", { template }); return await axios.post("/api/v1/validate/prompt", { template });
} }
export async function getExamples(): Promise<FlowType[]> { export async function getExamples(): Promise<FlowType[]> {
@ -47,5 +48,11 @@ export async function getExamples(): Promise<FlowType[]> {
export async function getBuildStatus( export async function getBuildStatus(
flowId: string flowId: string
): Promise<BuildStatusTypeAPI> { ): Promise<BuildStatusTypeAPI> {
return await axios.get(`/build/${flowId}/status`); return await axios.get(`/api/v1/build/${flowId}/status`);
}
export async function postBuildInit(
flow: FlowType
): Promise<AxiosResponse<InitTypeAPI>> {
return await axios.post(`/api/v1/build/init`, flow);
} }

View file

@ -42,3 +42,7 @@ export type PromptTypeAPI = { input_variables: Array<string> };
export type BuildStatusTypeAPI = { export type BuildStatusTypeAPI = {
built: boolean; built: boolean;
}; };
export type InitTypeAPI = {
flowId: string;
};

View file

@ -2,13 +2,12 @@ import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc"; import react from "@vitejs/plugin-react-swc";
import svgr from "vite-plugin-svgr"; import svgr from "vite-plugin-svgr";
const apiRoutes = [ const apiRoutes = [
"/all", "/api/v1/all",
"/predict", "/api/v1/predict",
"^/validate/*", "^/api/v1/validate/*",
"^/chat/*", "/api/v1/chat/*",
"/version", "/api/v1/version",
"/health", "/health",
"^/build/*",
]; ];
// Use environment variable to determine the target. // Use environment variable to determine the target.
@ -20,11 +19,9 @@ const proxyTargets = apiRoutes.reduce((proxyObj, route) => {
changeOrigin: true, changeOrigin: true,
secure: false, secure: false,
ws: true, ws: true,
rewrite: (path) => `/api/v1${path}`,
}; };
return proxyObj; return proxyObj;
}, {}); }, {});
export default defineConfig(() => { export default defineConfig(() => {
return { return {
build: { build: {