Merge branch 'validation_fix' of personal:logspace-ai/langflow into validation_fix
This commit is contained in:
commit
1a6406e394
7 changed files with 61 additions and 40 deletions
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -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: {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue