Merge branch 'dev' into fix/api_settings
This commit is contained in:
commit
5e16e8ac4b
40 changed files with 634 additions and 424 deletions
|
|
@ -1,3 +1,4 @@
|
|||
from .load import load_flow_from_json, run_flow_from_json # noqa: F401
|
||||
from .load import load_flow_from_json, run_flow_from_json
|
||||
from .utils import upload_file, get_flow
|
||||
|
||||
__all__ = ["load_flow_from_json", "run_flow_from_json"]
|
||||
__all__ = ["load_flow_from_json", "run_flow_from_json", "upload_file", "get_flow"]
|
||||
|
|
|
|||
89
src/backend/base/langflow/load/utils.py
Normal file
89
src/backend/base/langflow/load/utils.py
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import httpx
|
||||
|
||||
from langflow.services.database.models.flow.model import FlowBase
|
||||
|
||||
|
||||
def upload(file_path, host, flow_id):
|
||||
"""
|
||||
Upload a file to Langflow and return the file path.
|
||||
|
||||
Args:
|
||||
file_path (str): The path to the file to be uploaded.
|
||||
host (str): The host URL of Langflow.
|
||||
flow_id (UUID): The ID of the flow to which the file belongs.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the file path.
|
||||
|
||||
Raises:
|
||||
Exception: If an error occurs during the upload process.
|
||||
"""
|
||||
try:
|
||||
url = f"{host}/api/v1/upload/{flow_id}"
|
||||
response = httpx.post(url, files={"file": open(file_path, "rb")})
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
raise Exception(f"Error uploading file: {response.status_code}")
|
||||
except Exception as e:
|
||||
raise Exception(f"Error uploading file: {e}")
|
||||
|
||||
|
||||
def upload_file(file_path, host, flow_id, components, tweaks={}):
|
||||
"""
|
||||
Upload a file to Langflow and return the file path.
|
||||
|
||||
Args:
|
||||
file_path (str): The path to the file to be uploaded.
|
||||
host (str): The host URL of Langflow.
|
||||
port (int): The port number of Langflow.
|
||||
flow_id (UUID): The ID of the flow to which the file belongs.
|
||||
components (str): List of component IDs or names that need the file.
|
||||
tweaks (dict): A dictionary of tweaks to be applied to the file.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the file path and any tweaks that were applied.
|
||||
|
||||
Raises:
|
||||
Exception: If an error occurs during the upload process.
|
||||
"""
|
||||
try:
|
||||
response = upload(file_path, host, flow_id)
|
||||
if response["file_path"]:
|
||||
for component in components:
|
||||
if isinstance(component, str):
|
||||
tweaks[component] = {"file_path": response["file_path"]}
|
||||
else:
|
||||
raise ValueError(f"Component ID or name must be a string. Got {type(component)}")
|
||||
return tweaks
|
||||
else:
|
||||
raise ValueError("Error uploading file")
|
||||
except Exception as e:
|
||||
raise ValueError(f"Error uploading file: {e}")
|
||||
|
||||
|
||||
def get_flow(url: str, flow_id: str):
|
||||
"""Get the details of a flow from Langflow.
|
||||
|
||||
Args:
|
||||
url (str): The host URL of Langflow.
|
||||
port (int): The port number of Langflow.
|
||||
flow_id (UUID): The ID of the flow to retrieve.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the details of the flow.
|
||||
|
||||
Raises:
|
||||
Exception: If an error occurs during the retrieval process.
|
||||
"""
|
||||
try:
|
||||
flow_url = f"{url}/api/v1/flows/{flow_id}"
|
||||
response = httpx.get(flow_url)
|
||||
if response.status_code == 200:
|
||||
json_response = response.json()
|
||||
flow = FlowBase(**json_response).model_dump()
|
||||
return flow
|
||||
else:
|
||||
raise Exception(f"Error retrieving flow: {response.status_code}")
|
||||
except Exception as e:
|
||||
raise Exception(f"Error retrieving flow: {e}")
|
||||
|
|
@ -29,7 +29,6 @@ class FlowBase(SQLModel):
|
|||
is_component: Optional[bool] = Field(default=False, nullable=True)
|
||||
updated_at: Optional[datetime] = Field(default_factory=lambda: datetime.now(timezone.utc), nullable=True)
|
||||
webhook: Optional[bool] = Field(default=False, nullable=True, description="Can be used on the webhook endpoint")
|
||||
folder_id: Optional[UUID] = Field(default=None, nullable=True)
|
||||
endpoint_name: Optional[str] = Field(default=None, nullable=True, index=True)
|
||||
|
||||
@field_validator("endpoint_name")
|
||||
|
|
|
|||
|
|
@ -9,19 +9,35 @@ export default function getPythonApiCode(
|
|||
flowId: string,
|
||||
isAuth: boolean,
|
||||
tweaksBuildedObject,
|
||||
endpointName?: string
|
||||
): string {
|
||||
const tweaksObject = tweaksBuildedObject[0];
|
||||
return `import requests
|
||||
return `import argparse
|
||||
import json
|
||||
from argparse import RawTextHelpFormatter
|
||||
import requests
|
||||
from typing import Optional
|
||||
import warnings
|
||||
try:
|
||||
from langflow.load import upload_file
|
||||
except ImportError:
|
||||
warnings.warn("Langflow provides a function to help you upload files to the flow. Please install langflow to use it.")
|
||||
upload_file = None
|
||||
|
||||
BASE_API_URL = "${window.location.protocol}//${window.location.host}/api/v1/run"
|
||||
FLOW_ID = "${flowId}"
|
||||
ENDPOINT = "${endpointName || ""}" ${
|
||||
endpointName
|
||||
? `# The endpoint name of the flow`
|
||||
: `# You can set a specific endpoint name in the flow settings`
|
||||
}
|
||||
|
||||
# You can tweak the flow by adding a tweaks dictionary
|
||||
# e.g {"OpenAI-XXXXX": {"model_name": "gpt-4"}}
|
||||
TWEAKS = ${JSON.stringify(tweaksObject, null, 2)}
|
||||
|
||||
def run_flow(message: str,
|
||||
flow_id: str,
|
||||
endpoint: str,
|
||||
output_type: str = "chat",
|
||||
input_type: str = "chat",
|
||||
tweaks: Optional[dict] = None,
|
||||
|
|
@ -30,11 +46,11 @@ def run_flow(message: str,
|
|||
Run a flow with a given message and optional tweaks.
|
||||
|
||||
:param message: The message to send to the flow
|
||||
:param flow_id: The ID of the flow to run
|
||||
:param endpoint: The ID or the endpoint name of the flow
|
||||
:param tweaks: Optional tweaks to customize the flow
|
||||
:return: The JSON response from the flow
|
||||
"""
|
||||
api_url = f"{BASE_API_URL}/{flow_id}"
|
||||
api_url = f"{BASE_API_URL}/{endpoint}"
|
||||
|
||||
payload = {
|
||||
"input_value": message,
|
||||
|
|
@ -49,10 +65,43 @@ def run_flow(message: str,
|
|||
response = requests.post(api_url, json=payload, headers=headers)
|
||||
return response.json()
|
||||
|
||||
# Setup any tweaks you want to apply to the flow
|
||||
message = "message"
|
||||
${!isAuth ? `api_key = "<your api key>"` : ""}
|
||||
print(run_flow(message=message, flow_id=FLOW_ID, tweaks=TWEAKS${
|
||||
!isAuth ? `, api_key=api_key` : ""
|
||||
}))`;
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="""Run a flow with a given message and optional tweaks.\nRun it like: python <your file>.py "your message here" --endpoint "your_endpoint" --tweaks '{"key": "value"}'""",
|
||||
formatter_class=RawTextHelpFormatter)
|
||||
parser.add_argument("message", type=str, help="The message to send to the flow")
|
||||
parser.add_argument("--endpoint", type=str, default=ENDPOINT or FLOW_ID, help="The ID or the endpoint name of the flow")
|
||||
parser.add_argument("--tweaks", type=str, help="JSON string representing the tweaks to customize the flow", default=json.dumps(TWEAKS))
|
||||
parser.add_argument("--api_key", type=str, help="API key for authentication", default=None)
|
||||
parser.add_argument("--output_type", type=str, default="chat", help="The output type")
|
||||
parser.add_argument("--input_type", type=str, default="chat", help="The input type")
|
||||
parser.add_argument("--upload_file", type=str, help="Path to the file to upload", default=None)
|
||||
parser.add_argument("--components", type=str, help="Components to upload the file to", default=None)
|
||||
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
tweaks = json.loads(args.tweaks)
|
||||
except json.JSONDecodeError:
|
||||
raise ValueError("Invalid tweaks JSON string")
|
||||
|
||||
if args.upload_file:
|
||||
if not upload_file:
|
||||
raise ImportError("Langflow is not installed. Please install it to use the upload_file function.")
|
||||
elif not args.components:
|
||||
raise ValueError("You need to provide the components to upload the file to.")
|
||||
tweaks = upload_file(file_path=args.upload_file, host=BASE_API_URL, flow_id=ENDPOINT, components=args.components, tweaks=tweaks)
|
||||
|
||||
response = run_flow(
|
||||
message=args.message,
|
||||
endpoint=args.endpoint,
|
||||
output_type=args.output_type,
|
||||
input_type=args.input_type,
|
||||
tweaks=tweaks,
|
||||
api_key=args.api_key
|
||||
)
|
||||
|
||||
print(json.dumps(response, indent=2))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const ApiModal = forwardRef(
|
|||
flow: FlowType;
|
||||
children: ReactNode;
|
||||
},
|
||||
ref,
|
||||
ref
|
||||
) => {
|
||||
const tweak = useTweaksStore((state) => state.tweak);
|
||||
const addTweaks = useTweaksStore((state) => state.setTweak);
|
||||
|
|
@ -46,17 +46,22 @@ const ApiModal = forwardRef(
|
|||
const { autoLogin } = useContext(AuthContext);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState("0");
|
||||
const pythonApiCode = getPythonApiCode(flow?.id, autoLogin, tweak);
|
||||
const pythonApiCode = getPythonApiCode(
|
||||
flow?.id,
|
||||
autoLogin,
|
||||
tweak,
|
||||
flow?.endpoint_name
|
||||
);
|
||||
const curl_run_code = getCurlRunCode(
|
||||
flow?.id,
|
||||
autoLogin,
|
||||
tweak,
|
||||
flow?.endpoint_name,
|
||||
flow?.endpoint_name
|
||||
);
|
||||
const curl_webhook_code = getCurlWebhookCode(
|
||||
flow?.id,
|
||||
autoLogin,
|
||||
flow?.endpoint_name,
|
||||
flow?.endpoint_name
|
||||
);
|
||||
const pythonCode = getPythonCode(flow?.name, tweak);
|
||||
const widgetCode = getWidgetCode(flow?.id, flow?.name, autoLogin);
|
||||
|
|
@ -72,7 +77,7 @@ const ApiModal = forwardRef(
|
|||
pythonCode,
|
||||
];
|
||||
const [tabs, setTabs] = useState(
|
||||
createTabsArray(codesArray, includeWebhook),
|
||||
createTabsArray(codesArray, includeWebhook)
|
||||
);
|
||||
|
||||
const canShowTweaks =
|
||||
|
|
@ -121,7 +126,7 @@ const ApiModal = forwardRef(
|
|||
buildTweakObject(
|
||||
nodeId,
|
||||
element.data.node.template[templateField].value,
|
||||
element.data.node.template[templateField],
|
||||
element.data.node.template[templateField]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -138,7 +143,7 @@ const ApiModal = forwardRef(
|
|||
async function buildTweakObject(
|
||||
tw: string,
|
||||
changes: string | string[] | boolean | number | Object[] | Object,
|
||||
template: TemplateVariableType,
|
||||
template: TemplateVariableType
|
||||
) {
|
||||
changes = getChangesType(changes, template);
|
||||
|
||||
|
|
@ -180,7 +185,7 @@ const ApiModal = forwardRef(
|
|||
flow?.id,
|
||||
autoLogin,
|
||||
cloneTweak,
|
||||
flow?.endpoint_name,
|
||||
flow?.endpoint_name
|
||||
);
|
||||
const pythonCode = getPythonCode(flow?.name, cloneTweak);
|
||||
const widgetCode = getWidgetCode(flow?.id, flow?.name, autoLogin);
|
||||
|
|
@ -224,7 +229,7 @@ const ApiModal = forwardRef(
|
|||
</BaseModal.Content>
|
||||
</BaseModal>
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default ApiModal;
|
||||
|
|
|
|||
|
|
@ -498,16 +498,6 @@ export default function NodeToolbarComponent({
|
|||
/>
|
||||
</SelectItem>
|
||||
)}
|
||||
{/* <SelectItem value={"duplicate"}>
|
||||
<ToolbarSelectItem
|
||||
keyboardKey="D"
|
||||
isMac={navigator.userAgent.toUpperCase().includes("MAC")}
|
||||
shift={false}
|
||||
value={"Duplicate"}
|
||||
icon={"Copy"}
|
||||
dataTestId="duplicate-button-modal"
|
||||
/>
|
||||
</SelectItem> */}
|
||||
<SelectItem value={"copy"}>
|
||||
<ToolbarSelectItem
|
||||
keyboardKey="C"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue