Merge branch 'form_io' of github.com:logspace-ai/langflow into form_io
This commit is contained in:
commit
4616ca68bd
9 changed files with 133 additions and 41 deletions
|
|
@ -77,7 +77,7 @@ def validate_prompt(template: str):
|
||||||
def check_input_variables(input_variables: list):
|
def check_input_variables(input_variables: list):
|
||||||
invalid_chars = []
|
invalid_chars = []
|
||||||
fixed_variables = []
|
fixed_variables = []
|
||||||
wrong_variables = set()
|
wrong_variables = []
|
||||||
empty_variables = []
|
empty_variables = []
|
||||||
for variable in input_variables:
|
for variable in input_variables:
|
||||||
new_var = variable
|
new_var = variable
|
||||||
|
|
@ -92,17 +92,14 @@ def check_input_variables(input_variables: list):
|
||||||
if variable[0].isdigit():
|
if variable[0].isdigit():
|
||||||
invalid_chars.append(variable[0])
|
invalid_chars.append(variable[0])
|
||||||
new_var = new_var.replace(variable[0], "")
|
new_var = new_var.replace(variable[0], "")
|
||||||
wrong_variables.add(variable)
|
wrong_variables.append(variable)
|
||||||
|
else:
|
||||||
for char in INVALID_CHARACTERS:
|
for char in INVALID_CHARACTERS:
|
||||||
if char in variable:
|
if char in variable:
|
||||||
invalid_chars.append(char)
|
invalid_chars.append(char)
|
||||||
new_var = new_var.replace(char, "")
|
new_var = new_var.replace(char, "")
|
||||||
wrong_variables.add(variable)
|
wrong_variables.append(variable)
|
||||||
fixed_variables.append(new_var)
|
fixed_variables.append(new_var)
|
||||||
# if new_var != variable and new_var not in input_variables:
|
|
||||||
# input_variables.remove(variable)
|
|
||||||
# input_variables.append(new_var)
|
|
||||||
# If any of the input_variables is not in the fixed_variables, then it means that
|
# If any of the input_variables is not in the fixed_variables, then it means that
|
||||||
# there are invalid characters in the input_variables
|
# there are invalid characters in the input_variables
|
||||||
|
|
||||||
|
|
@ -122,17 +119,20 @@ def build_error_message(
|
||||||
input_variables, invalid_chars, wrong_variables, fixed_variables, empty_variables
|
input_variables, invalid_chars, wrong_variables, fixed_variables, empty_variables
|
||||||
):
|
):
|
||||||
input_variables_str = ", ".join([f"'{var}'" for var in input_variables])
|
input_variables_str = ", ".join([f"'{var}'" for var in input_variables])
|
||||||
error_string = f"Invalid input variables: {input_variables_str}."
|
error_string = f"Invalid input variables: {input_variables_str}. "
|
||||||
|
|
||||||
if wrong_variables and invalid_chars:
|
if wrong_variables and invalid_chars:
|
||||||
", ".join([f"'{var}'" for var in wrong_variables])
|
# fix the wrong variables replacing invalid chars and find them in the fixed variables
|
||||||
invalid_chars_str = ", ".join([f"'{char}'" for char in invalid_chars])
|
error_string_vars = "You can fix them by replacing the invalid characters: "
|
||||||
error_string += (
|
wvars = wrong_variables.copy()
|
||||||
f" Please, remove the invalid characters: {invalid_chars_str}"
|
for i, wrong_var in enumerate(wvars):
|
||||||
" from the variables: {wrong_variables_str}."
|
for char in invalid_chars:
|
||||||
)
|
wrong_var = wrong_var.replace(char, "")
|
||||||
|
if wrong_var in fixed_variables:
|
||||||
|
error_string_vars += f"'{wrong_variables[i]}' -> '{wrong_var}'"
|
||||||
|
error_string += error_string_vars
|
||||||
elif empty_variables:
|
elif empty_variables:
|
||||||
error_string += f" There are {len(empty_variables)} empty variable{'s' if len(empty_variables) > 1 else ''}."
|
error_string += f" There are {len(empty_variables)} empty variable{'s' if len(empty_variables) > 1 else ''}."
|
||||||
elif len(set(fixed_variables)) != len(fixed_variables):
|
elif len(set(fixed_variables)) != len(fixed_variables):
|
||||||
error_string += " There are duplicate variables."
|
error_string += "There are duplicate variables."
|
||||||
return error_string
|
return error_string
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,107 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from langchain.callbacks.base import AsyncCallbackHandler, BaseCallbackHandler
|
from langchain.callbacks.base import AsyncCallbackHandler, BaseCallbackHandler
|
||||||
|
|
||||||
from langflow.api.v1.schemas import ChatResponse
|
from langflow.api.v1.schemas import ChatResponse
|
||||||
|
|
||||||
|
|
||||||
|
from typing import Any, Dict, List, Union
|
||||||
|
from fastapi import WebSocket
|
||||||
|
|
||||||
|
|
||||||
|
from langchain.schema import AgentAction, LLMResult, AgentFinish
|
||||||
|
|
||||||
|
|
||||||
# https://github.com/hwchase17/chat-langchain/blob/master/callback.py
|
# https://github.com/hwchase17/chat-langchain/blob/master/callback.py
|
||||||
class AsyncStreamingLLMCallbackHandler(AsyncCallbackHandler):
|
class AsyncStreamingLLMCallbackHandler(AsyncCallbackHandler):
|
||||||
"""Callback handler for streaming LLM responses."""
|
"""Callback handler for streaming LLM responses."""
|
||||||
|
|
||||||
def __init__(self, websocket):
|
def __init__(self, websocket: WebSocket):
|
||||||
self.websocket = websocket
|
self.websocket = websocket
|
||||||
|
|
||||||
async def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
|
async def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
|
||||||
resp = ChatResponse(message=token, type="stream", intermediate_steps="")
|
resp = ChatResponse(message=token, type="stream", intermediate_steps="")
|
||||||
await self.websocket.send_json(resp.dict())
|
await self.websocket.send_json(resp.dict())
|
||||||
|
|
||||||
|
async def on_llm_start(
|
||||||
|
self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
|
||||||
|
) -> Any:
|
||||||
|
"""Run when LLM starts running."""
|
||||||
|
|
||||||
|
async def on_llm_end(self, response: LLMResult, **kwargs: Any) -> Any:
|
||||||
|
"""Run when LLM ends running."""
|
||||||
|
|
||||||
|
async def on_llm_error(
|
||||||
|
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
||||||
|
) -> Any:
|
||||||
|
"""Run when LLM errors."""
|
||||||
|
|
||||||
|
async def on_chain_start(
|
||||||
|
self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs: Any
|
||||||
|
) -> Any:
|
||||||
|
"""Run when chain starts running."""
|
||||||
|
|
||||||
|
async def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) -> Any:
|
||||||
|
"""Run when chain ends running."""
|
||||||
|
|
||||||
|
async def on_chain_error(
|
||||||
|
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
||||||
|
) -> Any:
|
||||||
|
"""Run when chain errors."""
|
||||||
|
|
||||||
|
async def on_tool_start(
|
||||||
|
self, serialized: Dict[str, Any], input_str: str, **kwargs: Any
|
||||||
|
) -> Any:
|
||||||
|
"""Run when tool starts running."""
|
||||||
|
resp = ChatResponse(
|
||||||
|
message="",
|
||||||
|
type="stream",
|
||||||
|
intermediate_steps=f"Tool input: {input_str}",
|
||||||
|
)
|
||||||
|
await self.websocket.send_json(resp.dict())
|
||||||
|
|
||||||
|
async def on_tool_end(self, output: str, **kwargs: Any) -> Any:
|
||||||
|
"""Run when tool ends running."""
|
||||||
|
resp = ChatResponse(
|
||||||
|
message="",
|
||||||
|
type="stream",
|
||||||
|
intermediate_steps=f"Tool output: {output}",
|
||||||
|
)
|
||||||
|
await self.websocket.send_json(resp.dict())
|
||||||
|
|
||||||
|
async def on_tool_error(
|
||||||
|
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
||||||
|
) -> Any:
|
||||||
|
"""Run when tool errors."""
|
||||||
|
|
||||||
|
async def on_text(self, text: str, **kwargs: Any) -> Any:
|
||||||
|
"""Run on arbitrary text."""
|
||||||
|
# This runs when first sending the prompt
|
||||||
|
# to the LLM, adding it will send the final prompt
|
||||||
|
# to the frontend
|
||||||
|
|
||||||
|
async def on_agent_action(self, action: AgentAction, **kwargs: Any):
|
||||||
|
log = f"Thought: {action.log}"
|
||||||
|
# if there are line breaks, split them and send them
|
||||||
|
# as separate messages
|
||||||
|
if "\n" in log:
|
||||||
|
logs = log.split("\n")
|
||||||
|
for log in logs:
|
||||||
|
resp = ChatResponse(message="", type="stream", intermediate_steps=log)
|
||||||
|
await self.websocket.send_json(resp.dict())
|
||||||
|
else:
|
||||||
|
resp = ChatResponse(message="", type="stream", intermediate_steps=log)
|
||||||
|
await self.websocket.send_json(resp.dict())
|
||||||
|
|
||||||
|
async def on_agent_finish(self, finish: AgentFinish, **kwargs: Any) -> Any:
|
||||||
|
"""Run on agent end."""
|
||||||
|
resp = ChatResponse(
|
||||||
|
message="",
|
||||||
|
type="stream",
|
||||||
|
intermediate_steps=finish.log,
|
||||||
|
)
|
||||||
|
await self.websocket.send_json(resp.dict())
|
||||||
|
|
||||||
|
|
||||||
class StreamingLLMCallbackHandler(BaseCallbackHandler):
|
class StreamingLLMCallbackHandler(BaseCallbackHandler):
|
||||||
"""Callback handler for streaming LLM responses."""
|
"""Callback handler for streaming LLM responses."""
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ async def process_graph(
|
||||||
try:
|
try:
|
||||||
logger.debug("Generating result and thought")
|
logger.debug("Generating result and thought")
|
||||||
result, intermediate_steps = await get_result_and_steps(
|
result, intermediate_steps = await get_result_and_steps(
|
||||||
langchain_object, chat_inputs.message or "", websocket=websocket
|
langchain_object, chat_inputs.message, websocket=websocket
|
||||||
)
|
)
|
||||||
logger.debug("Generated result and intermediate_steps")
|
logger.debug("Generated result and intermediate_steps")
|
||||||
return result, intermediate_steps
|
return result, intermediate_steps
|
||||||
|
|
|
||||||
|
|
@ -226,4 +226,5 @@ class Vertex:
|
||||||
return id(self)
|
return id(self)
|
||||||
|
|
||||||
def _built_object_repr(self):
|
def _built_object_repr(self):
|
||||||
return "Built sucessfully" if self._built_object else "Not built yet"
|
# Add a message with an emoji, stars for sucess,
|
||||||
|
return "Built sucessfully ✨" if self._built_object else "Failed to build 😵💫"
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,15 @@ class PromptVertex(Vertex):
|
||||||
self._build()
|
self._build()
|
||||||
return self._built_object
|
return self._built_object
|
||||||
|
|
||||||
|
def _built_object_repr(self):
|
||||||
|
if self.artifacts and hasattr(self._built_object, "format"):
|
||||||
|
# We'll build the prompt with the artifacts
|
||||||
|
# to show the user what the prompt looks like
|
||||||
|
# with the variables filled in
|
||||||
|
return self._built_object.format(**self.artifacts)
|
||||||
|
else:
|
||||||
|
super()._built_object_repr()
|
||||||
|
|
||||||
|
|
||||||
class OutputParserVertex(Vertex):
|
class OutputParserVertex(Vertex):
|
||||||
def __init__(self, data: Dict):
|
def __init__(self, data: Dict):
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,12 @@ def instantiate_llm(node_type, class_object, params: Dict):
|
||||||
|
|
||||||
|
|
||||||
def instantiate_memory(node_type, class_object, params):
|
def instantiate_memory(node_type, class_object, params):
|
||||||
|
# process input_key and output_key to remove them if
|
||||||
|
# they are empty strings
|
||||||
|
for key in ["input_key", "output_key"]:
|
||||||
|
if key in params and not params[key]:
|
||||||
|
params.pop(key)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if "retriever" in params and hasattr(params["retriever"], "as_retriever"):
|
if "retriever" in params and hasattr(params["retriever"], "as_retriever"):
|
||||||
params["retriever"] = params["retriever"].as_retriever()
|
params["retriever"] = params["retriever"].as_retriever()
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,6 @@ export default function GenericNode({
|
||||||
deleteNode(data.id);
|
deleteNode(data.id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(data);
|
|
||||||
useEffect(() => {}, [closePopUp, data.node.template]);
|
useEffect(() => {}, [closePopUp, data.node.template]);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -121,10 +120,11 @@ export default function GenericNode({
|
||||||
"Validating..."
|
"Validating..."
|
||||||
) : (
|
) : (
|
||||||
<div className="max-h-96 overflow-auto">
|
<div className="max-h-96 overflow-auto">
|
||||||
{validationStatus.params ||
|
{validationStatus.params
|
||||||
""
|
? validationStatus.params
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.map((line, index) => <div key={index}>{line}</div>)}
|
.map((line, index) => <div key={index}>{line}</div>)
|
||||||
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import { useSSE } from "../../../contexts/SSEContext";
|
||||||
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";
|
import { postBuildInit } from "../../../controllers/API";
|
||||||
import ShadTooltip from "../../ShadTooltipComponent";
|
|
||||||
|
|
||||||
import RadialProgressComponent from "../../RadialProgress";
|
import RadialProgressComponent from "../../RadialProgress";
|
||||||
import { TabsContext } from "../../../contexts/tabsContext";
|
import { TabsContext } from "../../../contexts/tabsContext";
|
||||||
|
|
@ -76,7 +75,6 @@ export default function BuildTrigger({
|
||||||
const eventSource = new EventSource(apiUrl);
|
const eventSource = new EventSource(apiUrl);
|
||||||
|
|
||||||
eventSource.onmessage = (event) => {
|
eventSource.onmessage = (event) => {
|
||||||
console.log(event);
|
|
||||||
// If the event is parseable, return
|
// If the event is parseable, return
|
||||||
if (!event.data) {
|
if (!event.data) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,7 @@ import {
|
||||||
varHighlightHTML,
|
varHighlightHTML,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { Badge } from "../../components/ui/badge";
|
import { Badge } from "../../components/ui/badge";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "../../components/ui/tooltip";
|
|
||||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||||
import { set } from "lodash";
|
|
||||||
import DOMPurify from "dompurify";
|
import DOMPurify from "dompurify";
|
||||||
|
|
||||||
export default function GenericModal({
|
export default function GenericModal({
|
||||||
|
|
@ -68,7 +61,8 @@ export default function GenericModal({
|
||||||
const [wordsHighlightInvalid, setWordsHighlightInvalid] = useState([]);
|
const [wordsHighlightInvalid, setWordsHighlightInvalid] = useState([]);
|
||||||
const [wordsHighlight, setWordsHighlight] = useState([]);
|
const [wordsHighlight, setWordsHighlight] = useState([]);
|
||||||
const { dark } = useContext(darkContext);
|
const { dark } = useContext(darkContext);
|
||||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
const { setErrorData, setSuccessData, setNoticeData } =
|
||||||
|
useContext(alertContext);
|
||||||
const { closePopUp, setCloseEdit } = useContext(PopUpContext);
|
const { closePopUp, setCloseEdit } = useContext(PopUpContext);
|
||||||
const ref = useRef();
|
const ref = useRef();
|
||||||
function setModalOpen(x: boolean) {
|
function setModalOpen(x: boolean) {
|
||||||
|
|
@ -149,9 +143,8 @@ export default function GenericModal({
|
||||||
let inputVariables = apiReturn.data.input_variables;
|
let inputVariables = apiReturn.data.input_variables;
|
||||||
if (inputVariables.length === 0) {
|
if (inputVariables.length === 0) {
|
||||||
setIsEdit(true);
|
setIsEdit(true);
|
||||||
setErrorData({
|
setNoticeData({
|
||||||
title:
|
title: "Your template does not have any variables.",
|
||||||
"The template you are attempting to use does not contain any variables for data entry.",
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setIsEdit(false);
|
setIsEdit(false);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue