🚀 feat(llms.py): add method to format ctransformers field in LLMFrontendNode (#519)
This commit is contained in:
commit
b276cc9dd6
7 changed files with 70 additions and 24 deletions
|
|
@ -1,12 +1,6 @@
|
||||||
from fastapi import (
|
from fastapi import APIRouter, HTTPException, WebSocket, WebSocketException, status
|
||||||
APIRouter,
|
|
||||||
HTTPException,
|
|
||||||
WebSocket,
|
|
||||||
WebSocketException,
|
|
||||||
status,
|
|
||||||
)
|
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
from langflow.api.v1.schemas import BuiltResponse, InitResponse, StreamData
|
from langflow.api.v1.schemas import BuildStatus, BuiltResponse, InitResponse, StreamData
|
||||||
|
|
||||||
from langflow.chat.manager import ChatManager
|
from langflow.chat.manager import ChatManager
|
||||||
from langflow.graph.graph.base import Graph
|
from langflow.graph.graph.base import Graph
|
||||||
|
|
@ -32,15 +26,29 @@ async def chat(client_id: str, websocket: WebSocket):
|
||||||
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc))
|
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc))
|
||||||
|
|
||||||
|
|
||||||
@router.post("/build/init", response_model=InitResponse, status_code=201)
|
@router.post("/build/init/{flow_id}", response_model=InitResponse, status_code=201)
|
||||||
async def init_build(graph_data: dict):
|
async def init_build(graph_data: dict, flow_id: str):
|
||||||
"""Initialize the build by storing graph data and returning a unique session ID."""
|
"""Initialize the build by storing graph data and returning a unique session ID."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
flow_id = graph_data.get("id")
|
|
||||||
if flow_id is None:
|
if flow_id is None:
|
||||||
raise ValueError("No ID provided")
|
raise ValueError("No ID provided")
|
||||||
flow_data_store[flow_id] = graph_data
|
# Check if already building
|
||||||
|
if (
|
||||||
|
flow_id in flow_data_store
|
||||||
|
and flow_data_store[flow_id]["status"] == BuildStatus.IN_PROGRESS
|
||||||
|
):
|
||||||
|
return InitResponse(flowId=flow_id)
|
||||||
|
|
||||||
|
# Delete from cache if already exists
|
||||||
|
if flow_id in chat_manager.in_memory_cache:
|
||||||
|
with chat_manager.in_memory_cache._lock:
|
||||||
|
chat_manager.in_memory_cache.delete(flow_id)
|
||||||
|
logger.debug(f"Deleted flow {flow_id} from cache")
|
||||||
|
flow_data_store[flow_id] = {
|
||||||
|
"graph_data": graph_data,
|
||||||
|
"status": BuildStatus.IN_PROGRESS,
|
||||||
|
}
|
||||||
|
|
||||||
return InitResponse(flowId=flow_id)
|
return InitResponse(flowId=flow_id)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
|
@ -52,8 +60,9 @@ async def init_build(graph_data: dict):
|
||||||
async def build_status(flow_id: str):
|
async def build_status(flow_id: str):
|
||||||
"""Check the flow_id is in the flow_data_store."""
|
"""Check the flow_id is in the flow_data_store."""
|
||||||
try:
|
try:
|
||||||
built = flow_id in flow_data_store and not isinstance(
|
built = (
|
||||||
flow_data_store[flow_id], dict
|
flow_id in flow_data_store
|
||||||
|
and flow_data_store[flow_id]["status"] == BuildStatus.SUCCESS
|
||||||
)
|
)
|
||||||
|
|
||||||
return BuiltResponse(
|
return BuiltResponse(
|
||||||
|
|
@ -77,6 +86,11 @@ async def stream_build(flow_id: str):
|
||||||
yield str(StreamData(event="error", data={"error": error_message}))
|
yield str(StreamData(event="error", data={"error": error_message}))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if flow_data_store[flow_id].get("status") == BuildStatus.IN_PROGRESS:
|
||||||
|
error_message = "Already building"
|
||||||
|
yield str(StreamData(event="error", data={"error": error_message}))
|
||||||
|
return
|
||||||
|
|
||||||
graph_data = flow_data_store[flow_id].get("data")
|
graph_data = flow_data_store[flow_id].get("data")
|
||||||
|
|
||||||
if not graph_data:
|
if not graph_data:
|
||||||
|
|
@ -110,6 +124,7 @@ async def stream_build(flow_id: str):
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
params = str(exc)
|
params = str(exc)
|
||||||
valid = False
|
valid = False
|
||||||
|
flow_data_store[flow_id]["status"] = BuildStatus.FAILURE
|
||||||
|
|
||||||
response = {
|
response = {
|
||||||
"valid": valid,
|
"valid": valid,
|
||||||
|
|
@ -121,8 +136,10 @@ async def stream_build(flow_id: str):
|
||||||
yield str(StreamData(event="message", data=response))
|
yield str(StreamData(event="message", data=response))
|
||||||
|
|
||||||
chat_manager.set_cache(flow_id, graph.build())
|
chat_manager.set_cache(flow_id, graph.build())
|
||||||
|
flow_data_store[flow_id]["status"] = BuildStatus.SUCCESS
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error("Error while building the flow: %s", exc)
|
logger.error("Error while building the flow: %s", exc)
|
||||||
|
flow_data_store[flow_id]["status"] = BuildStatus.FAILURE
|
||||||
yield str(StreamData(event="error", data={"error": str(exc)}))
|
yield str(StreamData(event="error", data={"error": str(exc)}))
|
||||||
finally:
|
finally:
|
||||||
yield str(StreamData(event="message", data=final_response))
|
yield str(StreamData(event="message", data=final_response))
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Union
|
from typing import Any, Dict, List, Optional, Union
|
||||||
from langflow.database.models.flow import FlowCreate, FlowRead
|
from langflow.database.models.flow import FlowCreate, FlowRead
|
||||||
|
|
@ -5,6 +6,14 @@ from pydantic import BaseModel, Field, validator
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
class BuildStatus(Enum):
|
||||||
|
"""Status of the build."""
|
||||||
|
|
||||||
|
SUCCESS = "success"
|
||||||
|
FAILURE = "failure"
|
||||||
|
IN_PROGRESS = "in_progress"
|
||||||
|
|
||||||
|
|
||||||
class GraphData(BaseModel):
|
class GraphData(BaseModel):
|
||||||
"""Data inside the exported flow."""
|
"""Data inside the exported flow."""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ from langchain.chains.base import Chain
|
||||||
from langchain.vectorstores.base import VectorStore
|
from langchain.vectorstores.base import VectorStore
|
||||||
from langchain.document_loaders.base import BaseLoader
|
from langchain.document_loaders.base import BaseLoader
|
||||||
from langchain.prompts.base import BasePromptTemplate
|
from langchain.prompts.base import BasePromptTemplate
|
||||||
from langflow.chat.config import ChatConfig
|
|
||||||
|
|
||||||
|
|
||||||
def instantiate_class(node_type: str, base_type: str, params: Dict) -> Any:
|
def instantiate_class(node_type: str, base_type: str, params: Dict) -> Any:
|
||||||
|
|
@ -49,8 +48,8 @@ def convert_params_to_sets(params):
|
||||||
|
|
||||||
def convert_kwargs(params):
|
def convert_kwargs(params):
|
||||||
# if *kwargs are passed as a string, convert to dict
|
# if *kwargs are passed as a string, convert to dict
|
||||||
# first find any key that has kwargs in it
|
# first find any key that has kwargs or config in it
|
||||||
kwargs_keys = [key for key in params.keys() if "kwargs" in key]
|
kwargs_keys = [key for key in params.keys() if "kwargs" in key or "config" in key]
|
||||||
for key in kwargs_keys:
|
for key in kwargs_keys:
|
||||||
if isinstance(params[key], str):
|
if isinstance(params[key], str):
|
||||||
params[key] = json.loads(params[key])
|
params[key] = json.loads(params[key])
|
||||||
|
|
@ -85,11 +84,6 @@ def instantiate_based_on_type(class_object, base_type, node_type, params):
|
||||||
|
|
||||||
|
|
||||||
def instantiate_llm(node_type, class_object, params: Dict):
|
def instantiate_llm(node_type, class_object, params: Dict):
|
||||||
# This is a workaround so JinaChat works until streaming is implemented
|
|
||||||
# if "openai_api_base" in params and "jina" in params["openai_api_base"]:
|
|
||||||
# False if condition is True
|
|
||||||
ChatConfig.streaming = "jina" not in params.get("openai_api_base", "")
|
|
||||||
|
|
||||||
return class_object(**params)
|
return class_object(**params)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,22 @@ HUMAN_PROMPT = "{input}"
|
||||||
|
|
||||||
QA_CHAIN_TYPES = ["stuff", "map_reduce", "map_rerank", "refine"]
|
QA_CHAIN_TYPES = ["stuff", "map_reduce", "map_rerank", "refine"]
|
||||||
|
|
||||||
|
CTRANSFORMERS_DEFAULT_CONFIG = {
|
||||||
|
"top_k": 40,
|
||||||
|
"top_p": 0.95,
|
||||||
|
"temperature": 0.8,
|
||||||
|
"repetition_penalty": 1.1,
|
||||||
|
"last_n_tokens": 64,
|
||||||
|
"seed": -1,
|
||||||
|
"max_new_tokens": 256,
|
||||||
|
"stop": None,
|
||||||
|
"stream": False,
|
||||||
|
"reset": True,
|
||||||
|
"batch_size": 8,
|
||||||
|
"threads": -1,
|
||||||
|
"context_length": -1,
|
||||||
|
"gpu_layers": 0,
|
||||||
|
}
|
||||||
|
|
||||||
# This variable is used to tell the user
|
# This variable is used to tell the user
|
||||||
# that it can be changed to use other APIs
|
# that it can be changed to use other APIs
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
import json
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from langflow.template.field.base import TemplateField
|
from langflow.template.field.base import TemplateField
|
||||||
from langflow.template.frontend_node.base import FrontendNode
|
from langflow.template.frontend_node.base import FrontendNode
|
||||||
|
from langflow.template.frontend_node.constants import CTRANSFORMERS_DEFAULT_CONFIG
|
||||||
from langflow.template.frontend_node.constants import OPENAI_API_BASE_INFO
|
from langflow.template.frontend_node.constants import OPENAI_API_BASE_INFO
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -35,6 +37,13 @@ class LLMFrontendNode(FrontendNode):
|
||||||
field.show = True
|
field.show = True
|
||||||
field.advanced = not field.required
|
field.advanced = not field.required
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format_ctransformers_field(field: TemplateField):
|
||||||
|
if field.name == "config":
|
||||||
|
field.show = True
|
||||||
|
field.advanced = True
|
||||||
|
field.value = json.dumps(CTRANSFORMERS_DEFAULT_CONFIG, indent=2)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||||
display_names_dict = {
|
display_names_dict = {
|
||||||
|
|
@ -42,6 +51,7 @@ class LLMFrontendNode(FrontendNode):
|
||||||
}
|
}
|
||||||
FrontendNode.format_field(field, name)
|
FrontendNode.format_field(field, name)
|
||||||
LLMFrontendNode.format_openai_field(field)
|
LLMFrontendNode.format_openai_field(field)
|
||||||
|
LLMFrontendNode.format_ctransformers_field(field)
|
||||||
if name and "azure" in name.lower():
|
if name and "azure" in name.lower():
|
||||||
LLMFrontendNode.format_azure_field(field)
|
LLMFrontendNode.format_azure_field(field)
|
||||||
if name and "llama" in name.lower():
|
if name and "llama" in name.lower():
|
||||||
|
|
|
||||||
|
|
@ -311,7 +311,7 @@ export async function getBuildStatus(
|
||||||
export async function postBuildInit(
|
export async function postBuildInit(
|
||||||
flow: FlowType
|
flow: FlowType
|
||||||
): Promise<AxiosResponse<InitTypeAPI>> {
|
): Promise<AxiosResponse<InitTypeAPI>> {
|
||||||
return await axios.post(`/api/v1/build/init`, flow);
|
return await axios.post(`/api/v1/build/init/${flow.id}`, flow);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch(`/upload/${id}`, {
|
// fetch(`/upload/${id}`, {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import pytest
|
||||||
|
|
||||||
def test_init_build(client):
|
def test_init_build(client):
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"api/v1/build/init", json={"id": "test", "data": {"key": "value"}}
|
"api/v1/build/init/test", json={"id": "test", "data": {"key": "value"}}
|
||||||
)
|
)
|
||||||
assert response.status_code == 201
|
assert response.status_code == 201
|
||||||
assert response.json() == {"flowId": "test"}
|
assert response.json() == {"flowId": "test"}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue