Merge branch 'zustand/io/migration' of github.com:logspace-ai/langflow into zustand/io/migration
This commit is contained in:
commit
0484a193b1
8 changed files with 72 additions and 62 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue