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

This commit is contained in:
igorrCarvalho 2024-03-11 17:34:14 -03:00
commit 0484a193b1
8 changed files with 72 additions and 62 deletions

View file

@ -8,7 +8,7 @@ from sqlmodel import Session, select
from langflow.api.utils import update_frontend_node_with_template_values from langflow.api.utils import update_frontend_node_with_template_values
from langflow.api.v1.schemas import ( from langflow.api.v1.schemas import (
CustomComponentCode, CustomComponentRequest,
InputValueRequest, InputValueRequest,
ProcessResponse, ProcessResponse,
RunResponse, RunResponse,
@ -253,12 +253,12 @@ def get_version():
@router.post("/custom_component", status_code=HTTPStatus.OK) @router.post("/custom_component", status_code=HTTPStatus.OK)
async def custom_component( async def custom_component(
raw_code: CustomComponentCode, raw_code: CustomComponentRequest,
user: User = Depends(get_current_active_user), user: User = Depends(get_current_active_user),
): ):
component = CustomComponent(code=raw_code.code) component = CustomComponent(code=raw_code.code)
built_frontend_node = build_custom_component_template(component, user_id=user.id) 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 return built_frontend_node
@ -275,23 +275,40 @@ async def reload_custom_component(path: str, user: User = Depends(get_current_ac
raise ValueError(content) raise ValueError(content)
extractor = CustomComponent(code=content) extractor = CustomComponent(code=content)
return build_custom_component_template(extractor, user_id=user.id) frontend_node, _ = build_custom_component_template(extractor, user_id=user.id)
return frontend_node
except Exception as exc: except Exception as exc:
raise HTTPException(status_code=400, detail=str(exc)) raise HTTPException(status_code=400, detail=str(exc))
@router.post("/custom_component/update", status_code=HTTPStatus.OK) @router.post("/custom_component/update", status_code=HTTPStatus.OK)
async def custom_component_update( async def custom_component_update(
raw_code: CustomComponentCode, code_request: CustomComponentRequest,
user: User = Depends(get_current_active_user), user: User = Depends(get_current_active_user),
): ):
component = CustomComponent(code=raw_code.code) """
Update a custom component with the provided code request.
component_node = build_custom_component_template( This endpoint generates the CustomComponentFrontendNode normally but then runs the `update_build_config` method
on the latest version of the template. This ensures that every time it runs, it has the latest version of the template.
Args:
code_request (CustomComponentRequest): The code request containing the updated code for the custom component.
user (User, optional): The user making the request. Defaults to the current active user.
Returns:
dict: The updated custom component node.
"""
component = CustomComponent(code=code_request.code)
component_node, cc_instance = build_custom_component_template(
component, component,
user_id=user.id, user_id=user.id,
update_field=raw_code.field,
update_field_value=raw_code.field_value,
) )
# Update the field updated_build_config = cc_instance.update_build_config(
code_request.template, code_request.field_value, code_request.field_name
)
component_node["template"] = updated_build_config
return component_node return component_node

View file

@ -4,8 +4,17 @@ from pathlib import Path
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional, Union
from uuid import UUID from uuid import UUID
from pydantic import BaseModel, Field, RootModel, field_validator, model_serializer from pydantic import (
BaseModel,
ConfigDict,
Field,
RootModel,
field_serializer,
field_validator,
model_serializer,
)
from langflow.schema import dotdict
from langflow.services.database.models.api_key.model import ApiKeyRead from langflow.services.database.models.api_key.model import ApiKeyRead
from langflow.services.database.models.base import orjson_dumps from langflow.services.database.models.base import orjson_dumps
from langflow.services.database.models.flow import FlowCreate, FlowRead from langflow.services.database.models.flow import FlowCreate, FlowRead
@ -161,12 +170,18 @@ class StreamData(BaseModel):
return f"event: {self.event}\ndata: {orjson_dumps(self.data, indent_2=False)}\n\n" return f"event: {self.event}\ndata: {orjson_dumps(self.data, indent_2=False)}\n\n"
class CustomComponentCode(BaseModel): class CustomComponentRequest(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
code: str code: str
field: Optional[str] = None field: Optional[str] = None
field_value: Optional[Any] = None field_value: Optional[Any] = None
template: Optional[dict] = None
frontend_node: Optional[dict] = None frontend_node: Optional[dict] = None
@field_serializer("template")
def template_into_dotdict(v):
return dotdict(v)
class CustomComponentResponseError(BaseModel): class CustomComponentResponseError(BaseModel):
detail: str detail: str

View file

@ -162,14 +162,16 @@ class Graph:
if vertex is None: if vertex is None:
raise ValueError(f"Vertex {vertex_id} not found") raise ValueError(f"Vertex {vertex_id} not found")
vertex.update_raw_params({"session_id": session_id}) vertex.update_raw_params({"session_id": session_id})
# Process the graph
try: try:
await self.process() await self.process()
self.increment_run_count() self.increment_run_count()
except Exception as exc: except Exception as exc:
logger.exception(exc) logger.exception(exc)
raise ValueError(f"Error running graph: {exc}") from exc raise ValueError(f"Error running graph: {exc}") from exc
# Get the outputs
vertex_outputs = [] vertex_outputs = []
for vertex_id in self._is_output_vertices: for vertex_id in self.vertices:
vertex = self.get_vertex(vertex_id) vertex = self.get_vertex(vertex_id)
if vertex is None: if vertex is None:
raise ValueError(f"Vertex {vertex_id} not found") raise ValueError(f"Vertex {vertex_id} not found")
@ -178,6 +180,7 @@ class Graph:
await vertex.consume_async_generator() await vertex.consume_async_generator()
if not outputs or (vertex.display_name in outputs or vertex.id in outputs): if not outputs or (vertex.display_name in outputs or vertex.id in outputs):
vertex_outputs.append(vertex.result) vertex_outputs.append(vertex.result)
return vertex_outputs return vertex_outputs
async def run( async def run(

View file

@ -3,7 +3,7 @@ import contextlib
import re import re
import traceback import traceback
import warnings import warnings
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional, Tuple, Union
from uuid import UUID from uuid import UUID
from fastapi import HTTPException from fastapi import HTTPException
@ -243,9 +243,7 @@ def get_field_dict(field: Union[TemplateField, dict]):
def run_build_config( def run_build_config(
custom_component: CustomComponent, custom_component: CustomComponent,
user_id: Optional[Union[str, UUID]] = None, user_id: Optional[Union[str, UUID]] = None,
update_field=None, ) -> Tuple[dict, CustomComponent]:
update_field_value=None,
):
"""Build the field configuration for a custom component""" """Build the field configuration for a custom component"""
try: try:
@ -274,38 +272,6 @@ def run_build_config(
# as a dict with the same keys as TemplateField # as a dict with the same keys as TemplateField
field_dict = get_field_dict(field) field_dict = get_field_dict(field)
build_config[field_name] = field_dict build_config[field_name] = field_dict
# This has to be done to set refresh if options or value are callable
if update_field is not None and field_name != update_field:
build_config = update_field_dict(
custom_component_instance=custom_instance,
field_dict=field_dict,
build_config=build_config,
call=False,
)
continue
try:
build_config = update_field_dict(
custom_component_instance=custom_instance,
field_dict=field_dict,
build_config=build_config,
update_field=update_field,
update_field_value=update_field_value,
call=True,
)
build_config[field_name] = field_dict
except Exception as exc:
logger.error(f"Error while getting build_config: {str(exc)}")
if isinstance(exc, UpdateBuildConfigError):
message = str(exc)
else:
message = f"Error while getting build_config: {str(exc)}"
raise HTTPException(
status_code=400,
detail={
"error": message,
"traceback": traceback.format_exc(),
},
) from exc
return build_config, custom_instance return build_config, custom_instance
@ -358,9 +324,7 @@ def add_code_field(frontend_node: CustomComponentFrontendNode, raw_code, field_c
def build_custom_component_template( def build_custom_component_template(
custom_component: CustomComponent, custom_component: CustomComponent,
user_id: Optional[Union[str, UUID]] = None, user_id: Optional[Union[str, UUID]] = None,
update_field: Optional[str] = None, ) -> Tuple[Dict[str, Any], CustomComponent]:
update_field_value: Optional[str] = None,
) -> Optional[Dict[str, Any]]:
"""Build a custom component template for the langchain""" """Build a custom component template for the langchain"""
try: try:
frontend_node = build_frontend_node(custom_component.template_config) frontend_node = build_frontend_node(custom_component.template_config)
@ -368,8 +332,6 @@ def build_custom_component_template(
field_config, custom_instance = run_build_config( field_config, custom_instance = run_build_config(
custom_component, custom_component,
user_id=user_id, user_id=user_id,
update_field=update_field,
update_field_value=update_field_value,
) )
entrypoint_args = custom_component.get_function_entrypoint_args entrypoint_args = custom_component.get_function_entrypoint_args
@ -383,7 +345,7 @@ def build_custom_component_template(
reorder_fields(frontend_node, custom_instance._get_field_order()) reorder_fields(frontend_node, custom_instance._get_field_order())
return frontend_node.to_dict(add_name=False) return frontend_node.to_dict(add_name=False), custom_instance
except Exception as exc: except Exception as exc:
if isinstance(exc, HTTPException): if isinstance(exc, HTTPException):
raise exc raise exc
@ -403,7 +365,7 @@ def create_component_template(component):
component_extractor = CustomComponent(code=component_code) component_extractor = CustomComponent(code=component_code)
component_template = build_custom_component_template(component_extractor) component_template, _ = build_custom_component_template(component_extractor)
if not component_template["output_types"] and component_output_types: if not component_template["output_types"] and component_output_types:
component_template["output_types"] = component_output_types component_template["output_types"] = component_output_types

View file

@ -85,8 +85,8 @@ export default function GenericNode({
// return // return
if (!thisNodeTemplate.code) return; if (!thisNodeTemplate.code) return;
const currentCode = thisNodeTemplate.code.value; const currentCode = thisNodeTemplate.code?.value;
const thisNodesCode = data.node!.template.code.value; const thisNodesCode = data.node!.template?.code?.value;
if (currentCode !== thisNodesCode) { if (currentCode !== thisNodesCode) {
addToOutdatedNodes(data.id); addToOutdatedNodes(data.id);
setIsOutdated(true); setIsOutdated(true);
@ -96,7 +96,7 @@ export default function GenericNode({
setIsOutdated(false); setIsOutdated(false);
} }
// template.code can be undefined // template.code can be undefined
}, [data.node?.template.code.value]); }, [data.node?.template?.code?.value]);
const updateNodeCode = useCallback( const updateNodeCode = useCallback(
(newNodeClass: APIClassType, code: string, name: string) => { (newNodeClass: APIClassType, code: string, name: string) => {

View file

@ -4,6 +4,7 @@ import { BASE_URL_API } from "../../constants/constants";
import { api } from "../../controllers/API/api"; import { api } from "../../controllers/API/api";
import { import {
APIObjectType, APIObjectType,
APITemplateType,
Component, Component,
LoginType, LoginType,
Users, Users,
@ -369,11 +370,13 @@ export async function postCustomComponent(
export async function postCustomComponentUpdate( export async function postCustomComponentUpdate(
code: string, code: string,
template: APITemplateType,
field: string, field: string,
field_value: any field_value: any
): Promise<AxiosResponse<APIClassType>> { ): Promise<AxiosResponse<APIClassType>> {
return await api.post(`${BASE_URL_API}custom_component/update`, { return await api.post(`${BASE_URL_API}custom_component/update`, {
code, code,
template,
field, field,
field_value, field_value,
}); });

View file

@ -9,9 +9,15 @@ export const handleUpdateValues = async (name: string, data: NodeDataType) => {
console.error("Code not found in the template"); console.error("Code not found in the template");
return; return;
} }
const template = data.node?.template;
if (!template) {
console.error("No template found in the node.");
return;
}
try { try {
let newTemplate = await postCustomComponentUpdate( let newTemplate = await postCustomComponentUpdate(
code, code,
template,
name, name,
data.node?.template[name]?.value data.node?.template[name]?.value
) )

View file

@ -24,7 +24,9 @@ def test_document_to_record_component():
# Act # Act
# Replace with your actual test data # Replace with your actual test data
document = Document(page_content="key: value", metadata={"url": "https://example.com"}) document = Document(
page_content="key: value", metadata={"url": "https://example.com"}
)
result = document_to_record_component.build(document) result = document_to_record_component.build(document)
# Assert # Assert
@ -37,12 +39,14 @@ def test_uuid_generator_component():
uuid_generator_component = helpers.UUIDGeneratorComponent() uuid_generator_component = helpers.UUIDGeneratorComponent()
uuid_generator_component.code = open(helpers.IDGenerator.__file__, "r").read() uuid_generator_component.code = open(helpers.IDGenerator.__file__, "r").read()
frontend_node = build_custom_component_template(uuid_generator_component) frontend_node, _ = build_custom_component_template(uuid_generator_component)
# Act # Act
build_config = frontend_node.get("template") build_config = frontend_node.get("template")
field_name = "unique_id" field_name = "unique_id"
build_config = uuid_generator_component.update_build_config(build_config, None, field_name) build_config = uuid_generator_component.update_build_config(
build_config, None, field_name
)
unique_id = build_config["unique_id"]["value"] unique_id = build_config["unique_id"]["value"]
result = uuid_generator_component.build(unique_id) result = uuid_generator_component.build(unique_id)