* Update styleUtils.ts * update to prompt component * update to template * update to mcp component * update to smart function * [autofix.ci] apply automated fixes * update to templates * fix sidebar * change name * update import * update import * update import * [autofix.ci] apply automated fixes * fix import * fix ollama * fix ruff * refactor(agent): standardize memory handling and update chat history logic (#8715) * update chat history * update to agents * Update Simple Agent.json * update to templates * ruff errors * Update agent.py * Update test_agent_component.py * [autofix.ci] apply automated fixes * update templates * test fix --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Mike Fortman <michael.fortman@datastax.com> * fix prompt change * feat(message): support sequencing of multiple streamable models (#8434) * feat: update OpenAI model parameters handling for reasoning models * feat: extend input_value type in LCModelComponent to support AsyncIterator and Iterator * refactor: remove assert_streaming_sequence method and related checks from Graph class * feat: add consume_iterator method to Message class for handling iterators * test: add unit tests for OpenAIModelComponent functionality and integration * feat: update OpenAIModelComponent to include temperature and seed parameters in build_model method * feat: rename consume_iterator method to consume_iterator_in_text and update its implementation for handling text * feat: add is_connected_to_chat_output method to Component class for improved message handling * feat: refactor LCModelComponent methods to support asynchronous message handling and improve chat output integration * refactor: remove consume_iterator_in_text method from Message class and clean up LCModelComponent input handling * fix: update import paths for input components in multiple starter project JSON files * fix: enhance error message formatting in ErrorMessage class to handle additional exception attributes * refactor: remove validate_stream calls from generate_flow_events and Graph class to streamline flow processing * fix: handle asyncio.CancelledError in aadd_messagetables to ensure proper session rollback and retry logic * refactor: streamline message handling in LCModelComponent by replacing async invocation with synchronous calls and updating message text handling * refactor: enhance message handling in LCModelComponent by introducing lf_message for improved return value management and updating properties for consistency * feat: add _build_source method to Component class for enhanced source handling and flexibility in source object management * feat: enhance LCModelComponent by adding _handle_stream method for improved streaming response handling and refactoring chat output integration * feat: update MemoryComponent to enhance message retrieval and storage functionality, including new sender type handling and output options for text and dataframe formats * test: refactor LanguageModelComponent tests to use ComponentTestBaseWithoutClient and add tests for Google model creation and error handling * test: add fixtures for API keys and implement live API tests for OpenAI, Anthropic, and Google models * fix: reorder JSON properties for consistency in starter projects * Updated JSON files for various starter projects to ensure consistent ordering of properties, specifically moving "type" to follow "selected_output" for better readability and maintainability. * Affected files: Basic Prompt Chaining.json, Blog Writer.json, Financial Report Parser.json, Hybrid Search RAG.json, SEO Keyword Generator.json. * refactor: simplify input_value type in LCModelComponent * Updated the input_value parameter in LCModelComponent to remove AsyncIterator and Iterator types, streamlining the input options to only str and Message for improved clarity and maintainability. * This change enhances the documentation and understanding of the expected input types for the component. * fix: clarify comment for handling source in Component class * refactor: remove unnecessary mocking in OpenAI model integration tests * auto update * update * [autofix.ci] apply automated fixes * fix openai import * revert template changes * test fixes * update templates * [autofix.ci] apply automated fixes * fix tests * fix order * fix prompts import * fix frontend tests * fix frontend * [autofix.ci] apply automated fixes * add charmander * [autofix.ci] apply automated fixes * fix prompt frontend * fix frontend * test fix * [autofix.ci] apply automated fixes * change pokedex * remove pokedex extra * update template * name fix * update template * mcp test fix --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: cristhianzl <cristhian.lousa@gmail.com> Co-authored-by: Yuqi Tang <yuqi.tang@datastax.com> Co-authored-by: Mike Fortman <michael.fortman@datastax.com> Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
167 lines
5.3 KiB
Python
167 lines
5.3 KiB
Python
import os
|
|
import sqlite3
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from langflow.base.tools.component_tool import ComponentToolkit
|
|
from langflow.components.data.sql_executor import SQLComponent
|
|
from langflow.components.input_output.chat_output import ChatOutput
|
|
from langflow.components.langchain_utilities import ToolCallingAgentComponent
|
|
from langflow.components.openai.openai_chat_model import OpenAIModelComponent
|
|
from langflow.components.tools.calculator import CalculatorToolComponent
|
|
from langflow.graph.graph.base import Graph
|
|
from pydantic import BaseModel
|
|
|
|
|
|
@pytest.fixture
|
|
def test_db():
|
|
"""Fixture that creates a temporary SQLite database for testing."""
|
|
test_data_dir = Path(__file__).parent.parent.parent.parent / "data"
|
|
db_path = test_data_dir / "test.db"
|
|
conn = sqlite3.connect(db_path)
|
|
cursor = conn.cursor()
|
|
# Create students table
|
|
cursor.execute("""
|
|
CREATE TABLE students (
|
|
id INTEGER PRIMARY KEY,
|
|
first_name TEXT NOT NULL,
|
|
last_name TEXT NOT NULL,
|
|
age INTEGER,
|
|
gpa REAL,
|
|
major TEXT
|
|
)
|
|
""")
|
|
|
|
# Create courses table
|
|
cursor.execute("""
|
|
CREATE TABLE courses (
|
|
id INTEGER PRIMARY KEY,
|
|
course_name TEXT NOT NULL,
|
|
instructor TEXT,
|
|
credits INTEGER
|
|
)
|
|
""")
|
|
|
|
# Create enrollment junction table
|
|
cursor.execute("""
|
|
CREATE TABLE enrollments (
|
|
student_id INTEGER,
|
|
course_id INTEGER,
|
|
grade TEXT,
|
|
PRIMARY KEY (student_id, course_id),
|
|
FOREIGN KEY (student_id) REFERENCES students (id),
|
|
FOREIGN KEY (course_id) REFERENCES courses (id)
|
|
)
|
|
""")
|
|
|
|
# Insert sample student data
|
|
students = [
|
|
(1, "John", "Smith", 20, 3.5, "Computer Science"),
|
|
(2, "Emma", "Johnson", 21, 3.8, "Mathematics"),
|
|
(3, "Michael", "Williams", 19, 3.2, "Physics"),
|
|
(4, "Olivia", "Brown", 22, 3.9, "Biology"),
|
|
(5, "James", "Davis", 20, 3.1, "Chemistry"),
|
|
]
|
|
|
|
cursor.executemany("INSERT INTO students VALUES (?, ?, ?, ?, ?, ?)", students)
|
|
|
|
# Insert sample course data
|
|
courses = [
|
|
(101, "Introduction to Programming", "Dr. Jones", 3),
|
|
(102, "Calculus I", "Dr. Smith", 4),
|
|
(103, "Physics 101", "Dr. Brown", 4),
|
|
(104, "Biology Fundamentals", "Dr. Wilson", 3),
|
|
(105, "Chemistry Basics", "Dr. Miller", 3),
|
|
]
|
|
|
|
cursor.executemany("INSERT INTO courses VALUES (?, ?, ?, ?)", courses)
|
|
|
|
# Insert sample enrollment data
|
|
enrollments = [
|
|
(1, 101, "A"),
|
|
(1, 102, "B+"),
|
|
(2, 102, "A"),
|
|
(2, 103, "A-"),
|
|
(3, 103, "B"),
|
|
(3, 105, "C+"),
|
|
(4, 104, "A"),
|
|
(5, 105, "B+"),
|
|
]
|
|
|
|
cursor.executemany("INSERT INTO enrollments VALUES (?, ?, ?)", enrollments)
|
|
|
|
# Commit changes and close connection
|
|
conn.commit()
|
|
conn.close()
|
|
yield str(db_path)
|
|
|
|
Path(db_path).unlink()
|
|
|
|
|
|
def test_component_tool():
|
|
calculator_component = CalculatorToolComponent()
|
|
component_toolkit = ComponentToolkit(component=calculator_component)
|
|
component_tool = component_toolkit.get_tools()[0]
|
|
assert component_tool.name == "run_model"
|
|
assert issubclass(component_tool.args_schema, BaseModel)
|
|
# TODO: fix this
|
|
# assert component_tool.args_schema.model_json_schema()["properties"] == {
|
|
# "input_value": {
|
|
# "default": "",
|
|
# "description": "Message to be passed as input.",
|
|
# "title": "Input Value",
|
|
# "type": "string",
|
|
# },
|
|
# }
|
|
assert component_toolkit.component == calculator_component
|
|
|
|
result = component_tool.invoke(input={"expression": "1+1"})
|
|
assert isinstance(result[0], dict)
|
|
assert "result" in result[0]["data"]
|
|
assert result[0]["data"]["result"] == "2"
|
|
|
|
|
|
@pytest.mark.api_key_required
|
|
@pytest.mark.usefixtures("client")
|
|
async def test_component_tool_with_api_key():
|
|
chat_output = ChatOutput()
|
|
openai_llm = OpenAIModelComponent()
|
|
openai_llm.set(api_key=os.environ["OPENAI_API_KEY"])
|
|
tool_calling_agent = ToolCallingAgentComponent()
|
|
tools = await chat_output.to_toolkit()
|
|
tool_calling_agent.set(
|
|
llm=openai_llm.build_model,
|
|
tools=list(tools),
|
|
input_value="Which tools are available? Please tell its name.",
|
|
)
|
|
|
|
g = Graph(start=tool_calling_agent, end=tool_calling_agent)
|
|
g.session_id = "test"
|
|
assert g is not None
|
|
results = [result async for result in g.async_start()]
|
|
assert len(results) == 3
|
|
assert "message_response" in tool_calling_agent._outputs_map["response"].value.get_text()
|
|
|
|
|
|
@pytest.mark.api_key_required
|
|
@pytest.mark.usefixtures("client")
|
|
async def test_sql_component_to_toolkit(test_db):
|
|
sql_component = SQLComponent()
|
|
sql_component.set(database_url=f"sqlite:///{test_db}")
|
|
tool = await sql_component.to_toolkit()
|
|
openai_llm = OpenAIModelComponent()
|
|
openai_llm.set(api_key=os.environ["OPENAI_API_KEY"])
|
|
tool_calling_agent = ToolCallingAgentComponent()
|
|
|
|
tool_calling_agent.set(
|
|
llm=openai_llm.build_model,
|
|
tools=list(tool),
|
|
input_value="run SELECT * FROM courses to get course details.",
|
|
)
|
|
|
|
g = Graph(start=tool_calling_agent, end=tool_calling_agent)
|
|
g.session_id = "test"
|
|
assert g is not None
|
|
results = [result async for result in g.async_start()]
|
|
assert len(results) > 0
|
|
assert "Physics 101" in tool_calling_agent._outputs_map["response"].value.get_text()
|