Merge branch 'zustand/io/migration' of github.com:logspace-ai/langflow into zustand/io/migration

This commit is contained in:
igorrCarvalho 2024-02-27 14:09:48 -03:00
commit 952561d628
29 changed files with 317 additions and 103 deletions

View file

@ -3,11 +3,15 @@ from typing import Annotated, Any, List, Optional, Union
import sqlalchemy as sa
from fastapi import APIRouter, Body, Depends, HTTPException, UploadFile, status
from loguru import logger
from sqlmodel import select
from langflow.api.utils import update_frontend_node_with_template_values
from langflow.api.v1.schemas import (
CustomComponentCode,
PreloadResponse,
ProcessResponse,
RunResponse,
TaskResponse,
TaskStatusResponse,
UploadFileResponse,
@ -15,15 +19,23 @@ from langflow.api.v1.schemas import (
from langflow.interface.custom.custom_component import CustomComponent
from langflow.interface.custom.directory_reader import DirectoryReader
from langflow.interface.custom.utils import build_custom_component_template
from langflow.processing.process import build_graph_and_generate_result, process_graph_cached, process_tweaks
from langflow.processing.process import (
build_graph_and_generate_result,
process_graph_cached,
process_tweaks,
run_graph,
)
from langflow.services.auth.utils import api_key_security, get_current_active_user
from langflow.services.cache.utils import save_uploaded_file
from langflow.services.database.models.flow import Flow
from langflow.services.database.models.user.model import User
from langflow.services.deps import get_session, get_session_service, get_settings_service, get_task_service
from langflow.services.deps import (
get_session,
get_session_service,
get_settings_service,
get_task_service,
)
from langflow.services.session.service import SessionService
from loguru import logger
from sqlmodel import select
try:
from langflow.worker import process_graph_cached_task
@ -33,9 +45,10 @@ except ImportError:
raise NotImplementedError("Celery is not installed")
from langflow.services.task.service import TaskService
from sqlmodel import Session
from langflow.services.task.service import TaskService
# build router
router = APIRouter(tags=["Base"])
@ -80,9 +93,15 @@ async def process_graph_data(
)
if session_id is None:
# Generate a session ID
session_id = get_session_service().generate_key(session_id=session_id, data_graph=graph_data)
session_id = get_session_service().generate_key(
session_id=session_id, data_graph=graph_data
)
task_id, task = await task_service.launch_task(
process_graph_cached_task if task_service.use_celery else process_graph_cached,
(
process_graph_cached_task
if task_service.use_celery
else process_graph_cached
),
graph_data,
inputs,
clear_cache,
@ -176,7 +195,11 @@ async def preload_flow(
else:
if session_id is None:
session_id = flow_id
flow = session.exec(select(Flow).where(Flow.id == flow_id).where(Flow.user_id == api_key_user.id)).first()
flow = session.exec(
select(Flow)
.where(Flow.id == flow_id)
.where(Flow.user_id == api_key_user.id)
).first()
if flow is None:
raise ValueError(f"Flow {flow_id} not found")
@ -197,6 +220,76 @@ async def preload_flow(
raise HTTPException(status_code=500, detail=str(exc)) from exc
@router.post("/run/{flow_id}", response_model=ProcessResponse)
async def run_flow_with_caching(
session: Annotated[Session, Depends(get_session)],
flow_id: str,
inputs: Optional[Union[List[dict], dict]] = None,
tweaks: Optional[dict] = None,
session_id: Annotated[Union[None, str], Body(embed=True)] = None, # noqa: F821
api_key_user: User = Depends(api_key_security),
session_service: SessionService = Depends(get_session_service),
):
try:
if session_id:
session_data = await session_service.load_session(session_id)
graph, artifacts = session_data if session_data else (None, None)
task_result: Any = None
task_status = None
if not graph:
raise ValueError("Graph not found in the session")
task_result = await run_graph(
graph,
session_id,
inputs,
artifacts=artifacts,
session_service=session_service,
)
else:
# Get the flow that matches the flow_id and belongs to the user
# flow = session.query(Flow).filter(Flow.id == flow_id).filter(Flow.user_id == api_key_user.id).first()
flow = session.exec(
select(Flow)
.where(Flow.id == flow_id)
.where(Flow.user_id == api_key_user.id)
).first()
if flow is None:
raise ValueError(f"Flow {flow_id} not found")
if flow.data is None:
raise ValueError(f"Flow {flow_id} has no data")
graph_data = flow.data
graph_data = process_tweaks(graph_data, tweaks)
task_result = await run_graph(
graph_data,
inputs,
tweaks,
session_id,
session_service=session_service,
)
return RunResponse(
outputs=task_result, session_id=session_id, status=task_status
)
except sa.exc.StatementError as exc:
# StatementError('(builtins.ValueError) badly formed hexadecimal UUID string')
if "badly formed hexadecimal UUID string" in str(exc):
# This means the Flow ID is not a valid UUID which means it can't find the flow
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)
) from exc
except ValueError as exc:
if f"Flow {flow_id} not found" in str(exc):
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)
) from exc
else:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc)
) from exc
@router.post(
"/predict/{flow_id}",
response_model=ProcessResponse,
@ -269,7 +362,11 @@ async def process(
# Get the flow that matches the flow_id and belongs to the user
# flow = session.query(Flow).filter(Flow.id == flow_id).filter(Flow.user_id == api_key_user.id).first()
flow = session.exec(select(Flow).where(Flow.id == flow_id).where(Flow.user_id == api_key_user.id)).first()
flow = session.exec(
select(Flow)
.where(Flow.id == flow_id)
.where(Flow.user_id == api_key_user.id)
).first()
if flow is None:
raise ValueError(f"Flow {flow_id} not found")
@ -289,12 +386,18 @@ async def process(
# StatementError('(builtins.ValueError) badly formed hexadecimal UUID string')
if "badly formed hexadecimal UUID string" in str(exc):
# This means the Flow ID is not a valid UUID which means it can't find the flow
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)
) from exc
except ValueError as exc:
if f"Flow {flow_id} not found" in str(exc):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)
) from exc
else:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc)) from exc
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc)
) from exc
except Exception as e:
# Log stack trace
logger.exception(e)
@ -364,12 +467,16 @@ async def custom_component(
built_frontend_node = build_custom_component_template(component, user_id=user.id)
built_frontend_node = update_frontend_node_with_template_values(built_frontend_node, raw_code.frontend_node)
built_frontend_node = update_frontend_node_with_template_values(
built_frontend_node, raw_code.frontend_node
)
return built_frontend_node
@router.post("/custom_component/reload", status_code=HTTPStatus.OK)
async def reload_custom_component(path: str, user: User = Depends(get_current_active_user)):
async def reload_custom_component(
path: str, user: User = Depends(get_current_active_user)
):
from langflow.interface.custom.utils import build_custom_component_template
try:
@ -391,6 +498,8 @@ async def custom_component_update(
):
component = CustomComponent(code=raw_code.code)
component_node = build_custom_component_template(component, user_id=user.id, update_field=raw_code.field)
component_node = build_custom_component_template(
component, user_id=user.id, update_field=raw_code.field
)
# Update the field
return component_node

View file

@ -66,6 +66,14 @@ class ProcessResponse(BaseModel):
backend: Optional[str] = None
class RunResponse(BaseModel):
"""Run response schema."""
outputs: Optional[List[Any]] = None
status: Optional[str] = None
session_id: Optional[str] = None
class PreloadResponse(BaseModel):
"""Preload response schema."""
@ -73,9 +81,6 @@ class PreloadResponse(BaseModel):
is_clear: Optional[bool] = None
# TaskStatusResponse(
# status=task.status, result=task.result if task.ready() else None
# )
class TaskStatusResponse(BaseModel):
"""Task status response schema."""

View file

@ -23,7 +23,7 @@ class ConversationChainComponent(CustomComponent):
def build(
self,
inputs: str,
input_value: str,
llm: BaseLanguageModel,
memory: Optional[BaseMemory] = None,
) -> Text:

View file

@ -18,7 +18,7 @@ class LLMCheckerChainComponent(CustomComponent):
def build(
self,
inputs: str,
input_value: str,
llm: BaseLanguageModel,
) -> Text:

View file

@ -24,7 +24,7 @@ class LLMMathChainComponent(CustomComponent):
def build(
self,
inputs: Text,
input_value: Text,
llm: BaseLanguageModel,
llm_chain: LLMChain,
input_key: str = "question",

View file

@ -27,7 +27,7 @@ class RetrievalQAComponent(CustomComponent):
self,
combine_documents_chain: BaseCombineDocumentsChain,
retriever: BaseRetriever,
inputs: str = "",
input_value: str = "",
memory: Optional[BaseMemory] = None,
input_key: str = "query",
output_key: str = "result",

View file

@ -26,7 +26,7 @@ class RetrievalQAWithSourcesChainComponent(CustomComponent):
def build(
self,
inputs: str,
input_value: str,
retriever: BaseRetriever,
llm: BaseLanguageModel,
chain_type: str,

View file

@ -28,7 +28,7 @@ class SQLGeneratorComponent(CustomComponent):
def build(
self,
inputs: Text,
input_value: Text,
db: SQLDatabase,
llm: BaseLanguageModel,
top_k: int = 5,

View file

@ -11,7 +11,7 @@ class ChatInput(CustomComponent):
def build_config(self):
return {
"message": {
"input_value": {
"input_types": ["Text"],
"display_name": "Message",
"multiline": True,
@ -35,26 +35,26 @@ class ChatInput(CustomComponent):
self,
sender: Optional[str] = "User",
sender_name: Optional[str] = "User",
message: Optional[str] = None,
input_value: Optional[str] = None,
session_id: Optional[str] = None,
return_record: Optional[bool] = False,
) -> Union[Text, Record]:
if return_record:
if isinstance(message, Record):
if isinstance(input_value, Record):
# Update the data of the record
message.data["sender"] = sender
message.data["sender_name"] = sender_name
message.data["session_id"] = session_id
input_value.data["sender"] = sender
input_value.data["sender_name"] = sender_name
input_value.data["session_id"] = session_id
else:
message = Record(
text=message,
input_value = Record(
text=input_value,
data={
"sender": sender,
"sender_name": sender_name,
"session_id": session_id,
},
)
if not message:
message = ""
self.status = message
return message
if not input_value:
input_value = ""
self.status = input_value
return input_value

View file

@ -17,7 +17,7 @@ class ChatOutput(CustomComponent):
def build_config(self):
return {
"message": {"input_types": ["Text"], "display_name": "Message"},
"input_value": {"input_types": ["Text"], "display_name": "Message"},
"sender": {
"options": ["Machine", "User"],
"display_name": "Sender Type",
@ -39,25 +39,25 @@ class ChatOutput(CustomComponent):
sender: Optional[str] = "Machine",
sender_name: Optional[str] = "AI",
session_id: Optional[str] = None,
message: Optional[str] = None,
input_value: Optional[str] = None,
return_record: Optional[bool] = False,
) -> Union[Text, Record]:
if return_record:
if isinstance(message, Record):
if isinstance(input_value, Record):
# Update the data of the record
message.data["sender"] = sender
message.data["sender_name"] = sender_name
message.data["session_id"] = session_id
input_value.data["sender"] = sender
input_value.data["sender_name"] = sender_name
input_value.data["session_id"] = session_id
else:
message = Record(
text=message,
input_value = Record(
text=input_value,
data={
"sender": sender,
"sender_name": sender_name,
"session_id": session_id,
},
)
if not message:
message = ""
self.status = message
return message
if not input_value:
input_value = ""
self.status = input_value
return input_value

View file

@ -39,7 +39,7 @@ class AmazonBedrockComponent(CustomComponent):
def build(
self,
inputs: str,
input_value: str,
model_id: str = "anthropic.claude-instant-v1",
credentials_profile_name: Optional[str] = None,
region_name: Optional[str] = None,

View file

@ -9,7 +9,9 @@ from langflow.field_typing import Text
class AnthropicLLM(CustomComponent):
display_name: str = "AnthropicModel"
description: str = "Generate text using Anthropic Chat&Completion large language models."
description: str = (
"Generate text using Anthropic Chat&Completion large language models."
)
def build_config(self):
return {
@ -53,7 +55,7 @@ class AnthropicLLM(CustomComponent):
def build(
self,
model: str,
inputs: str,
input_value: str,
anthropic_api_key: Optional[str] = None,
max_tokens: Optional[int] = None,
temperature: Optional[float] = None,
@ -66,7 +68,9 @@ class AnthropicLLM(CustomComponent):
try:
output = ChatAnthropic(
model_name=model,
anthropic_api_key=(SecretStr(anthropic_api_key) if anthropic_api_key else None),
anthropic_api_key=(
SecretStr(anthropic_api_key) if anthropic_api_key else None
),
max_tokens_to_sample=max_tokens, # type: ignore
temperature=temperature,
anthropic_api_url=api_endpoint,

View file

@ -9,7 +9,9 @@ from langflow import CustomComponent
class AzureChatOpenAIComponent(CustomComponent):
display_name: str = "AzureOpenAI Model"
description: str = "Generate text using LLM model from Azure OpenAI."
documentation: str = "https://python.langchain.com/docs/integrations/llms/azure_openai"
documentation: str = (
"https://python.langchain.com/docs/integrations/llms/azure_openai"
)
beta = False
AZURE_OPENAI_MODELS = [
@ -78,7 +80,7 @@ class AzureChatOpenAIComponent(CustomComponent):
self,
model: str,
azure_endpoint: str,
inputs: str,
input_value: str,
azure_deployment: str,
api_key: str,
api_version: str,

View file

@ -73,7 +73,7 @@ class QianfanChatEndpointComponent(CustomComponent):
def build(
self,
inputs: str,
input_value: str,
model: str = "ERNIE-Bot-turbo",
qianfan_ak: Optional[str] = None,
qianfan_sk: Optional[str] = None,

View file

@ -35,11 +35,13 @@ class CTransformersComponent(CustomComponent):
self,
model: str,
model_file: str,
inputs: str,
input_value: str,
model_type: str,
config: Optional[Dict] = None,
) -> Text:
output = CTransformers(model=model, model_file=model_file, model_type=model_type, config=config)
output = CTransformers(
model=model, model_file=model_file, model_type=model_type, config=config
)
message = output.invoke(inputs)
result = message.content if hasattr(message, "content") else message
self.status = result

View file

@ -34,7 +34,7 @@ class CohereComponent(CustomComponent):
def build(
self,
cohere_api_key: str,
inputs: str,
input_value: str,
max_tokens: int = 256,
temperature: float = 0.75,
) -> Text:

View file

@ -57,7 +57,7 @@ class GoogleGenerativeAIComponent(CustomComponent):
self,
google_api_key: str,
model: str,
inputs: str,
input_value: str,
max_output_tokens: Optional[int] = None,
temperature: float = 0.1,
top_k: Optional[int] = None,

View file

@ -4,7 +4,6 @@ from langchain_community.chat_models.huggingface import ChatHuggingFace
from langchain_community.llms.huggingface_endpoint import HuggingFaceEndpoint
from langflow import CustomComponent
from langflow.field_typing import Text
@ -30,7 +29,7 @@ class HuggingFaceEndpointsComponent(CustomComponent):
def build(
self,
inputs: str,
input_value: str,
endpoint_url: str,
task: str = "text2text-generation",
huggingfacehub_api_token: Optional[str] = None,

View file

@ -62,7 +62,7 @@ class LlamaCppComponent(CustomComponent):
def build(
self,
model_path: str,
inputs: str,
input_value: str,
grammar: Optional[str] = None,
cache: Optional[bool] = None,
client: Optional[Any] = None,

View file

@ -171,7 +171,7 @@ class ChatOllamaComponent(CustomComponent):
self,
base_url: Optional[str],
model: str,
inputs: str,
input_value: str,
mirostat: Optional[str],
mirostat_eta: Optional[float] = None,
mirostat_tau: Optional[float] = None,

Some files were not shown because too many files have changed in this diff Show more