mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 00:03:32 +08:00
Merge release-1.11.0 (via feat/content-blocks-backend-v2) into feat/content-blocks-frontend-v2
This commit is contained in:
@ -1,268 +0,0 @@
|
||||
---
|
||||
description: "Guidelines for backend development in Langflow, focusing on Python components, FastAPI services, and backend testing."
|
||||
globs:
|
||||
- "src/backend/**/*.py"
|
||||
- "tests/**/*.py"
|
||||
- "Makefile"
|
||||
- "pyproject.toml"
|
||||
- "uv.lock"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
|
||||
# Backend Development Guidelines
|
||||
|
||||
## Purpose
|
||||
Guidelines for backend development in Langflow, focusing on Python components, FastAPI services, and backend testing.
|
||||
|
||||
---
|
||||
|
||||
## 1. Backend Environment Setup
|
||||
|
||||
### Prerequisites
|
||||
- **Python Package Manager:** `uv` (>=0.4) for dependency management
|
||||
- **Database:** SQLite for development, PostgreSQL for production
|
||||
- **Development Tools:** `make` for build coordination
|
||||
|
||||
### Backend Service
|
||||
```bash
|
||||
make backend # Start FastAPI backend on port 7860
|
||||
```
|
||||
- Auto-reloads on file changes
|
||||
- Health check: http://localhost:7860/health
|
||||
- Backend components: `src/backend/base/langflow/`
|
||||
|
||||
---
|
||||
|
||||
## 2. Component Development
|
||||
|
||||
### Component Structure
|
||||
```
|
||||
src/backend/base/langflow/components/
|
||||
├── agents/ # Agent components
|
||||
├── data/ # Data processing components
|
||||
├── embeddings/ # Embedding components
|
||||
├── input_output/ # Input/output components
|
||||
├── models/ # Language model components
|
||||
├── processing/ # Text processing components
|
||||
├── prompts/ # Prompt components
|
||||
├── tools/ # Tool components
|
||||
└── vectorstores/ # Vector store components
|
||||
```
|
||||
|
||||
### Adding New Components
|
||||
1. **Location:** Add to appropriate subdirectory under `src/backend/base/langflow/components/`
|
||||
2. **Import:** Update `__init__.py` with alphabetical imports:
|
||||
```python
|
||||
from .my_component import MyComponent
|
||||
|
||||
__all__ = [
|
||||
"ExistingComponent",
|
||||
"MyComponent", # Add alphabetically
|
||||
]
|
||||
```
|
||||
3. **Auto-restart:** Backend auto-restarts on save
|
||||
4. **Browser refresh:** Refresh browser to see component changes
|
||||
|
||||
### Component Testing
|
||||
- **Unit Tests:** `src/backend/tests/unit/components/`
|
||||
- **Test Structure:** Mirror component directory structure
|
||||
- **Test Base Classes:** Use `ComponentTestBaseWithClient` or `ComponentTestBaseWithoutClient`
|
||||
- **Version Testing:** Provide `file_names_mapping` for backward compatibility
|
||||
|
||||
### Development Tips
|
||||
- **Fast iteration:** Edit component in UI first, then save to source
|
||||
- **Component updates:** Old components show "Updates Available" after backend restart
|
||||
- **Testing:** Create comprehensive unit tests for all new components
|
||||
|
||||
---
|
||||
|
||||
## 3. Backend Code Quality
|
||||
|
||||
### Formatting (CRITICAL)
|
||||
```bash
|
||||
make format_backend # Format Python code
|
||||
```
|
||||
**Important:** Run `make format_backend` _early and often_ (ideally before running linting or committing changes). It auto-corrects the majority of style issues, preventing lengthy manual fixes when lint errors surface later.
|
||||
|
||||
### Linting
|
||||
```bash
|
||||
make lint # Run linting checks
|
||||
```
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
make unit_tests # Run backend unit tests
|
||||
```
|
||||
|
||||
### Pre-commit Workflow
|
||||
1. **Run `make format_backend`** (FIRST - saves time on lint fixes)
|
||||
2. Run `make lint`
|
||||
3. Run `make unit_tests`
|
||||
4. Commit changes
|
||||
|
||||
---
|
||||
|
||||
## 4. FastAPI Development
|
||||
|
||||
### API Structure
|
||||
```
|
||||
src/backend/base/langflow/api/
|
||||
├── v1/ # API version 1
|
||||
│ ├── chat.py # Chat endpoints
|
||||
│ ├── flows.py # Flow management
|
||||
│ ├── users.py # User management
|
||||
│ └── ...
|
||||
└── v2/ # API version 2 (future)
|
||||
```
|
||||
|
||||
### Testing APIs
|
||||
- Use `client` fixture from `conftest.py`
|
||||
- Test with `logged_in_headers` for authenticated endpoints
|
||||
- Example:
|
||||
```python
|
||||
async def test_flows_endpoint(client, logged_in_headers):
|
||||
response = await client.post(
|
||||
"api/v1/flows/",
|
||||
json=flow_data,
|
||||
headers=logged_in_headers
|
||||
)
|
||||
assert response.status_code == 201
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Database Development
|
||||
|
||||
### Models Location
|
||||
```
|
||||
src/backend/base/langflow/services/database/models/
|
||||
├── api_key/ # API key models
|
||||
├── flow/ # Flow models
|
||||
├── folder/ # Folder models
|
||||
├── user/ # User models
|
||||
└── ...
|
||||
```
|
||||
|
||||
### Database Testing
|
||||
- Use in-memory SQLite for tests
|
||||
- Database tests may fail in batch runs - run individually if needed:
|
||||
```bash
|
||||
uv run pytest src/backend/tests/unit/test_database.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Async Development Patterns
|
||||
|
||||
### Component Async Methods
|
||||
```python
|
||||
async def run(self) -> MessageType:
|
||||
"""Main component execution method."""
|
||||
# Use await for async operations
|
||||
result = await self.async_operation()
|
||||
return result
|
||||
|
||||
async def message_response(self) -> Message:
|
||||
"""Return a Message object for chat components."""
|
||||
return Message(
|
||||
text=self.input_value,
|
||||
sender=self.sender,
|
||||
session_id=self.session_id,
|
||||
)
|
||||
```
|
||||
|
||||
### Background Tasks
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
async def process_in_background(self):
|
||||
"""Process items without blocking."""
|
||||
# Use asyncio.create_task for background work
|
||||
task = asyncio.create_task(self.heavy_operation())
|
||||
|
||||
# Ensure proper cleanup
|
||||
try:
|
||||
result = await task
|
||||
return result
|
||||
except asyncio.CancelledError:
|
||||
# Handle cancellation gracefully
|
||||
await self.cleanup()
|
||||
raise
|
||||
```
|
||||
|
||||
### Queue Operations
|
||||
```python
|
||||
async def queue_processing(self):
|
||||
"""Non-blocking queue operations."""
|
||||
queue = asyncio.Queue()
|
||||
|
||||
# Non-blocking put
|
||||
queue.put_nowait(data)
|
||||
|
||||
# Timeout-controlled get
|
||||
try:
|
||||
result = await asyncio.wait_for(queue.get(), timeout=5.0)
|
||||
return result
|
||||
except asyncio.TimeoutError:
|
||||
# Handle timeout appropriately
|
||||
raise ComponentError("Processing timeout")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Component Integration Testing
|
||||
|
||||
### Flow Testing
|
||||
```python
|
||||
from tests.unit.build_utils import create_flow, build_flow, get_build_events
|
||||
|
||||
async def test_component_in_flow(client, json_flow, logged_in_headers):
|
||||
"""Test component within a complete flow."""
|
||||
flow_id = await create_flow(client, json_flow, logged_in_headers)
|
||||
build_response = await build_flow(client, flow_id, logged_in_headers)
|
||||
|
||||
# Validate flow execution
|
||||
job_id = build_response["job_id"]
|
||||
events_response = await get_build_events(client, job_id, logged_in_headers)
|
||||
assert events_response.status_code == 200
|
||||
```
|
||||
|
||||
### External API Testing
|
||||
```python
|
||||
@pytest.mark.api_key_required
|
||||
@pytest.mark.no_blockbuster
|
||||
async def test_with_real_api(self):
|
||||
"""Test component with external service."""
|
||||
api_key = os.getenv("OPENAI_API_KEY")
|
||||
component = MyComponent(api_key=api_key, model="gpt-4o")
|
||||
|
||||
response = await component.run()
|
||||
assert response is not None
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Known Backend Issues
|
||||
|
||||
### Testing Quirks
|
||||
- `test_database.py` may fail in batch runs but pass individually
|
||||
- Use `@pytest.mark.no_blockbuster` to skip blockbuster plugin when needed
|
||||
- Context variables may not propagate correctly in `asyncio.to_thread` - test both patterns
|
||||
|
||||
### File Changes
|
||||
- Starter project files auto-format after `langflow run`
|
||||
- These formatting changes can be committed or ignored
|
||||
|
||||
---
|
||||
|
||||
## Backend Development Checklist
|
||||
- [ ] Component added to appropriate subdirectory
|
||||
- [ ] `__init__.py` updated with alphabetical imports
|
||||
- [ ] Code formatted with `make format_backend` (FIRST)
|
||||
- [ ] Linting passed with `make lint`
|
||||
- [ ] Unit tests created and passing with `make unit_tests`
|
||||
- [ ] Component tested in UI with backend restart + browser refresh
|
||||
- [ ] Version mapping provided for backward compatibility
|
||||
- [ ] Async patterns implemented correctly with proper cleanup
|
||||
- [ ] External API calls use appropriate pytest markers
|
||||
@ -1,82 +0,0 @@
|
||||
---
|
||||
description: "Guide for creating a basic Langflow component, including requirements gathering, component structure, and implementation best practices."
|
||||
globs:
|
||||
- "src/backend/**/components/**/*.py"
|
||||
- "src/backend/base/langflow/components/**/*.py"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
|
||||
# Rule: How to Create a Basic Langflow Component
|
||||
|
||||
## Purpose
|
||||
Guide for Creating a Langflow Component
|
||||
|
||||
---
|
||||
|
||||
### 1. Gather Requirements
|
||||
|
||||
Ask the user for:
|
||||
- **Component Name:** What should the component be called?
|
||||
- **Description:** What does the component do?
|
||||
- **Inputs:** What are the required inputs? (e.g., text, dropdown, boolean, etc.)
|
||||
- **Outputs:** What should the component output? (e.g., a message, a value, etc.)
|
||||
- **Category:** Which component category should this component be stored under in `langflow/src/backend/base/langflow/components`
|
||||
|
||||
### 2. Define the Component
|
||||
|
||||
- Inherit from `Component`.
|
||||
- Set `display_name`, `description`, `icon`.
|
||||
- Define the `inputs` and `outputs` as lists of input/output field objects (e.g., `DropdownInput`, `MessageTextInput`, `Output`).
|
||||
- Implement the main logic as a method (e.g., `get_current_date`, `true_response`, etc.).
|
||||
|
||||
### 3. Example: Conditional If-Else Component
|
||||
|
||||
```python
|
||||
class ConditionalRouterComponent(Component):
|
||||
display_name = "If-Else"
|
||||
description = "Routes an input message to a corresponding output based on text comparison."
|
||||
icon = "split"
|
||||
name = "ConditionalRouter"
|
||||
inputs = [
|
||||
# Define your inputs here
|
||||
]
|
||||
outputs = [
|
||||
# Define your outputs here
|
||||
]
|
||||
# Implement your logic methods here
|
||||
```
|
||||
|
||||
### 4. Example: Current Date Component
|
||||
|
||||
```python
|
||||
class CurrentDateComponent(Component):
|
||||
display_name = "Current Date"
|
||||
description = "Returns the current date and time in the selected timezone."
|
||||
icon = "clock"
|
||||
name = "CurrentDate"
|
||||
inputs = [
|
||||
# Define your inputs here
|
||||
]
|
||||
outputs = [
|
||||
# Define your outputs here
|
||||
]
|
||||
# Implement your logic methods here
|
||||
```
|
||||
|
||||
### 5. Best Practices
|
||||
|
||||
- Use clear and descriptive names for inputs and outputs.
|
||||
- Provide helpful `info` for each input to guide users.
|
||||
- Handle errors gracefully and provide meaningful error messages.
|
||||
- Use appropriate icons to visually represent the component's function.
|
||||
- Use a Lucide icon or if you want a custom icon follow the icon rules (`./cursor/rules/icons.mdc`)
|
||||
|
||||
---
|
||||
|
||||
## Checklist for Creating a Component
|
||||
- [ ] Ask the user for component name, description, inputs, and outputs.
|
||||
- [ ] Define the component class with the required fields.
|
||||
- [ ] Implement the main logic.
|
||||
- [ ] Add helpful info and error handling.
|
||||
- [ ] Test the component.
|
||||
@ -1,374 +0,0 @@
|
||||
---
|
||||
description: "Guidelines for frontend development in Langflow, focusing on React/TypeScript UI components, build processes, and frontend testing."
|
||||
globs:
|
||||
- "src/frontend/**/*.{ts,tsx,js,jsx}"
|
||||
- "src/frontend/**/*.{css,scss,json}"
|
||||
- "src/frontend/package*.json"
|
||||
- "src/frontend/vite.config.*"
|
||||
- "src/frontend/tailwind.config.*"
|
||||
- "src/frontend/tsconfig.json"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
## 1. Frontend Environment Setup
|
||||
|
||||
### Prerequisites
|
||||
- **Node.js:** v22.12 LTS for JavaScript runtime
|
||||
- **Package Manager:** npm (v10.9) for dependency management
|
||||
- **Development Tools:** Vite for build tooling
|
||||
|
||||
### Frontend Service
|
||||
```bash
|
||||
make frontend # Start Vite dev server on port 3000
|
||||
```
|
||||
- Hot-reload enabled for UI changes
|
||||
- Access at: http://localhost:3000/
|
||||
- Frontend source: `src/frontend/`
|
||||
|
||||
---
|
||||
|
||||
## 2. Frontend Structure
|
||||
|
||||
### Directory Layout
|
||||
```
|
||||
src/frontend/src/
|
||||
├── components/ # Reusable UI components
|
||||
├── pages/ # Page-level components
|
||||
├── icons/ # Component icons and lazy loading
|
||||
├── stores/ # State management (Zustand)
|
||||
├── types/ # TypeScript type definitions
|
||||
├── utils/ # Utility functions
|
||||
├── hooks/ # Custom React hooks
|
||||
├── services/ # API service functions
|
||||
└── assets/ # Static assets
|
||||
```
|
||||
|
||||
### Key Technologies
|
||||
- **React 18** with TypeScript
|
||||
- **Vite** for build tooling and dev server
|
||||
- **Tailwind CSS** for styling
|
||||
- **Zustand** for state management
|
||||
- **React Flow** for flow graph visualization
|
||||
- **Lucide React** for icons
|
||||
|
||||
---
|
||||
|
||||
## 3. Frontend Code Quality
|
||||
|
||||
### Formatting
|
||||
```bash
|
||||
make format_frontend # Format TypeScript/JavaScript code
|
||||
```
|
||||
|
||||
### Linting
|
||||
```bash
|
||||
make lint # Run ESLint and TypeScript checks
|
||||
```
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
make tests_frontend # Run frontend tests (requires additional setup)
|
||||
```
|
||||
|
||||
### Pre-commit Workflow
|
||||
1. Run `make format_frontend`
|
||||
2. Run `make lint`
|
||||
3. Test changes in browser
|
||||
4. Commit changes
|
||||
|
||||
---
|
||||
|
||||
## 4. State Management
|
||||
|
||||
### Zustand Stores
|
||||
```typescript
|
||||
// stores/myStore.ts
|
||||
import { create } from 'zustand';
|
||||
|
||||
interface MyState {
|
||||
value: string;
|
||||
setValue: (value: string) => void;
|
||||
}
|
||||
|
||||
export const useMyStore = create<MyState>((set) => ({
|
||||
value: '',
|
||||
setValue: (value) => set({ value }),
|
||||
}));
|
||||
```
|
||||
|
||||
### Using Stores in Components
|
||||
```typescript
|
||||
// components/MyComponent.tsx
|
||||
import { useMyStore } from '@/stores/myStore';
|
||||
|
||||
export function MyComponent() {
|
||||
const { value, setValue } = useMyStore();
|
||||
|
||||
return (
|
||||
<input
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. API Integration
|
||||
|
||||
### Service Functions
|
||||
```typescript
|
||||
// services/api.ts
|
||||
import { api } from '@/controllers/API';
|
||||
|
||||
export async function createFlow(flowData: FlowData) {
|
||||
const response = await api.post('/flows/', flowData);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function getFlows() {
|
||||
const response = await api.get('/flows/');
|
||||
return response.data;
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
```typescript
|
||||
// hooks/useApi.ts
|
||||
import { useState, useCallback } from 'react';
|
||||
|
||||
export function useApi<T>(apiFunction: (...args: any[]) => Promise<T>) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const execute = useCallback(async (...args: any[]) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const result = await apiFunction(...args);
|
||||
return result;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Unknown error');
|
||||
throw err;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [apiFunction]);
|
||||
|
||||
return { execute, loading, error };
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. React Flow Integration
|
||||
|
||||
### Flow Graph Components
|
||||
```typescript
|
||||
// components/FlowGraph.tsx
|
||||
import ReactFlow, {
|
||||
Node,
|
||||
Edge,
|
||||
Controls,
|
||||
Background
|
||||
} from 'reactflow';
|
||||
|
||||
interface FlowGraphProps {
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
onNodesChange: (changes: NodeChange[]) => void;
|
||||
onEdgesChange: (changes: EdgeChange[]) => void;
|
||||
}
|
||||
|
||||
export function FlowGraph({
|
||||
nodes,
|
||||
edges,
|
||||
onNodesChange,
|
||||
onEdgesChange
|
||||
}: FlowGraphProps) {
|
||||
return (
|
||||
<div className="w-full h-full">
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
fitView
|
||||
>
|
||||
<Controls />
|
||||
<Background />
|
||||
</ReactFlow>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Node Types
|
||||
```typescript
|
||||
// components/nodes/ComponentNode.tsx
|
||||
import { memo } from 'react';
|
||||
import { Handle, Position } from 'reactflow';
|
||||
|
||||
interface ComponentNodeProps {
|
||||
data: {
|
||||
label: string;
|
||||
icon?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const ComponentNode = memo(({ data }: ComponentNodeProps) => {
|
||||
return (
|
||||
<div className="px-4 py-2 shadow-md rounded-md bg-white border">
|
||||
<Handle type="target" position={Position.Top} />
|
||||
|
||||
<div className="flex items-center">
|
||||
{data.icon && (
|
||||
<img src={data.icon} alt="" className="w-4 h-4 mr-2" />
|
||||
)}
|
||||
<span className="text-sm">{data.label}</span>
|
||||
</div>
|
||||
|
||||
<Handle type="source" position={Position.Bottom} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Styling with Tailwind
|
||||
|
||||
### Component Styling
|
||||
```typescript
|
||||
// components/Button.tsx
|
||||
import { cn } from '@/utils/cn';
|
||||
|
||||
interface ButtonProps {
|
||||
variant?: 'primary' | 'secondary';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
children: React.ReactNode;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export function Button({
|
||||
variant = 'primary',
|
||||
size = 'md',
|
||||
children,
|
||||
onClick
|
||||
}: ButtonProps) {
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
'rounded-md font-medium transition-colors',
|
||||
{
|
||||
'bg-blue-600 hover:bg-blue-700 text-white': variant === 'primary',
|
||||
'bg-gray-200 hover:bg-gray-300 text-gray-900': variant === 'secondary',
|
||||
'px-2 py-1 text-sm': size === 'sm',
|
||||
'px-4 py-2 text-base': size === 'md',
|
||||
'px-6 py-3 text-lg': size === 'lg',
|
||||
}
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Dark Mode Support
|
||||
```typescript
|
||||
// hooks/useDarkMode.ts
|
||||
import { useDarkStore } from '@/stores/darkStore';
|
||||
|
||||
export function useDarkMode() {
|
||||
const { dark, setDark } = useDarkStore();
|
||||
|
||||
const toggle = () => setDark(!dark);
|
||||
|
||||
return { isDark: dark, toggle };
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Frontend Testing
|
||||
|
||||
### Component Testing
|
||||
```typescript
|
||||
// __tests__/Button.test.tsx
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { Button } from '@/components/Button';
|
||||
|
||||
describe('Button', () => {
|
||||
it('renders with correct text', () => {
|
||||
render(<Button>Click me</Button>);
|
||||
expect(screen.getByText('Click me')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onClick when clicked', () => {
|
||||
const handleClick = jest.fn();
|
||||
render(<Button onClick={handleClick}>Click me</Button>);
|
||||
|
||||
fireEvent.click(screen.getByText('Click me'));
|
||||
expect(handleClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Integration Testing
|
||||
```typescript
|
||||
// __tests__/FlowEditor.test.tsx
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { FlowEditor } from '@/pages/FlowEditor';
|
||||
|
||||
describe('FlowEditor', () => {
|
||||
it('loads flow data correctly', async () => {
|
||||
render(<FlowEditor flowId="test-flow-id" />);
|
||||
|
||||
// Wait for flow to load
|
||||
await screen.findByText('Flow loaded');
|
||||
|
||||
expect(screen.getByTestId('flow-canvas')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Build and Deployment
|
||||
|
||||
### Development Build
|
||||
```bash
|
||||
make build_frontend # Build frontend static files
|
||||
```
|
||||
|
||||
### Production Build
|
||||
```bash
|
||||
cd src/frontend
|
||||
npm run build # Creates dist/ directory
|
||||
```
|
||||
|
||||
### Build Integration
|
||||
- Frontend builds to `src/frontend/dist/`
|
||||
- Backend serves static files from this directory in production
|
||||
- `make init` includes frontend build step
|
||||
---
|
||||
|
||||
## Frontend Development Checklist
|
||||
- [ ] Frontend service running with `make frontend`
|
||||
- [ ] Changes hot-reload correctly in browser
|
||||
- [ ] State management uses Zustand stores
|
||||
- [ ] API calls use proper error handling
|
||||
- [ ] Components styled with Tailwind CSS
|
||||
- [ ] Dark mode support implemented where needed
|
||||
- [ ] Code formatted with `make format_frontend`
|
||||
- [ ] Linting passed with `make lint`
|
||||
- [ ] Changes tested in both light and dark mode
|
||||
|
||||
- [ ] Components styled with Tailwind CSS
|
||||
- [ ] Dark mode support implemented where needed
|
||||
- [ ] Code formatted with `make format_frontend`
|
||||
- [ ] Changes tested in both light and dark mode
|
||||
|
||||
@ -1,137 +0,0 @@
|
||||
---
|
||||
description: "Rules for creating and implementing component icons in Langflow, covering both backend Python component icon attributes and frontend React icon implementation."
|
||||
globs:
|
||||
- "src/frontend/src/icons/**/*"
|
||||
- "src/frontend/src/icons/lazyIconImports.ts"
|
||||
- "src/backend/**/*component*.py"
|
||||
- "src/backend/**/components/**/*.py"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Component Icon Rules
|
||||
|
||||
## Purpose
|
||||
|
||||
To ensure consistent, clear, and functional icon usage for components, covering both backend (Python) and frontend (React/TypeScript) steps.
|
||||
|
||||
---
|
||||
|
||||
## 1. Backend (Python) — Setting the Icon Name
|
||||
|
||||
- **Where:** In your component class (e.g., in `src/backend/base/langflow/components/vectorstores/astradb.py`)
|
||||
- **How:**
|
||||
Set the `icon` attribute to a string matching the icon you want to use.
|
||||
```python
|
||||
icon = "AstraDB"
|
||||
```
|
||||
- **Tip:**
|
||||
The string must match the frontend icon mapping exactly (case-sensitive).
|
||||
|
||||
---
|
||||
|
||||
## 2. Frontend (React/TypeScript) — Adding the Icon
|
||||
|
||||
### a. Create the Icon Component
|
||||
|
||||
- **Where:**
|
||||
In a new directory for your icon, e.g., `src/frontend/src/icons/AstraDB/`.
|
||||
- **How:**
|
||||
|
||||
- Add your SVG as a React component, e.g., `AstraSVG` in `AstraDB.jsx`.
|
||||
```jsx
|
||||
const AstraSVG = (props) => (
|
||||
<svg {...props}>
|
||||
<path
|
||||
// ...
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
```
|
||||
- Create an `index.tsx` that exports your icon using `forwardRef`:
|
||||
|
||||
```tsx
|
||||
import React, { forwardRef } from "react";
|
||||
import AstraSVG from "./AstraDB";
|
||||
|
||||
export const AstraDBIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <AstraSVG ref={ref} isDark={isDark} {...props} />;
|
||||
});
|
||||
```
|
||||
|
||||
#### Supporting Light and Dark Mode Icons
|
||||
|
||||
- **How:**
|
||||
- In your SVG component (e.g., `AstraDB.jsx`), use the `isDark` prop to switch colors:
|
||||
```jsx
|
||||
const AstraSVG = (props) => (
|
||||
<svg {...props}>
|
||||
<path
|
||||
fill={props.isDark ? "#ffffff" : "#0A0A0A"}
|
||||
// ...
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
```
|
||||
- The `isDark` prop is passed from the icon wrapper (see above) and should be used to toggle between light and dark color schemes.
|
||||
- You can use a utility like `stringToBool` to ensure the prop is interpreted correctly.
|
||||
|
||||
### b. Add to Lazy Icon Imports
|
||||
|
||||
- **Where:**
|
||||
In `src/frontend/src/icons/lazyIconImports.ts`
|
||||
- **How:**
|
||||
Add an entry to the `lazyIconsMapping` object:
|
||||
```ts
|
||||
AstraDB: () =>
|
||||
import("@/icons/AstraDB").then((mod) => ({ default: mod.AstraDBIcon })),
|
||||
```
|
||||
- **Tip:**
|
||||
The key (`AstraDB`) must match the string used in the backend.
|
||||
|
||||
---
|
||||
|
||||
## 3. Best Practices
|
||||
|
||||
- **Naming:**
|
||||
Use clear, recognizable names (e.g., `"AstraDB"`, `"Postgres"`, `"OpenAI"`).
|
||||
- **Consistency:**
|
||||
Always use the same icon name for the same service across backend and frontend.
|
||||
- **Missing Icon:**
|
||||
If no icon exists, use a [lucide icon](https://lucide.dev/icons)
|
||||
- **Light/Dark Mode:**
|
||||
Always support both light and dark mode for custom icons by using the `isDark` prop in your SVG.
|
||||
|
||||
---
|
||||
|
||||
## 4. Checklist for Adding a New Icon
|
||||
|
||||
- [ ] Decide on a clear, descriptive icon name (e.g., `AstraDB`).
|
||||
- [ ] In your Python component, set `icon = "YourIconName"`.
|
||||
- [ ] Create a new icon directory in `src/frontend/src/icons/YourIconName/`.
|
||||
- [ ] Add your SVG as a React component (e.g., `YourIconNameIcon.jsx`).
|
||||
- [ ] Create an `index.tsx` that exports your icon using `forwardRef` and passes the `isDark` prop.
|
||||
- [ ] Add your icon to `lazyIconsMapping` in `src/frontend/src/icons/lazyIconImports.ts` with the exact same name.
|
||||
- [ ] Verify the icon appears correctly in the UI in both light and dark mode.
|
||||
- [ ] If no suitable icon exists, use a generic icon and request a new one if needed.
|
||||
|
||||
---
|
||||
|
||||
**Example for AstraDB:**
|
||||
|
||||
- Backend:
|
||||
```python
|
||||
icon = "AstraDB"
|
||||
```
|
||||
- Frontend:
|
||||
- `src/icons/AstraDB/AstraDB.jsx` (SVG as React component, uses `isDark` prop)
|
||||
- `src/icons/AstraDB/index.tsx` (exports `AstraDBIcon` and passes `isDark`)
|
||||
- Add to `lazyIconImports.ts`:
|
||||
```ts
|
||||
AstraDB: () =>
|
||||
import("@/icons/AstraDB").then((mod) => ({ default: mod.AstraDBIcon })),
|
||||
```
|
||||
|
||||
---
|
||||
@ -1,561 +0,0 @@
|
||||
---
|
||||
description: "Guidelines for testing Langflow components and backend code, with emphasis on async patterns and robust, well-documented testing practices."
|
||||
globs:
|
||||
- "src/backend/tests/**/*.py"
|
||||
- "src/frontend/**/*.test.{ts,tsx,js,jsx}"
|
||||
- "src/frontend/**/*.spec.{ts,tsx,js,jsx}"
|
||||
- "tests/**/*.py"
|
||||
- "conftest.py"
|
||||
- "pytest.ini"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Testing Guidelines for Langflow
|
||||
|
||||
## Purpose
|
||||
Guidelines for testing Langflow components and backend code, with emphasis on async patterns and robust, well-documented testing practices.
|
||||
|
||||
---
|
||||
|
||||
## 1. Testing Structure
|
||||
|
||||
### Backend Tests Location
|
||||
- **Unit Tests:** `src/backend/tests/`
|
||||
- **Component Tests:** `src/backend/tests/unit/components/` (organized by component subdirectory)
|
||||
- **Integration Tests:** Available via `make integration_tests` (requires additional setup)
|
||||
|
||||
### Test File Naming
|
||||
- Use same filename as component with appropriate test prefix/suffix
|
||||
- Example: `my_component.py` → `test_my_component.py`
|
||||
|
||||
---
|
||||
|
||||
## 2. Built-in Fixtures & Base Classes
|
||||
|
||||
### `client` Fixture (FastAPI Test Client)
|
||||
- Defined in `src/backend/tests/conftest.py`
|
||||
- Provides an **async** `httpx.AsyncClient` connected to the full application via `ASGITransport` + `LifespanManager`.
|
||||
- Use it for API tests:
|
||||
```python
|
||||
async def test_login_endpoint(client):
|
||||
response = await client.post("api/v1/login", data={"username": "foo", "password": "bar"})
|
||||
assert response.status_code == 200
|
||||
```
|
||||
- Automatically configured with an **in-memory SQLite database** and mocked environment variables. No additional setup needed in individual tests.
|
||||
- Skip client creation by marking the test with `@pytest.mark.noclient`.
|
||||
|
||||
### `ComponentTestBase` Family
|
||||
Located in `src/backend/tests/base.py`.
|
||||
|
||||
| Base Class | Creates `client`? | Typical Use | Notes |
|
||||
|------------|------------------|-------------|-------|
|
||||
| `ComponentTestBase` | No | Core logic for component version testing | Requires you to provide fixtures described below. |
|
||||
| `ComponentTestBaseWithClient` | Yes (`@pytest.mark.usefixtures("client")`) | Components that need API access during `run()` | Inherit when the component interacts with backend services. |
|
||||
| `ComponentTestBaseWithoutClient` | No | Pure-logic components with no API calls | Lightweight alternative. |
|
||||
|
||||
Required fixtures for subclasses:
|
||||
1. `component_class` → the component **class** under test.
|
||||
2. `default_kwargs` → dict of kwargs to instantiate the component (can be empty).
|
||||
3. `file_names_mapping` → list of `VersionComponentMapping` dicts mapping historical **Langflow versions** to module/file names.
|
||||
|
||||
Example skeleton:
|
||||
```python
|
||||
from tests.base import ComponentTestBaseWithClient, VersionComponentMapping, DID_NOT_EXIST
|
||||
from langflow.components.my_namespace import MyComponent
|
||||
|
||||
class TestMyComponent(ComponentTestBaseWithClient):
|
||||
@pytest.fixture
|
||||
def component_class(self):
|
||||
return MyComponent
|
||||
|
||||
@pytest.fixture
|
||||
def default_kwargs(self):
|
||||
return {"foo": "bar"}
|
||||
|
||||
@pytest.fixture
|
||||
def file_names_mapping(self):
|
||||
return [
|
||||
VersionComponentMapping(version="1.1.1", module="my_module", file_name="my_component.py"),
|
||||
# Older versions can be mapped or DID_NOT_EXIST
|
||||
VersionComponentMapping(version="1.0.19", module="my_module", file_name=DID_NOT_EXIST),
|
||||
]
|
||||
```
|
||||
|
||||
`ComponentTestBase` automatically provides:
|
||||
- `test_latest_version` → Instantiates component via `component_class` and asserts `run()` doesn't return `None`.
|
||||
- `test_all_versions_have_a_file_name_defined` → Ensures mapping completeness vs `SUPPORTED_VERSIONS` constant (`src/backend/tests/constants.py`).
|
||||
- `test_component_versions` (parametrised) → Builds component from source for each supported version and asserts successful execution.
|
||||
|
||||
When adding a new component test, **inherit from the correct base class and provide the three fixtures**. This greatly reduces boilerplate and enforces version compatibility.
|
||||
|
||||
---
|
||||
|
||||
## 3. Component Testing Requirements
|
||||
|
||||
### Minimum Testing Requirements
|
||||
- **Unit Tests:** Create comprehensive unit tests for all new components
|
||||
- **Manual Test Documentation:** If unit tests are incomplete, create a Markdown file with manual testing steps
|
||||
- Location: Same directory as unit tests
|
||||
- Filename: Same as component but with `.md` extension
|
||||
- Content: Detailed manual testing steps and expected outcomes
|
||||
|
||||
### Testing Best Practices
|
||||
- Test both sync and async code paths
|
||||
- Mock external dependencies appropriately
|
||||
- Test error handling and edge cases
|
||||
- Validate input/output behavior
|
||||
- Test component initialization and configuration
|
||||
|
||||
---
|
||||
|
||||
## 4. Async Testing Patterns
|
||||
|
||||
### Async Component Testing
|
||||
```python
|
||||
import pytest
|
||||
import asyncio
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_component():
|
||||
# Test async component methods
|
||||
result = await component.async_method()
|
||||
assert result is not None
|
||||
```
|
||||
|
||||
### Testing Background Tasks
|
||||
```python
|
||||
@pytest.mark.asyncio
|
||||
async def test_background_task_completion():
|
||||
# Ensure background tasks complete properly
|
||||
task = asyncio.create_task(component.background_operation())
|
||||
result = await asyncio.wait_for(task, timeout=5.0)
|
||||
assert result.success
|
||||
```
|
||||
|
||||
### Testing Queue Operations
|
||||
```python
|
||||
@pytest.mark.asyncio
|
||||
async def test_queue_operations():
|
||||
# Test queue put/get operations without blocking
|
||||
queue = asyncio.Queue()
|
||||
|
||||
# Non-blocking put
|
||||
queue.put_nowait(test_data)
|
||||
|
||||
# Verify queue processing
|
||||
result = await asyncio.wait_for(queue.get(), timeout=1.0)
|
||||
assert result == test_data
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Special Testing Considerations
|
||||
|
||||
### Blockbuster Plugin
|
||||
- Use `no_blockbuster` pytest marker to skip blockbuster plugin in tests
|
||||
- Example: `@pytest.mark.no_blockbuster`
|
||||
|
||||
### Database Tests
|
||||
- `test_database.py` may fail in batch runs but pass individually
|
||||
- Run problematic tests sequentially: `uv run pytest src/backend/tests/unit/test_database.py`
|
||||
- Consider this behavior when writing database-related tests
|
||||
|
||||
### Context Variables in Async Tests
|
||||
- Be aware of ContextVar propagation in async tests
|
||||
- Test both direct event loop execution and `asyncio.to_thread` scenarios
|
||||
- Ensure proper context isolation between test cases
|
||||
|
||||
---
|
||||
|
||||
## 6. Test Execution
|
||||
|
||||
### Running Tests
|
||||
```bash
|
||||
make unit_tests # Run all backend unit tests
|
||||
make integration_tests # Run integration tests (requires additional setup)
|
||||
make coverage # Run tests with coverage (requires additional setup)
|
||||
make tests_frontend # Run frontend tests (requires additional setup)
|
||||
```
|
||||
|
||||
### Individual Test Execution
|
||||
```bash
|
||||
# Run specific test file
|
||||
uv run pytest src/backend/tests/unit/test_specific_component.py
|
||||
|
||||
# Run specific test method
|
||||
uv run pytest src/backend/tests/unit/test_component.py::test_specific_method
|
||||
|
||||
# Run with verbose output
|
||||
uv run pytest -v src/backend/tests/unit/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Robust Testing Patterns
|
||||
|
||||
### Error Handling Tests
|
||||
```python
|
||||
def test_component_error_handling():
|
||||
"""Test that component handles errors gracefully."""
|
||||
with pytest.raises(ExpectedError):
|
||||
component.method_that_should_fail(invalid_input)
|
||||
|
||||
# Test error message quality
|
||||
try:
|
||||
component.risky_operation()
|
||||
except ComponentError as e:
|
||||
assert "helpful error message" in str(e)
|
||||
```
|
||||
|
||||
### Resource Cleanup Tests
|
||||
```python
|
||||
@pytest.fixture
|
||||
async def component_with_cleanup():
|
||||
"""Fixture ensuring proper resource cleanup."""
|
||||
component = MyComponent()
|
||||
await component.initialize()
|
||||
try:
|
||||
yield component
|
||||
finally:
|
||||
await component.cleanup()
|
||||
```
|
||||
|
||||
### Timeout and Performance Tests
|
||||
```python
|
||||
@pytest.mark.asyncio
|
||||
async def test_operation_timeout():
|
||||
"""Test that operations respect timeout constraints."""
|
||||
start_time = time.time()
|
||||
|
||||
with pytest.raises(asyncio.TimeoutError):
|
||||
await asyncio.wait_for(
|
||||
component.long_running_operation(),
|
||||
timeout=1.0
|
||||
)
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
assert elapsed < 1.5 # Allow some tolerance
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Documentation in Tests
|
||||
|
||||
### Test Documentation Standards
|
||||
- Each test should have a clear docstring explaining its purpose
|
||||
- Complex test setups should be commented
|
||||
- Mock usage should be documented
|
||||
- Expected behaviors should be explicitly stated
|
||||
|
||||
### Example Well-Documented Test
|
||||
```python
|
||||
async def test_component_background_processing():
|
||||
"""
|
||||
Test that component processes items in background without blocking.
|
||||
|
||||
This test verifies:
|
||||
1. Items are added to processing queue immediately
|
||||
2. Background processing completes successfully
|
||||
3. No deadlocks occur during shutdown
|
||||
4. All tasks are properly cleaned up
|
||||
"""
|
||||
# Setup component with background processing
|
||||
component = BackgroundComponent()
|
||||
await component.start()
|
||||
|
||||
try:
|
||||
# Add items for processing
|
||||
for i in range(10):
|
||||
await component.add_item(f"item_{i}")
|
||||
|
||||
# Verify items are queued (non-blocking)
|
||||
assert component.queue_size() == 10
|
||||
|
||||
# Wait for processing to complete
|
||||
await component.wait_for_completion(timeout=5.0)
|
||||
|
||||
# Verify all items processed
|
||||
assert component.processed_count() == 10
|
||||
|
||||
finally:
|
||||
# Ensure clean shutdown
|
||||
await component.stop()
|
||||
assert component.is_stopped()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
- [ ] Unit tests created for all new components
|
||||
- [ ] Async patterns tested appropriately
|
||||
- [ ] Error handling and edge cases covered
|
||||
- [ ] Manual testing documentation created (if unit tests incomplete)
|
||||
- [ ] Background tasks and queues tested for proper cleanup
|
||||
- [ ] Database tests considered for sequential execution if needed
|
||||
- [ ] Appropriate pytest markers used (`no_blockbuster`, etc.)
|
||||
- [ ] Tests are well-documented with clear docstrings
|
||||
- [ ] Resource cleanup properly handled in fixtures
|
||||
- [ ] Timeout and performance constraints validated
|
||||
|
||||
---
|
||||
|
||||
## 9. Langflow-Specific Testing Patterns
|
||||
|
||||
### Message Testing
|
||||
Test Langflow's `Message` objects and chat functionality:
|
||||
|
||||
```python
|
||||
from langflow.schema.message import Message
|
||||
from langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER
|
||||
|
||||
async def test_message_response(self, component_class, default_kwargs):
|
||||
"""Test Message object creation and properties."""
|
||||
component = component_class(**default_kwargs)
|
||||
message = await component.message_response()
|
||||
|
||||
assert isinstance(message, Message)
|
||||
assert message.text == default_kwargs["input_value"]
|
||||
assert message.sender == default_kwargs["sender"]
|
||||
assert message.sender_name == default_kwargs["sender_name"]
|
||||
assert message.session_id == default_kwargs["session_id"]
|
||||
assert message.files == default_kwargs["files"]
|
||||
# Test message properties structure
|
||||
assert message.properties.model_dump() == {
|
||||
"background_color": default_kwargs["background_color"],
|
||||
"text_color": default_kwargs["text_color"],
|
||||
"icon": default_kwargs["chat_icon"],
|
||||
# ... other expected properties
|
||||
}
|
||||
```
|
||||
|
||||
### Flow Testing with JSON Data
|
||||
Use predefined JSON flows and utility functions:
|
||||
|
||||
```python
|
||||
from tests.unit.build_utils import create_flow, build_flow, get_build_events, consume_and_assert_stream
|
||||
|
||||
async def test_flow_execution(client, json_memory_chatbot_no_llm, logged_in_headers):
|
||||
"""Test flow creation, building, and execution."""
|
||||
# Create flow from JSON data
|
||||
flow_id = await create_flow(client, json_memory_chatbot_no_llm, logged_in_headers)
|
||||
|
||||
# Start the build process
|
||||
build_response = await build_flow(client, flow_id, logged_in_headers)
|
||||
job_id = build_response["job_id"]
|
||||
|
||||
# Get and validate event stream
|
||||
events_response = await get_build_events(client, job_id, logged_in_headers)
|
||||
assert events_response.status_code == codes.OK
|
||||
|
||||
# Process events and validate structure
|
||||
await consume_and_assert_stream(events_response, job_id)
|
||||
```
|
||||
|
||||
### Component Testing with Real Services
|
||||
Test components that need external APIs with proper markers:
|
||||
|
||||
```python
|
||||
@pytest.mark.api_key_required
|
||||
@pytest.mark.no_blockbuster
|
||||
async def test_component_with_external_api(self):
|
||||
"""Test component with external API integration."""
|
||||
api_key = os.getenv("OPENAI_API_KEY")
|
||||
|
||||
component = MyAPIComponent(
|
||||
api_key=api_key,
|
||||
model_name="gpt-4o",
|
||||
input_value="test input",
|
||||
session_id=str(uuid4()),
|
||||
)
|
||||
|
||||
response = await component.message_response()
|
||||
assert response.data.get("text") is not None
|
||||
```
|
||||
|
||||
### Mocking Language Models
|
||||
Use `MockLanguageModel` for testing without external APIs:
|
||||
|
||||
```python
|
||||
from tests.unit.mock_language_model import MockLanguageModel
|
||||
|
||||
@pytest.fixture
|
||||
def default_kwargs(self):
|
||||
return {
|
||||
"agent_llm": MockLanguageModel(),
|
||||
"input_value": "test message",
|
||||
"session_id": str(uuid4()),
|
||||
# ... other component-specific kwargs
|
||||
}
|
||||
```
|
||||
|
||||
### File Handling in Tests
|
||||
Use `anyio` and `aiofiles` for async file operations:
|
||||
|
||||
```python
|
||||
import aiofiles
|
||||
import anyio
|
||||
|
||||
async def test_file_operations(self, component_class, tmp_path):
|
||||
"""Test component file handling."""
|
||||
# Create temporary test file
|
||||
test_file = anyio.Path(tmp_path) / "test.txt"
|
||||
await test_file.write_text("Test content", encoding="utf-8")
|
||||
|
||||
# Test with file input
|
||||
component = component_class(files=[str(test_file)])
|
||||
result = await component.run()
|
||||
|
||||
# Verify file was processed
|
||||
assert len(result.files) == 1
|
||||
assert await test_file.exists()
|
||||
```
|
||||
|
||||
### API Endpoint Testing
|
||||
Test Langflow's REST API endpoints:
|
||||
|
||||
```python
|
||||
async def test_flows_endpoint(client, logged_in_headers):
|
||||
"""Test flow creation via API."""
|
||||
flow_data = {
|
||||
"name": "Test Flow",
|
||||
"description": "Test description",
|
||||
"data": {"nodes": [], "edges": []}
|
||||
}
|
||||
|
||||
response = await client.post(
|
||||
"api/v1/flows/",
|
||||
json=flow_data,
|
||||
headers=logged_in_headers
|
||||
)
|
||||
assert response.status_code == codes.CREATED
|
||||
|
||||
flow = response.json()
|
||||
assert flow["name"] == "Test Flow"
|
||||
assert "id" in flow
|
||||
```
|
||||
|
||||
### Build Config Testing
|
||||
Test component configuration updates:
|
||||
|
||||
```python
|
||||
async def test_build_config_updates(self, component_class, default_kwargs):
|
||||
"""Test dynamic build configuration updates."""
|
||||
component = await self.component_setup(component_class, default_kwargs)
|
||||
frontend_node = component.to_frontend_node()
|
||||
build_config = frontend_node["data"]["node"]["template"]
|
||||
|
||||
# Test configuration update
|
||||
component.set(llm_provider="OpenAI")
|
||||
updated_config = await component.update_build_config(
|
||||
build_config, "OpenAI", "llm_provider"
|
||||
)
|
||||
|
||||
assert updated_config["llm_provider"]["value"] == "OpenAI"
|
||||
assert "gpt-4o" in updated_config["model_name"]["options"]
|
||||
```
|
||||
|
||||
### Testing Event Streams
|
||||
Test real-time event streaming endpoints:
|
||||
|
||||
```python
|
||||
async def consume_and_validate_events(response, expected_job_id):
|
||||
"""Consume NDJSON event stream and validate structure."""
|
||||
count = 0
|
||||
first_event_seen = False
|
||||
end_event_seen = False
|
||||
|
||||
async for line in response.aiter_lines():
|
||||
if not line:
|
||||
continue
|
||||
|
||||
parsed = json.loads(line)
|
||||
|
||||
# Validate job_id in events
|
||||
if "job_id" in parsed:
|
||||
assert parsed["job_id"] == expected_job_id
|
||||
|
||||
# First event should be vertices_sorted
|
||||
if not first_event_seen:
|
||||
assert parsed["event"] == "vertices_sorted"
|
||||
first_event_seen = True
|
||||
|
||||
# Track end event
|
||||
elif parsed["event"] == "end":
|
||||
end_event_seen = True
|
||||
|
||||
count += 1
|
||||
|
||||
assert first_event_seen and end_event_seen
|
||||
return count
|
||||
```
|
||||
|
||||
### Testing Component Versioning
|
||||
Test backward compatibility across Langflow versions:
|
||||
|
||||
```python
|
||||
@pytest.fixture
|
||||
def file_names_mapping(self):
|
||||
"""Map component files across Langflow versions."""
|
||||
return [
|
||||
VersionComponentMapping(
|
||||
version="1.1.1",
|
||||
module="inputs",
|
||||
file_name="chat"
|
||||
),
|
||||
VersionComponentMapping(
|
||||
version="1.1.0",
|
||||
module="inputs",
|
||||
file_name="chat"
|
||||
),
|
||||
VersionComponentMapping(
|
||||
version="1.0.19",
|
||||
module="inputs",
|
||||
file_name="ChatInput"
|
||||
),
|
||||
]
|
||||
```
|
||||
|
||||
### Webhook Testing
|
||||
Test webhook endpoints with proper payload handling:
|
||||
|
||||
```python
|
||||
async def test_webhook_endpoint(client, added_webhook_test):
|
||||
"""Test webhook flow execution."""
|
||||
endpoint_name = added_webhook_test["endpoint_name"]
|
||||
endpoint = f"api/v1/webhook/{endpoint_name}"
|
||||
|
||||
payload = {"path": "/tmp/test_file.txt"}
|
||||
|
||||
response = await client.post(endpoint, json=payload)
|
||||
assert response.status_code == 202
|
||||
|
||||
# Verify webhook processed the payload
|
||||
# ... additional validation logic
|
||||
```
|
||||
|
||||
### Monkeypatch for Testing Errors
|
||||
Test error handling by mocking internal functions:
|
||||
|
||||
```python
|
||||
async def test_cancellation_error(client, flow_id, logged_in_headers, monkeypatch):
|
||||
"""Test error handling during flow cancellation."""
|
||||
import langflow.api.v1.chat
|
||||
|
||||
async def mock_cancel_with_error(*args, **kwargs):
|
||||
raise RuntimeError("Cancellation failed")
|
||||
|
||||
monkeypatch.setattr(
|
||||
langflow.api.v1.chat,
|
||||
"cancel_flow_build",
|
||||
mock_cancel_with_error
|
||||
)
|
||||
|
||||
response = await client.post(
|
||||
f"api/v1/build/{job_id}/cancel",
|
||||
headers=logged_in_headers
|
||||
)
|
||||
assert response.status_code == codes.INTERNAL_SERVER_ERROR
|
||||
assert "Cancellation failed" in response.json()["detail"]
|
||||
```
|
||||
|
||||
|
||||
39
.env.example
39
.env.example
@ -18,25 +18,24 @@ LANGFLOW_CONFIG_DIR=
|
||||
LANGFLOW_SAVE_DB_IN_CONFIG_DIR=
|
||||
|
||||
# Database URL
|
||||
# Postgres example: LANGFLOW_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/langflow
|
||||
# Postgres example: LANGFLOW_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/langflow # pragma: allowlist secret
|
||||
# SQLite example:
|
||||
LANGFLOW_DATABASE_URL=sqlite:///./langflow.db
|
||||
|
||||
# Alembic logs path flag. If set to true, Alembic will log to stdout.
|
||||
LANGFLOW_ALEMBIC_LOG_TO_STDOUT=False
|
||||
|
||||
|
||||
# mem0 creates a directory
|
||||
# mem0 creates a directory
|
||||
# for chat history, vector stores, and other artifacts
|
||||
# its default path is ~/.mem0.
|
||||
# we can change this path with
|
||||
# we can change this path with
|
||||
# environment variable "MEM0_DIR"
|
||||
# Example: MEM0_DIR=/tmp/.mem0
|
||||
|
||||
# composio creates a cache directory
|
||||
# composio creates a cache directory
|
||||
# for file uploads/downloads.
|
||||
# its default path is ~/.composio
|
||||
# we can change this path with
|
||||
# we can change this path with
|
||||
# environment variable "COMPOSIO_CACHE_DIR"
|
||||
# Example: COMPOSIO_CACHE_DIR=/tmp/.composio
|
||||
|
||||
@ -131,6 +130,34 @@ LANGFLOW_API_KEY_SOURCE=
|
||||
# Example: LANGFLOW_API_KEY=your-secure-api-key
|
||||
LANGFLOW_API_KEY=
|
||||
|
||||
# Rate Limiting Configuration
|
||||
# Controls rate limiting for login endpoint to prevent brute force attacks
|
||||
# LANGFLOW_RATE_LIMIT_PER_MINUTE: Number of login attempts allowed per minute per IP
|
||||
# Values: positive integer (default: 5)
|
||||
# Example: LANGFLOW_RATE_LIMIT_PER_MINUTE=10
|
||||
LANGFLOW_RATE_LIMIT_PER_MINUTE=
|
||||
|
||||
# LANGFLOW_RATE_LIMIT_STORAGE_URI: Storage backend for rate limit counters
|
||||
# Values: memory:// (default, single server) or redis://host:port (production, multiple servers)
|
||||
# - memory://: Fast, in-memory storage (not shared between server instances, lost on restart)
|
||||
# - redis://localhost:6379: Persistent, shared storage (required for load-balanced deployments)
|
||||
# Example: LANGFLOW_RATE_LIMIT_STORAGE_URI=redis://localhost:6379
|
||||
LANGFLOW_RATE_LIMIT_STORAGE_URI=
|
||||
|
||||
# LANGFLOW_RATE_LIMIT_TRUST_PROXY: Trust X-Forwarded-For header for client IP
|
||||
# Values: true, false (default: false)
|
||||
# When true, uses rightmost X-Forwarded-For IP (the trusted proxy before us)
|
||||
# When false, uses direct connection IP (prevents header spoofing in non-proxy deployments)
|
||||
#
|
||||
# SECURITY WARNING: Only enable if behind a trusted proxy (nginx, load balancer, etc.)
|
||||
# This setting controls BOTH the application rate limiter AND uvicorn's proxy header processing.
|
||||
# When false (default), uvicorn will NOT trust X-Forwarded-For headers, preventing IP spoofing.
|
||||
# When true, uvicorn trusts ALL proxy headers - ensure your proxy is properly configured to
|
||||
# prevent client-supplied X-Forwarded-For values from reaching Langflow.
|
||||
#
|
||||
# Example: LANGFLOW_RATE_LIMIT_TRUST_PROXY=true
|
||||
LANGFLOW_RATE_LIMIT_TRUST_PROXY=
|
||||
|
||||
# Should store environment variables in the database
|
||||
# Values: true, false
|
||||
LANGFLOW_STORE_ENVIRONMENT_VARIABLES=
|
||||
|
||||
17
.github/workflows/ci.yml
vendored
17
.github/workflows/ci.yml
vendored
@ -117,8 +117,11 @@ jobs:
|
||||
TODAY=$(date -u +"%Y-%m-%d")
|
||||
echo "Today's date: $TODAY"
|
||||
|
||||
# Query PyPI API for the langflow package
|
||||
HTTP_STATUS=$(curl -s -o response.json -w "%{http_code}" https://pypi.org/pypi/langflow-nightly/json)
|
||||
# Query PyPI for the canonical langflow project. The nightly now publishes as a `.devN`
|
||||
# pre-release of `langflow` (not a separate `langflow-nightly` distribution), so inspect
|
||||
# the most recent dev release rather than `.info.version` (which is the latest stable).
|
||||
# See src/bundles/NIGHTLY.md.
|
||||
HTTP_STATUS=$(curl -s -o response.json -w "%{http_code}" https://pypi.org/pypi/langflow/json)
|
||||
|
||||
# Check HTTP status code first
|
||||
if [ "$HTTP_STATUS" -ne 200 ]; then
|
||||
@ -136,16 +139,16 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Extract the latest version
|
||||
LATEST_VERSION=$(jq -r '.info.version // empty' response.json)
|
||||
# Extract the most recent nightly (.devN pre-release), highest by version sort
|
||||
LATEST_VERSION=$(jq -r '.releases | keys[]' response.json | grep -E '\.dev[0-9]+$' | sort -V | tail -n1)
|
||||
|
||||
if [ -z "$LATEST_VERSION" ]; then
|
||||
echo "Could not extract latest version"
|
||||
echo "Could not find a nightly (.devN) release"
|
||||
echo "success=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Extract the release date of the latest version
|
||||
# Extract the release date of that nightly version
|
||||
RELEASE_DATE=$(jq -r --arg ver "$LATEST_VERSION" '.releases[$ver][0].upload_time_iso_8601 // empty' response.json | cut -d'T' -f1)
|
||||
|
||||
if [ -z "$RELEASE_DATE" ]; then
|
||||
@ -282,6 +285,8 @@ jobs:
|
||||
needs.path-filter.outputs.frontend == 'true')
|
||||
)
|
||||
uses: ./.github/workflows/lint-js.yml
|
||||
with:
|
||||
allow-failure: ${{ inputs.release == true }}
|
||||
|
||||
test-frontend-unit:
|
||||
needs: [path-filter, set-ci-condition]
|
||||
|
||||
27
.github/workflows/db-migration-validation.yml
vendored
27
.github/workflows/db-migration-validation.yml
vendored
@ -161,12 +161,12 @@ jobs:
|
||||
NIGHTLY_TAG="${{ inputs.nightly_tag || 'langflowai/langflow-nightly:latest' }}"
|
||||
echo "Upgrading to nightly version: $NIGHTLY_TAG"
|
||||
|
||||
# Remove stable distributions before installing nightly.
|
||||
# langflow and langflow-nightly share langflow-base which writes to the
|
||||
# same site-packages/langflow/ namespace. If stable stays installed,
|
||||
# python -m langflow still boots the stable version and no real
|
||||
# nightly migration is exercised (false-positive test).
|
||||
echo "Removing stable langflow to avoid namespace collision with nightly..."
|
||||
# Remove the stable install first. The nightly now publishes as a `.devN` pre-release of
|
||||
# the canonical `langflow` (same distribution as stable, different version), so a clean
|
||||
# uninstall avoids stale files and guarantees the dev version is what boots — otherwise
|
||||
# `python -m langflow` could keep running the stable version and no real nightly migration
|
||||
# is exercised (false-positive test).
|
||||
echo "Removing stable langflow to force a clean install of the nightly dev version..."
|
||||
uv pip uninstall -y langflow langflow-base || true
|
||||
|
||||
# Extract version from Docker tag (format: langflowai/langflow-nightly:v1.10.0.dev20260522)
|
||||
@ -179,20 +179,23 @@ jobs:
|
||||
echo "Version for PyPI: $VERSION"
|
||||
|
||||
if [[ "$VERSION" == "latest" ]]; then
|
||||
# Install latest nightly from PyPI
|
||||
uv pip install --upgrade 'langflow-nightly[postgresql]'
|
||||
# Install latest nightly (canonical pre-release) from PyPI
|
||||
uv pip install --upgrade --prerelease=allow 'langflow[postgresql]'
|
||||
else
|
||||
# Install specific version
|
||||
uv pip install --upgrade "langflow-nightly[postgresql]==$VERSION"
|
||||
# Install specific version. --prerelease=allow is required even for an
|
||||
# exact ==X.Y.Z.devN pin: uv only implicitly allows the pre-release for
|
||||
# the directly requested package, while langflow's metadata pins
|
||||
# langflow-base==X.Y.Z.devN transitively, which uv rejects without it.
|
||||
uv pip install --upgrade --prerelease=allow "langflow[postgresql]==$VERSION"
|
||||
fi
|
||||
else
|
||||
# Direct version string (strip 'v' prefix if present)
|
||||
VERSION="${NIGHTLY_TAG#v}"
|
||||
uv pip install --upgrade "langflow-nightly[postgresql]==$VERSION"
|
||||
uv pip install --upgrade --prerelease=allow "langflow[postgresql]==$VERSION"
|
||||
fi
|
||||
|
||||
# Verify upgrade
|
||||
python -c 'from importlib.metadata import version; print("Upgraded to Langflow Nightly version:", version("langflow-nightly"))'
|
||||
python -c 'from importlib.metadata import version; print("Upgraded to Langflow Nightly version:", version("langflow"))'
|
||||
|
||||
- name: Run migration and verify startup
|
||||
working-directory: migration-test
|
||||
|
||||
8
.github/workflows/docker-nightly-build.yml
vendored
8
.github/workflows/docker-nightly-build.yml
vendored
@ -72,7 +72,11 @@ jobs:
|
||||
id: version
|
||||
run: |
|
||||
echo "Extracting base version from pyproject.toml"
|
||||
version=$(uv tree 2>/dev/null | grep 'langflow-base' | awk '{print $3}' | sed 's/^v//' | head -n 1)
|
||||
# Anchor to the langflow-base workspace-root line (same pattern as the
|
||||
# langflow extraction below): the unanchored grep used to match a
|
||||
# dependency line whose version sat in field 3, but uv tree now lists
|
||||
# the langflow-base root line first, which has no third field.
|
||||
version=$(uv tree 2>/dev/null | grep -E '^langflow-base[[:space:]]' | cut -d' ' -f2 | sed 's/^v//' | head -n 1)
|
||||
|
||||
|
||||
# Verify nightly tag format
|
||||
@ -351,7 +355,7 @@ jobs:
|
||||
id: version
|
||||
run: |
|
||||
if [[ "${{ inputs.release_type }}" == "nightly-base" ]]; then
|
||||
version=$(uv tree 2>/dev/null | grep 'langflow-base' | awk '{print $3}' | sed 's/^v//' | head -n 1)
|
||||
version=$(uv tree 2>/dev/null | grep -E '^langflow-base[[:space:]]' | cut -d' ' -f2 | sed 's/^v//' | head -n 1)
|
||||
else
|
||||
version=$(uv tree 2>/dev/null | grep -E '^langflow(-nightly)?[[:space:]]' | cut -d' ' -f2 | sed 's/^v//')
|
||||
fi
|
||||
|
||||
18
.github/workflows/lint-js.yml
vendored
18
.github/workflows/lint-js.yml
vendored
@ -2,6 +2,12 @@ name: Lint Frontend
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
allow-failure:
|
||||
description: "When true, Biome errors are reported but job exits 0 (non-blocking)."
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
@ -84,6 +90,12 @@ jobs:
|
||||
RELATIVE_FILES=$(echo "$CHANGED_FILES" | sed 's|^src/frontend/||')
|
||||
|
||||
cd src/frontend
|
||||
echo "$RELATIVE_FILES" | xargs npx @biomejs/biome check \
|
||||
--files-ignore-unknown=true \
|
||||
--diagnostic-level=error
|
||||
if ${{ inputs.allow-failure || 'false' }}; then
|
||||
echo "$RELATIVE_FILES" | xargs npx @biomejs/biome check \
|
||||
--files-ignore-unknown=true \
|
||||
--diagnostic-level=error || echo "Biome found errors (non-blocking for this run)."
|
||||
else
|
||||
echo "$RELATIVE_FILES" | xargs npx @biomejs/biome check \
|
||||
--files-ignore-unknown=true \
|
||||
--diagnostic-level=error
|
||||
fi
|
||||
|
||||
28
.github/workflows/nightly_build.yml
vendored
28
.github/workflows/nightly_build.yml
vendored
@ -172,13 +172,9 @@ jobs:
|
||||
cd src/lfx && uv lock && cd ../..
|
||||
|
||||
git add pyproject.toml src/backend/base/pyproject.toml src/lfx/pyproject.toml src/sdk/pyproject.toml uv.lock
|
||||
# update_lfx_version.py re-pins each bundle's `lfx` dep to
|
||||
# lfx-nightly==<dev>, so any modified bundle pyprojects need to
|
||||
# ride the same commit/tag as the rest of the version bumps.
|
||||
if compgen -G "src/bundles/*/pyproject.toml" > /dev/null; then
|
||||
git add src/bundles/*/pyproject.toml
|
||||
fi
|
||||
git commit -m "Update version and project name"
|
||||
# The nightly keeps canonical package names and the stable `lfx-*` bundles
|
||||
# are not modified (see src/bundles/NIGHTLY.md), so no bundle pyprojects ride this commit.
|
||||
git commit -m "Update version for nightly"
|
||||
|
||||
echo "Tagging main with $RELEASE_TAG"
|
||||
if ! git tag -a $RELEASE_TAG -m "Langflow nightly $RELEASE_TAG"; then
|
||||
@ -283,11 +279,19 @@ jobs:
|
||||
# python-versions: '["3.10", "3.11", "3.12", "3.13"]'
|
||||
# ref: ${{ needs.create-nightly-tag.outputs.tag }}
|
||||
|
||||
stress-tests:
|
||||
if: github.repository == 'langflow-ai/langflow' && !inputs.skip_backend_tests
|
||||
name: Run Stress Tests
|
||||
needs: [resolve-release-branch, create-nightly-tag]
|
||||
uses: ./.github/workflows/stress-tests.yml
|
||||
with:
|
||||
ref: ${{ needs.resolve-release-branch.outputs.branch }}
|
||||
|
||||
release-nightly-build:
|
||||
if: github.repository == 'langflow-ai/langflow' && always() && needs.create-nightly-tag.result == 'success' && needs.frontend-tests-linux.result != 'failure' && needs.backend-unit-tests.result != 'failure'
|
||||
name: Run Nightly Langflow Build
|
||||
needs:
|
||||
[validate-inputs, create-nightly-tag, frontend-tests-linux, frontend-tests-windows, backend-unit-tests]
|
||||
[validate-inputs, create-nightly-tag, frontend-tests-linux, frontend-tests-windows, backend-unit-tests, stress-tests]
|
||||
uses: ./.github/workflows/release_nightly.yml
|
||||
with:
|
||||
build_docker_base: true
|
||||
@ -316,12 +320,12 @@ jobs:
|
||||
|
||||
slack-notification:
|
||||
name: Send Slack Notification
|
||||
needs: [frontend-tests-linux, frontend-tests-windows, backend-unit-tests, release-nightly-build, db-migration-validation]
|
||||
if: ${{ github.repository == 'langflow-ai/langflow' && !inputs.skip_slack && always() && (needs.release-nightly-build.result == 'failure' || needs.frontend-tests-linux.result == 'failure' || needs.frontend-tests-windows.result == 'failure' || needs.backend-unit-tests.result == 'failure' || needs.db-migration-validation.result == 'failure' || needs.release-nightly-build.result == 'success') }}
|
||||
needs: [frontend-tests-linux, frontend-tests-windows, backend-unit-tests, stress-tests, release-nightly-build, db-migration-validation]
|
||||
if: ${{ github.repository == 'langflow-ai/langflow' && !inputs.skip_slack && always() && (needs.release-nightly-build.result == 'failure' || needs.frontend-tests-linux.result == 'failure' || needs.frontend-tests-windows.result == 'failure' || needs.backend-unit-tests.result == 'failure' || needs.stress-tests.result == 'failure' || needs.db-migration-validation.result == 'failure' || needs.release-nightly-build.result == 'success') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send failure notification to Slack
|
||||
if: ${{ needs.release-nightly-build.result == 'failure' || needs.frontend-tests-linux.result == 'failure' || needs.frontend-tests-windows.result == 'failure' || needs.backend-unit-tests.result == 'failure' || needs.db-migration-validation.result == 'failure' }}
|
||||
if: ${{ needs.release-nightly-build.result == 'failure' || needs.frontend-tests-linux.result == 'failure' || needs.frontend-tests-windows.result == 'failure' || needs.backend-unit-tests.result == 'failure' || needs.stress-tests.result == 'failure' || needs.db-migration-validation.result == 'failure' }}
|
||||
run: |
|
||||
# Determine which job failed
|
||||
FAILED_JOB="unknown"
|
||||
@ -333,6 +337,8 @@ jobs:
|
||||
FAILED_JOB="frontend-tests-windows (non-blocking)"
|
||||
elif [ "${{ needs.backend-unit-tests.result }}" == "failure" ]; then
|
||||
FAILED_JOB="backend-unit-tests"
|
||||
elif [ "${{ needs.stress-tests.result }}" == "failure" ]; then
|
||||
FAILED_JOB="stress-tests (non-blocking)"
|
||||
elif [ "${{ needs.db-migration-validation.result }}" == "failure" ]; then
|
||||
FAILED_JOB="db-migration-validation"
|
||||
fi
|
||||
|
||||
2
.github/workflows/python_test.yml
vendored
2
.github/workflows/python_test.yml
vendored
@ -93,7 +93,7 @@ jobs:
|
||||
- name: Run unit tests
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 40
|
||||
timeout_minutes: 50
|
||||
max_attempts: 2
|
||||
command: make unit_tests args="-x -vv --splits ${{ matrix.splitCount }} --group ${{ matrix.group }} --reruns 5 --cov --cov-config=src/backend/.coveragerc --cov-report=xml --cov-report=html"
|
||||
|
||||
|
||||
48
.github/workflows/release.yml
vendored
48
.github/workflows/release.yml
vendored
@ -646,12 +646,16 @@ jobs:
|
||||
CURRENT_CONSTRAINT=$(grep -E '^\s*"lfx' pyproject.toml | head -1)
|
||||
echo "Current constraint: $CURRENT_CONSTRAINT"
|
||||
|
||||
# Extract the major.minor version (e.g., "0.3" from "~=0.3.0")
|
||||
MAJOR_MINOR=$(echo "$CURRENT_CONSTRAINT" | sed -E 's/.*[~>=<]+([0-9]+\.[0-9]+).*/\1/')
|
||||
NEXT_MAJOR=$((${MAJOR_MINOR%.*} + 1))
|
||||
# Extract the major.minor version (e.g., "1.10" from "~=1.10.0", or from the
|
||||
# lower bound of the range form ">=1.10.0,<1.11.dev0"). Anchor to the FIRST
|
||||
# version in the line so a greedy match can't pick up the range's upper bound.
|
||||
MAJOR_MINOR=$(echo "$CURRENT_CONSTRAINT" | sed -E 's/^[^0-9]*([0-9]+\.[0-9]+).*/\1/')
|
||||
MAJOR=${MAJOR_MINOR%.*}
|
||||
MINOR=${MAJOR_MINOR#*.}
|
||||
NEXT_MINOR=$((MINOR + 1))
|
||||
|
||||
# Create new constraint: >=LFX_VERSION,<NEXT_MAJOR.dev0
|
||||
NEW_CONSTRAINT="\"lfx>=$LFX_VERSION,<$NEXT_MAJOR.dev0\""
|
||||
# Create new constraint: >=LFX_VERSION,<MAJOR.(MINOR+1).dev0
|
||||
NEW_CONSTRAINT="\"lfx>=$LFX_VERSION,<$MAJOR.$NEXT_MINOR.dev0\""
|
||||
|
||||
echo "New constraint: $NEW_CONSTRAINT"
|
||||
|
||||
@ -884,6 +888,23 @@ jobs:
|
||||
prune-cache: false
|
||||
- name: Install the project
|
||||
run: uv sync
|
||||
- name: Relax bundle lfx floor for pre-release
|
||||
if: ${{ inputs.pre_release }}
|
||||
run: |
|
||||
# Bundles floor lfx at the release line (e.g. lfx>=1.10.0). A pre-release
|
||||
# such as 1.10.0rc0 sorts BELOW 1.10.0 under PEP 440, so it fails that
|
||||
# floor and the cross-platform install test cannot resolve. Mirror what
|
||||
# build-base / build-main / build-lfx already do for pre-release: rewrite
|
||||
# the floor to the exact pre-release lfx version, preserving the bundle's
|
||||
# wide <2.0.0 BUNDLE_API cap. Stable source stays at >=1.10.0 -- only the
|
||||
# RC wheels are relaxed, at build time.
|
||||
LFX_VERSION="${{ needs.determine-lfx-version.outputs.version }}"
|
||||
echo "Relaxing bundle lfx floor to pre-release version: $LFX_VERSION"
|
||||
for pyproject in src/bundles/*/pyproject.toml; do
|
||||
sed -i.bak -E "s|\"lfx>=[^\"]*\"|\"lfx>=${LFX_VERSION},<2.0.0\"|" "$pyproject"
|
||||
rm -f "${pyproject}.bak"
|
||||
grep -E '"lfx>=' "$pyproject" | sed "s|^| ${pyproject}: |"
|
||||
done
|
||||
- name: Build bundle wheels
|
||||
id: build
|
||||
run: |
|
||||
@ -991,10 +1012,25 @@ jobs:
|
||||
echo "No bundle wheels to publish."
|
||||
exit 0
|
||||
fi
|
||||
failed=0
|
||||
for wheel in "${wheels[@]}"; do
|
||||
echo "Publishing $wheel"
|
||||
uv publish "$wheel"
|
||||
# Tolerate "already exists" so reruns -- or a bundle whose version is
|
||||
# unchanged and already on PyPI -- do not fail the job. Matches the
|
||||
# duplicate-tolerant publish in release_bundles.yml.
|
||||
if ! err=$(uv publish "$wheel" 2>&1); then
|
||||
echo "$err"
|
||||
if echo "$err" | grep -qiE 'already exists|file already exists|HTTP 400|duplicate'; then
|
||||
echo "Skipping $wheel: already published."
|
||||
else
|
||||
echo "Publish failed for $wheel"
|
||||
failed=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [ "$failed" = "1" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
publish-main:
|
||||
name: Publish Langflow Main to PyPI
|
||||
|
||||
118
.github/workflows/release_nightly.yml
vendored
118
.github/workflows/release_nightly.yml
vendored
@ -81,8 +81,8 @@ jobs:
|
||||
run: |
|
||||
name=$(grep '^name = "' src/sdk/pyproject.toml | head -n 1 | sed 's/.*"\(.*\)".*/\1/')
|
||||
version=$(grep '^version = "' src/sdk/pyproject.toml | head -n 1 | sed 's/.*"\(.*\)".*/\1/')
|
||||
if [ "$name" != "langflow-sdk-nightly" ]; then
|
||||
echo "Name $name does not match langflow-sdk-nightly. Exiting the workflow."
|
||||
if [ "$name" != "langflow-sdk" ]; then
|
||||
echo "Name $name does not match langflow-sdk. Exiting the workflow."
|
||||
exit 1
|
||||
fi
|
||||
if [ "v$version" != "${{ inputs.nightly_tag_sdk }}" ]; then
|
||||
@ -94,10 +94,11 @@ jobs:
|
||||
id: verify
|
||||
run: |
|
||||
cd src/lfx
|
||||
name=$(uv tree | grep 'lfx' | head -n 1 | awk '{print $1}')
|
||||
version=$(uv tree | grep 'lfx' | head -n 1 | awk '{print $2}')
|
||||
if [ "$name" != "lfx-nightly" ]; then
|
||||
echo "Name $name does not match lfx-nightly. Exiting the workflow."
|
||||
# Root the tree at lfx; a bare `grep lfx` matches bundles like `lfx-ibm` first.
|
||||
name=$(uv tree --package lfx | head -n 1 | awk '{print $1}')
|
||||
version=$(uv tree --package lfx | head -n 1 | awk '{print $2}')
|
||||
if [ "$name" != "lfx" ]; then
|
||||
echo "Name $name does not match lfx. Exiting the workflow."
|
||||
exit 1
|
||||
fi
|
||||
if [ "$version" != "${{ inputs.nightly_tag_lfx }}" ]; then
|
||||
@ -188,12 +189,14 @@ jobs:
|
||||
- name: Verify Nightly Name and Version
|
||||
id: verify
|
||||
run: |
|
||||
name=$(uv tree | grep 'langflow-base' | awk '{print $2}' | head -n 1)
|
||||
version=$(uv tree | grep 'langflow-base' | awk '{print $3}' | head -n 1)
|
||||
# Strip extras from package name (e.g., "langflow-base-nightly[complete]" -> "langflow-base-nightly")
|
||||
# Root the tree at langflow-base; it prints as a top-level `langflow-base v<ver>` line,
|
||||
# so a bare `grep langflow-base` would read the version into $name and leave $version empty.
|
||||
name=$(uv tree --package langflow-base | head -n 1 | awk '{print $1}')
|
||||
version=$(uv tree --package langflow-base | head -n 1 | awk '{print $2}')
|
||||
# Strip extras from package name (e.g., "langflow-base[complete]" -> "langflow-base")
|
||||
name_without_extras=$(echo $name | sed 's/\[.*\]//')
|
||||
if [ "$name_without_extras" != "langflow-base-nightly" ]; then
|
||||
echo "Name $name_without_extras does not match langflow-base-nightly. Exiting the workflow."
|
||||
if [ "$name_without_extras" != "langflow-base" ]; then
|
||||
echo "Name $name_without_extras does not match langflow-base. Exiting the workflow."
|
||||
echo "skipped=true" >> $GITHUB_OUTPUT
|
||||
exit 1
|
||||
fi
|
||||
@ -297,10 +300,10 @@ jobs:
|
||||
- name: Verify Nightly Name and Version
|
||||
id: verify
|
||||
run: |
|
||||
name=$(uv tree | grep -E '^langflow(-nightly)?[[:space:]]' | awk '{print $1}')
|
||||
version=$(uv tree | grep -E '^langflow(-nightly)?[[:space:]]' | awk '{print $2}')
|
||||
if [ "$name" != "langflow-nightly" ]; then
|
||||
echo "Name $name does not match langflow-nightly. Exiting the workflow."
|
||||
name=$(uv tree | grep -E '^langflow[[:space:]]' | awk '{print $1}')
|
||||
version=$(uv tree | grep -E '^langflow[[:space:]]' | awk '{print $2}')
|
||||
if [ "$name" != "langflow" ]; then
|
||||
echo "Name $name does not match langflow. Exiting the workflow."
|
||||
exit 1
|
||||
fi
|
||||
if [ "$version" != "${{ inputs.nightly_tag_release }}" ]; then
|
||||
@ -337,9 +340,9 @@ jobs:
|
||||
- name: Prepare Langflow Main artifact
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
MAIN_WHEELS=(dist/langflow_nightly-*.whl)
|
||||
MAIN_WHEELS=(dist/langflow-*.whl)
|
||||
if [ ${#MAIN_WHEELS[@]} -ne 1 ]; then
|
||||
echo "Expected exactly one langflow-nightly wheel in dist/, found ${#MAIN_WHEELS[@]}"
|
||||
echo "Expected exactly one langflow wheel in dist/, found ${#MAIN_WHEELS[@]}"
|
||||
ls -la dist/
|
||||
exit 1
|
||||
fi
|
||||
@ -347,23 +350,9 @@ jobs:
|
||||
mkdir -p main-dist
|
||||
cp "${MAIN_WHEELS[0]}" main-dist/
|
||||
|
||||
- name: Build bundle wheels for cross-platform test
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
bundles=(src/bundles/*/pyproject.toml)
|
||||
if [ ${#bundles[@]} -eq 0 ]; then
|
||||
echo "No bundles found under src/bundles/*/"
|
||||
exit 1
|
||||
fi
|
||||
rm -rf bundles-dist
|
||||
mkdir -p bundles-dist
|
||||
BUNDLES_DIST="$PWD/bundles-dist"
|
||||
for bundle_pyproject in "${bundles[@]}"; do
|
||||
bundle_dir=$(dirname "$bundle_pyproject")
|
||||
echo "Building wheel for $bundle_dir"
|
||||
(cd "$bundle_dir" && uv build --wheel --out-dir "$BUNDLES_DIST")
|
||||
done
|
||||
ls -la bundles-dist/
|
||||
# Bundles are NOT rebuilt/republished for nightly. The stable `lfx-*` bundles on
|
||||
# PyPI work as-is against the canonical `lfx` pre-release (see src/bundles/NIGHTLY.md). The
|
||||
# cross-platform test self-builds the bundles from src/bundles for install coverage.
|
||||
|
||||
# PyPI publishing moved to after cross-platform testing
|
||||
|
||||
@ -373,12 +362,6 @@ jobs:
|
||||
name: dist-nightly-main
|
||||
path: main-dist
|
||||
|
||||
- name: Upload bundles artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: dist-nightly-bundles
|
||||
path: bundles-dist
|
||||
|
||||
test-cross-platform:
|
||||
name: Test Cross-Platform Installation
|
||||
needs: [build-nightly-lfx, build-nightly-base, build-nightly-main]
|
||||
@ -388,7 +371,6 @@ jobs:
|
||||
main-artifact-name: "dist-nightly-main"
|
||||
lfx-artifact-name: "dist-nightly-lfx"
|
||||
sdk-artifact-name: "dist-nightly-sdk"
|
||||
bundles-artifact-name: "dist-nightly-bundles"
|
||||
|
||||
publish-nightly-lfx:
|
||||
name: Publish LFX Nightly to PyPI
|
||||
@ -475,60 +457,10 @@ jobs:
|
||||
run: |
|
||||
make publish base=true
|
||||
|
||||
publish-nightly-bundles:
|
||||
name: Publish Langflow Bundles Nightly to PyPI
|
||||
# langflow-nightly pins each `lfx-<name>-nightly==<dev>` exactly, so the
|
||||
# bundles must be on PyPI before main is published (publish-nightly-main
|
||||
# gates on this job). They depend on `lfx-nightly`, so gate on the lfx
|
||||
# publish too (or skip it when lfx isn't being rebuilt).
|
||||
needs: [build-nightly-main, test-cross-platform, publish-nightly-lfx]
|
||||
if: ${{ always() && needs.build-nightly-main.result == 'success' && needs.test-cross-platform.result == 'success' && (needs.publish-nightly-lfx.result == 'success' || inputs.build_lfx == false) }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download bundles artifact
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
name: dist-nightly-bundles
|
||||
path: bundles-dist
|
||||
- name: Setup Environment
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: false
|
||||
python-version: "3.13"
|
||||
- name: Publish bundles to PyPI
|
||||
env:
|
||||
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }}
|
||||
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
wheels=(bundles-dist/*.whl)
|
||||
if [ ${#wheels[@]} -eq 0 ]; then
|
||||
echo "No bundle wheels to publish."
|
||||
exit 1
|
||||
fi
|
||||
failed=0
|
||||
for wheel in "${wheels[@]}"; do
|
||||
echo "Publishing $wheel"
|
||||
# Capture stderr so a re-run without a version bump is a no-op for
|
||||
# an already-published bundle instead of failing the whole job.
|
||||
if ! err=$(uv publish "$wheel" 2>&1); then
|
||||
echo "$err"
|
||||
if echo "$err" | grep -qiE 'already exists|file already exists|HTTP 400|duplicate'; then
|
||||
echo "Skipping $wheel: already published."
|
||||
else
|
||||
echo "Publish failed for $wheel"
|
||||
failed=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [ "$failed" = "1" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
publish-nightly-main:
|
||||
name: Publish Langflow Main Nightly to PyPI
|
||||
needs: [build-nightly-main, test-cross-platform, publish-nightly-base, publish-nightly-bundles]
|
||||
if: ${{ always() && needs.build-nightly-main.result == 'success' && needs.test-cross-platform.result == 'success' && needs.publish-nightly-base.result == 'success' && needs.publish-nightly-bundles.result == 'success' }}
|
||||
needs: [build-nightly-main, test-cross-platform, publish-nightly-base]
|
||||
if: ${{ always() && needs.build-nightly-main.result == 'success' && needs.test-cross-platform.result == 'success' && needs.publish-nightly-base.result == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
|
||||
56
.github/workflows/stress-tests.yml
vendored
Normal file
56
.github/workflows/stress-tests.yml
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
name: Stress Tests
|
||||
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
workflow_call:
|
||||
inputs:
|
||||
ref:
|
||||
description: "Branch or tag to test"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
jobs:
|
||||
telemetry-writes:
|
||||
name: Telemetry Writes (Postgres)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
env:
|
||||
POSTGRES_USER: langflow
|
||||
POSTGRES_PASSWORD: langflow
|
||||
POSTGRES_DB: langflow
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd="pg_isready -U langflow"
|
||||
--health-interval=10s
|
||||
--health-timeout=5s
|
||||
--health-retries=5
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.sha }}
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
python-version: "3.12"
|
||||
prune-cache: false
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync --extra postgresql
|
||||
|
||||
- name: Run stress test
|
||||
env:
|
||||
DB_URL: "postgresql+psycopg://langflow:langflow@localhost:5432/langflow" # pragma: allowlist secret
|
||||
LANGFLOW_TELEMETRY_WRITER_ENABLED: "true"
|
||||
run: |
|
||||
uv run python src/backend/tests/stress/stress_telemetry_writes.py \
|
||||
--concurrency 50 --seconds 15
|
||||
@ -66,6 +66,12 @@ repos:
|
||||
language: system
|
||||
files: ^src/backend/base/langflow/api/.*\.py$
|
||||
pass_filenames: false
|
||||
- id: component-env-writes
|
||||
name: Components must not write to os.environ (process-global; bleeds across requests)
|
||||
entry: python scripts/lint/check_component_env_writes.py
|
||||
language: system
|
||||
files: ^(src/lfx/src/lfx/components/.*\.py|src/backend/base/langflow/components/.*\.py|src/bundles/.*\.py)$
|
||||
pass_filenames: false
|
||||
- repo: https://github.com/Yelp/detect-secrets
|
||||
rev: v1.5.0
|
||||
hooks:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -105,7 +105,7 @@ Authorization is a pluggable layer separate from authentication:
|
||||
|
||||
Default is **off**: `LANGFLOW_AUTHZ_ENABLED=false`. When enabled with only the OSS stub registered, every check returns allow — the stub is a no-op so routes stay wired and audit rows still flow. Real allow/deny requires a registered authorization plugin.
|
||||
|
||||
Route guards live in `langflow.services.authorization.utils`:
|
||||
Route guards live in `langflow.services.authorization.guards` (the legacy `langflow.services.authorization.utils` path re-exports them for backward compatibility):
|
||||
- `ensure_flow_permission(user, FlowAction.*, flow_id=..., flow_user_id=..., workspace_id=..., folder_id=...)` — single-flow CRUD + execute
|
||||
- `ensure_deployment_permission(user, DeploymentAction.*, deployment_id=..., deployment_user_id=..., workspace_id=..., project_id=...)`
|
||||
- `ensure_project_permission(user, ProjectAction.*, project_id=..., project_user_id=..., workspace_id=...)`
|
||||
@ -121,13 +121,13 @@ The enforcement request shape is `(subject, domain, object, action)`:
|
||||
- object = `flow:{uuid}` / `deployment:{uuid}` / `project:{uuid}` / `flow:*` / etc.
|
||||
- action = `read` / `write` / `create` / `delete` / `execute` / `deploy`
|
||||
|
||||
**Share-aware fetch (Phase 3):** route fetch helpers (`_read_flow`, `get_flow_by_id_or_endpoint_name`, `get_deployment`, project reads in `projects.py`, v2 file fetcher) branch on `BaseAuthorizationService.supports_cross_user_fetch()`. The OSS pass-through reports `False` so the existing owner-scoped queries are preserved — enabling `LANGFLOW_AUTHZ_ENABLED=true` without a registered plugin cannot widen visibility. Plugins set `SUPPORTS_CROSS_USER_FETCH=True` so resources load by id alone and `ensure_*_permission` decides access; route handlers can convert a plugin-deny `HTTPException(403)` to `HTTPException(404)` via `langflow.services.authorization.fetch.deny_to_404` to preserve UUID privacy.
|
||||
**Share-aware fetch (Phase 3):** route fetch helpers (`_read_flow`, `get_flow_by_id_or_endpoint_name`, `get_deployment`, project reads in `projects.py`, v2 file fetcher, variable PATCH/DELETE in `variable.py`) branch on `BaseAuthorizationService.supports_cross_user_fetch()`. The OSS pass-through reports `False` so the existing owner-scoped queries are preserved — enabling `LANGFLOW_AUTHZ_ENABLED=true` without a registered plugin cannot widen visibility. Plugins set `SUPPORTS_CROSS_USER_FETCH=True` so resources load by id alone and `ensure_*_permission` decides access; route handlers can convert a plugin-deny `HTTPException(403)` to `HTTPException(404)` via `langflow.services.authorization.fetch.deny_to_404` to preserve UUID privacy.
|
||||
|
||||
**Share CRUD API (Phase 3):** `/api/v1/authz/shares` provides POST / GET / PATCH / DELETE on `authz_share` rows. The handler enforces an OSS floor (resource owner or superuser may administer shares for that resource) so the OSS pass-through cannot let a non-owner mint share rows. Each write fires `BaseAuthorizationService.invalidate_user` / `invalidate_all` so a registered enforcer can drop cached policy. Audit rows are written via `audit_decision` with `share:create` / `share:update` / `share:delete` actions.
|
||||
|
||||
**Audit query API (Phase 4):** `GET /api/v1/authz/audit` (superuser-only) exposes a paginated, filterable view of `authz_audit_log`. Supports `user_id`, `resource_type`, `resource_id`, `action`, `result`, `since`, `until` filters; page size capped at 200.
|
||||
|
||||
**Default role catalog (Phase 4):** the seed migration `8d3a1f9c2e0b_seed_authz_system_roles` inserts the three built-in `is_system=True` roles (viewer / developer / admin) with `"{resource}:{action}"` permission slugs. OSS does not interpret these — they exist so a registered plugin's policy sync has a stable bootstrap source.
|
||||
**Default role catalog (Phase 4):** the consolidated foundations migration `7c8d9e0f1a2b_authz_foundations` seeds the three built-in `is_system=True` roles (viewer / developer / admin) with `"{resource}:{action}"` permission slugs. OSS does not interpret these — they exist so a registered plugin's policy sync has a stable bootstrap source.
|
||||
|
||||
## Component Development
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
# CLAUDE.md
|
||||
|
||||
@AGENTS.md
|
||||
@.claude/CLAUDE.md
|
||||
|
||||
This project uses [AGENTS.md](https://agents.md/) as the standard for providing context to AI coding agents. The `@AGENTS.md` import above tells Claude Code to load `AGENTS.md` automatically; other tools that natively support `AGENTS.md` will pick it up directly.
|
||||
This project uses [AGENTS.md](https://agents.md/) as the standard for providing context to AI coding agents. The `@AGENTS.md` import above tells Claude Code to load `AGENTS.md` automatically; other tools that natively support `AGENTS.md` will pick it up directly. The `@.claude/CLAUDE.md` import loads the local hard-rules file (gitignored) that mirrors the PostToolUse hook policy.
|
||||
|
||||
24
Makefile
24
Makefile
@ -568,20 +568,29 @@ patch: ## Update version across all projects. Usage: make patch v=1.5.0
|
||||
\
|
||||
echo "$(GREEN)Langflow version: $$LANGFLOW_VERSION$(NC)"; \
|
||||
echo "$(GREEN)Langflow-base version: $$LANGFLOW_BASE_VERSION$(NC)"; \
|
||||
echo "$(GREEN)LFX (synced): $$LANGFLOW_VERSION$(NC)"; \
|
||||
\
|
||||
echo "$(GREEN)Updating main pyproject.toml...$(NC)"; \
|
||||
python -c "import re; fname='pyproject.toml'; txt=open(fname).read(); txt=re.sub(r'^version = \".*\"', 'version = \"$$LANGFLOW_VERSION\"', txt, flags=re.MULTILINE); txt=re.sub(r'\"langflow-base==.*\"', '\"langflow-base==$$LANGFLOW_BASE_VERSION\"', txt); open(fname, 'w').write(txt)"; \
|
||||
python -c "import re; fname='pyproject.toml'; txt=open(fname).read(); txt=re.sub(r'^version = \".*\"', 'version = \"$$LANGFLOW_VERSION\"', txt, flags=re.MULTILINE); txt=re.sub(r'\"langflow-base(?:\[[^\]]*\])?(?:==|>=|~=)[^\"]*\"', '\"langflow-base[complete]>=$$LANGFLOW_BASE_VERSION\"', txt); open(fname, 'w').write(txt)"; \
|
||||
\
|
||||
echo "$(GREEN)Updating langflow-base pyproject.toml...$(NC)"; \
|
||||
python -c "import re; fname='src/backend/base/pyproject.toml'; txt=open(fname).read(); txt=re.sub(r'^version = \".*\"', 'version = \"$$LANGFLOW_BASE_VERSION\"', txt, flags=re.MULTILINE); open(fname, 'w').write(txt)"; \
|
||||
python -c "import re; fname='src/backend/base/pyproject.toml'; txt=open(fname).read(); txt=re.sub(r'^version = \".*\"', 'version = \"$$LANGFLOW_BASE_VERSION\"', txt, flags=re.MULTILINE); txt=re.sub(r'\"lfx(?:~=|>=)[^\"]*\"', '\"lfx~=$$LANGFLOW_VERSION\"', txt); open(fname, 'w').write(txt)"; \
|
||||
\
|
||||
echo "$(GREEN)Updating lfx pyproject.toml...$(NC)"; \
|
||||
python -c "import re; fname='src/lfx/pyproject.toml'; txt=open(fname).read(); txt=re.sub(r'^version = \".*\"', 'version = \"$$LANGFLOW_VERSION\"', txt, flags=re.MULTILINE); open(fname, 'w').write(txt)"; \
|
||||
\
|
||||
echo "$(GREEN)Syncing bundle lfx pins (src/bundles/*) -> $$LANGFLOW_VERSION...$(NC)"; \
|
||||
python scripts/ci/sync_bundle_lfx_pin.py "$$LANGFLOW_VERSION"; \
|
||||
\
|
||||
echo "$(GREEN)Updating frontend package.json...$(NC)"; \
|
||||
python -c "import re; fname='src/frontend/package.json'; txt=open(fname).read(); txt=re.sub(r'\"version\": \".*\"', '\"version\": \"$$LANGFLOW_VERSION\"', txt); open(fname, 'w').write(txt)"; \
|
||||
\
|
||||
echo "$(GREEN)Validating version changes...$(NC)"; \
|
||||
if ! grep -q "^version = \"$$LANGFLOW_VERSION\"" pyproject.toml; then echo "$(RED)✗ Main pyproject.toml version validation failed$(NC)"; exit 1; fi; \
|
||||
if ! grep -q "\"langflow-base==$$LANGFLOW_BASE_VERSION\"" pyproject.toml; then echo "$(RED)✗ Main pyproject.toml langflow-base dependency validation failed$(NC)"; exit 1; fi; \
|
||||
if ! grep -qF "\"langflow-base[complete]>=$$LANGFLOW_BASE_VERSION\"" pyproject.toml; then echo "$(RED)✗ Main pyproject.toml langflow-base dependency validation failed$(NC)"; exit 1; fi; \
|
||||
if ! grep -q "^version = \"$$LANGFLOW_BASE_VERSION\"" src/backend/base/pyproject.toml; then echo "$(RED)✗ Langflow-base pyproject.toml version validation failed$(NC)"; exit 1; fi; \
|
||||
if ! grep -q "\"lfx~=$$LANGFLOW_VERSION\"" src/backend/base/pyproject.toml; then echo "$(RED)✗ Langflow-base pyproject.toml lfx pin validation failed$(NC)"; exit 1; fi; \
|
||||
if ! grep -q "^version = \"$$LANGFLOW_VERSION\"" src/lfx/pyproject.toml; then echo "$(RED)✗ LFX pyproject.toml version validation failed$(NC)"; exit 1; fi; \
|
||||
if ! grep -q "\"version\": \"$$LANGFLOW_VERSION\"" src/frontend/package.json; then echo "$(RED)✗ Frontend package.json version validation failed$(NC)"; exit 1; fi; \
|
||||
echo "$(GREEN)✓ All versions updated successfully$(NC)"; \
|
||||
\
|
||||
@ -592,13 +601,13 @@ patch: ## Update version across all projects. Usage: make patch v=1.5.0
|
||||
\
|
||||
echo "$(GREEN)Validating final state...$(NC)"; \
|
||||
CHANGED_FILES=$$(git status --porcelain | wc -l | tr -d ' '); \
|
||||
if [ "$$CHANGED_FILES" -lt 5 ]; then \
|
||||
echo "$(RED)✗ Expected at least 5 changed files, but found $$CHANGED_FILES$(NC)"; \
|
||||
if [ "$$CHANGED_FILES" -lt 6 ]; then \
|
||||
echo "$(RED)✗ Expected at least 6 changed files, but found $$CHANGED_FILES$(NC)"; \
|
||||
echo "$(RED)Changed files:$(NC)"; \
|
||||
git status --porcelain; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
EXPECTED_FILES="pyproject.toml uv.lock src/backend/base/pyproject.toml src/frontend/package.json src/frontend/package-lock.json"; \
|
||||
EXPECTED_FILES="pyproject.toml uv.lock src/backend/base/pyproject.toml src/lfx/pyproject.toml src/frontend/package.json src/frontend/package-lock.json"; \
|
||||
for file in $$EXPECTED_FILES; do \
|
||||
if ! git status --porcelain | grep -q "$$file"; then \
|
||||
echo "$(RED)✗ Expected file $$file was not modified$(NC)"; \
|
||||
@ -610,7 +619,8 @@ patch: ## Update version across all projects. Usage: make patch v=1.5.0
|
||||
echo "$(GREEN)Version update complete!$(NC)"; \
|
||||
echo "$(GREEN)Updated files:$(NC)"; \
|
||||
echo " - pyproject.toml: $$LANGFLOW_VERSION"; \
|
||||
echo " - src/backend/base/pyproject.toml: $$LANGFLOW_BASE_VERSION"; \
|
||||
echo " - src/backend/base/pyproject.toml: $$LANGFLOW_BASE_VERSION (lfx pin → $$LANGFLOW_VERSION)"; \
|
||||
echo " - src/lfx/pyproject.toml: $$LANGFLOW_VERSION"; \
|
||||
echo " - src/frontend/package.json: $$LANGFLOW_VERSION"; \
|
||||
echo " - uv.lock: dependency lock updated"; \
|
||||
echo " - src/frontend/package-lock.json: dependency lock updated"; \
|
||||
|
||||
37
RELEASE.md
37
RELEASE.md
@ -132,6 +132,43 @@ git merge --ff-only release-X.Y.Z # Fast-forward main to include RC changes
|
||||
* Duplicate tags (e.g., both `1.8.3` and `v1.8.3`) cause GitHub's release notes generation to use the wrong base comparison, resulting in incomplete changelogs.
|
||||
* The workflow automatically checks for and prevents duplicate tags.
|
||||
|
||||
## LFX Compatibility
|
||||
|
||||
Langflow and LFX share a **major.minor version line**. The compatibility contract is:
|
||||
|
||||
> **LFX X.Y.N is guaranteed compatible with any Flow exported from Langflow X.Y.M.**
|
||||
|
||||
Patch releases (`N` and `M`) are independent — a patch to LFX does not require a Langflow patch release, and vice versa.
|
||||
|
||||
### Version management
|
||||
|
||||
`make patch v=X.Y.Z` updates all four artifacts together:
|
||||
|
||||
| Artifact | Version set |
|
||||
|---|---|
|
||||
| `langflow` | `X.Y.Z` |
|
||||
| `langflow-base` | `0.Y.Z` |
|
||||
| `lfx` | `X.Y.Z` |
|
||||
| frontend | `X.Y.Z` |
|
||||
|
||||
### Cutting an LFX patch release
|
||||
|
||||
Use `scripts/release-lfx.sh <version>`. The script warns if the LFX minor version does not match the current Langflow minor version, which would violate the compatibility contract. A warning is not a hard block — patch-only LFX releases within the same minor are expected and fine.
|
||||
|
||||
### Implications for users
|
||||
|
||||
Users can pin `lfx~=X.Y.0` in their `requirements.txt` to receive all compatible LFX patch releases for a given Langflow minor.
|
||||
|
||||
### Migrating from lfx 0.5.x to 1.10.0
|
||||
|
||||
LFX was realigned from its standalone `0.5.x` line onto Langflow's `major.minor` line, so the version jumps from `0.5.0` to `1.10.0` in a single step. This is a version-numbering change, not 95 minors of feature churn. The jump affects downstream pins, and neither pip nor uv will flag it — so it must be called out in the release announcement, not just here:
|
||||
|
||||
- `lfx==0.5.x` or `lfx<1.0` pins will **not** upgrade (intentional — those deployments stay put).
|
||||
- `lfx>=0.5,<1` pins will **not** upgrade.
|
||||
- `lfx>=0.5` with no upper bound **will** pull `1.10.0` on the next install — a major jump with no warning.
|
||||
|
||||
Going forward, pin `lfx~=X.Y.0` (e.g. `lfx~=1.10.0`) so you track compatible patches for a given Langflow minor without silently crossing minor lines.
|
||||
|
||||
## Roles
|
||||
|
||||
| Role | Responsibility |
|
||||
|
||||
92
deploy/observability/grafana-loki/README.md
Normal file
92
deploy/observability/grafana-loki/README.md
Normal file
@ -0,0 +1,92 @@
|
||||
# Langflow on Grafana + Loki
|
||||
|
||||
Reference stack that ingests Langflow's structured JSON logs into [Loki](https://grafana.com/oss/loki/) and visualizes them with a pre-provisioned Grafana dashboard.
|
||||
|
||||
Use this as a starting point. The compose file, Promtail config, and dashboard JSON are independent of the rest of `deploy/` and can be lifted into any environment.
|
||||
|
||||
## What you get
|
||||
|
||||
- **Loki 3.2** on `:3100`
|
||||
- **Promtail 3.2** scraping a directory of Langflow log files
|
||||
- **Grafana 11.3** on `:3000` with the Loki datasource and the `Langflow Logs` dashboard already provisioned
|
||||
|
||||
## Prerequisites on the Langflow side
|
||||
|
||||
The dashboard expects Langflow to be running in JSON mode with service metadata set. At minimum:
|
||||
|
||||
```bash
|
||||
LANGFLOW_LOG_ENV=container
|
||||
LANGFLOW_LOG_FILE=/absolute/path/to/langflow/logs/langflow.log
|
||||
LANGFLOW_SERVICE_NAME=langflow
|
||||
LANGFLOW_VERSION=1.10.0
|
||||
LANGFLOW_ENVIRONMENT=production
|
||||
```
|
||||
|
||||
Promtail scrapes a directory of `*.log` files, so `LANGFLOW_LOG_FILE` must point at a file inside
|
||||
the directory you expose to Promtail as `LANGFLOW_LOG_DIR` (see [Run](#run)). Set both to the same
|
||||
directory, otherwise Promtail watches an empty folder and the dashboard stays blank. Use an
|
||||
absolute path: `LANGFLOW_LOG_FILE` is resolved against Langflow's working directory, not this one.
|
||||
|
||||
In JSON mode the file is a single JSON stream: application logs and third-party stdlib loggers
|
||||
(`uvicorn`, `sqlalchemy`, `httpx`, `langchain`) are all rendered as JSON and run through PII
|
||||
redaction, so the `json` parse stage and the **Stdlib intercept routing** panel work against it
|
||||
directly. This stack scrapes a file, so `LANGFLOW_LOG_FILE` is required. If you instead run
|
||||
Langflow as a container, you can drop the file and scrape its stdout by swapping Promtail's
|
||||
`static_configs` file target for `docker_sd_configs` (same JSON, same labels).
|
||||
|
||||
See [Logs and observability](../../../docs/docs/Develop/observability-grafana-loki.mdx) for the full list of environment variables (per-logger overrides, extra PII redaction keys, trace correlation, etc.).
|
||||
|
||||
## Run
|
||||
|
||||
From this directory:
|
||||
|
||||
```bash
|
||||
# Point Promtail at the directory that holds the file you set in
|
||||
# LANGFLOW_LOG_FILE above. Must be the same directory. Defaults to the
|
||||
# bundled ./logs (used by the quick smoke test below).
|
||||
export LANGFLOW_LOG_DIR=/absolute/path/to/langflow/logs
|
||||
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Then open [http://localhost:3000/d/langflow-prod-logs](http://localhost:3000/d/langflow-prod-logs). Default credentials are `admin` / `admin` (override with `GF_ADMIN_USER` and `GF_ADMIN_PASSWORD`).
|
||||
|
||||
To stop:
|
||||
|
||||
```bash
|
||||
docker compose down -v
|
||||
```
|
||||
|
||||
### Quick smoke test (no Langflow required)
|
||||
|
||||
To verify the stack end to end without running Langflow, write a sample record into the bundled
|
||||
`./logs` directory and query Loki directly:
|
||||
|
||||
```bash
|
||||
mkdir -p logs
|
||||
echo '{"event":"smoke test","level":"info","logger":"langflow.api.run","timestamp":"2026-06-01T00:00:00Z","service":"langflow","environment":"production","version":"1.10.0"}' >> logs/langflow.log
|
||||
|
||||
docker compose up -d
|
||||
|
||||
# Give Promtail a few seconds to tail the file, then confirm the line reached Loki:
|
||||
sleep 5
|
||||
curl -sG 'http://localhost:3100/loki/api/v1/query_range' --data-urlencode 'query={job="langflow"}' | grep -q "smoke test" && echo "OK: log reached Loki"
|
||||
```
|
||||
|
||||
## What each dashboard panel proves
|
||||
|
||||
| Panel | LogQL it runs |
|
||||
|---|---|
|
||||
| **PII leak count (must be 0)** | `sum(count_over_time({job="langflow"} \|~ "sk-do-not-leak\|hunter2\|Bearer xyz" [$__range]))` |
|
||||
| **Errors with structured tracebacks** | `{job="langflow", level=~"error\|critical"} \|= "exception" \| json` |
|
||||
| **Redaction proof** | `{job="langflow"} \|~ "\\*\\*\\*"` |
|
||||
| **Stdlib intercept routing** | `{job="langflow", logger=~"uvicorn.*\|sqlalchemy.*\|httpx.*\|langchain.*"}` |
|
||||
| **Service / environment / version coverage** | `sum by (service, environment, version) (count_over_time({job="langflow"}[$__range]))` |
|
||||
| **Log rate by level** | `sum by (level) (rate({job="langflow"}[1m]))` |
|
||||
| **Log rate by logger** | `topk(10, sum by (logger) (rate({job="langflow"}[1m])))` |
|
||||
|
||||
## Notes
|
||||
|
||||
- Promtail only promotes `level`, `service`, `environment`, `version`, `logger` to labels. High-cardinality fields (`user_id`, `flow_id`, `trace_id`) stay in the log body — query them with `| json` in LogQL.
|
||||
- Replace Promtail with [Grafana Alloy](https://grafana.com/oss/alloy/) if you already standardize on it; the JSON parse stage maps 1:1.
|
||||
- If your runtime ships logs through a different transport (Fluent Bit, Vector, OTLP), only the scrape side changes — the dashboard and label schema stay the same.
|
||||
41
deploy/observability/grafana-loki/docker-compose.yml
Normal file
41
deploy/observability/grafana-loki/docker-compose.yml
Normal file
@ -0,0 +1,41 @@
|
||||
services:
|
||||
loki:
|
||||
image: grafana/loki:3.2.0
|
||||
container_name: lf-loki
|
||||
ports:
|
||||
- "3100:3100"
|
||||
command: -config.file=/etc/loki/local-config.yaml
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3100/ready"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 20
|
||||
|
||||
promtail:
|
||||
image: grafana/promtail:3.2.0
|
||||
container_name: lf-promtail
|
||||
volumes:
|
||||
- ./promtail/config.yml:/etc/promtail/config.yml
|
||||
# Mount the directory that holds your langflow JSON log file.
|
||||
# Override LANGFLOW_LOG_DIR to point at your real log location.
|
||||
- ${LANGFLOW_LOG_DIR:-./logs}:/var/log/langflow:ro
|
||||
command: -config.file=/etc/promtail/config.yml
|
||||
depends_on:
|
||||
loki:
|
||||
condition: service_healthy
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:11.3.0
|
||||
container_name: lf-grafana
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- GF_SECURITY_ADMIN_USER=${GF_ADMIN_USER:-admin}
|
||||
- GF_SECURITY_ADMIN_PASSWORD=${GF_ADMIN_PASSWORD:-admin}
|
||||
- GF_USERS_DEFAULT_THEME=dark
|
||||
volumes:
|
||||
- ./grafana/provisioning:/etc/grafana/provisioning
|
||||
- ./grafana/dashboards:/var/lib/grafana/dashboards
|
||||
depends_on:
|
||||
loki:
|
||||
condition: service_healthy
|
||||
@ -0,0 +1,163 @@
|
||||
{
|
||||
"uid": "langflow-prod-logs",
|
||||
"title": "Langflow Logs",
|
||||
"description": "Structured logs from Langflow and lfx: tracebacks, PII redaction, service labels, stdlib intercept, trace correlation.",
|
||||
"tags": ["langflow", "lfx", "structured-logs"],
|
||||
"timezone": "browser",
|
||||
"schemaVersion": 39,
|
||||
"version": 1,
|
||||
"refresh": "5s",
|
||||
"time": {
|
||||
"from": "now-15m",
|
||||
"to": "now"
|
||||
},
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"name": "environment",
|
||||
"type": "query",
|
||||
"datasource": "Loki",
|
||||
"query": "label_values({job=\"langflow\"}, environment)",
|
||||
"current": {"text": "All", "value": "$__all"},
|
||||
"includeAll": true,
|
||||
"multi": true,
|
||||
"refresh": 2
|
||||
},
|
||||
{
|
||||
"name": "service",
|
||||
"type": "query",
|
||||
"datasource": "Loki",
|
||||
"query": "label_values({job=\"langflow\"}, service)",
|
||||
"current": {"text": "All", "value": "$__all"},
|
||||
"includeAll": true,
|
||||
"multi": true,
|
||||
"refresh": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
"panels": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "stat",
|
||||
"title": "Total log lines (window)",
|
||||
"gridPos": {"h": 4, "w": 6, "x": 0, "y": 0},
|
||||
"datasource": "Loki",
|
||||
"targets": [
|
||||
{"expr": "sum(count_over_time({job=\"langflow\", service=~\"$service\", environment=~\"$environment\"}[$__range]))", "refId": "A"}
|
||||
],
|
||||
"options": {"reduceOptions": {"calcs": ["lastNotNull"]}}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "stat",
|
||||
"title": "Errors (window)",
|
||||
"gridPos": {"h": 4, "w": 6, "x": 6, "y": 0},
|
||||
"datasource": "Loki",
|
||||
"fieldConfig": {"defaults": {"color": {"mode": "thresholds"}, "thresholds": {"steps": [{"color": "green", "value": null}, {"color": "red", "value": 1}]}}},
|
||||
"targets": [
|
||||
{"expr": "sum(count_over_time({job=\"langflow\", level=~\"error|critical\", service=~\"$service\", environment=~\"$environment\"}[$__range]))", "refId": "A"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "stat",
|
||||
"title": "Distinct loggers",
|
||||
"gridPos": {"h": 4, "w": 6, "x": 12, "y": 0},
|
||||
"datasource": "Loki",
|
||||
"targets": [
|
||||
{"expr": "count(count by (logger)({job=\"langflow\", service=~\"$service\", environment=~\"$environment\"}))", "refId": "A"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "stat",
|
||||
"title": "PII leak count (must be 0)",
|
||||
"description": "Counts log lines that contain literal sensitive values that should have been redacted. Anything non-zero means redaction failed.",
|
||||
"gridPos": {"h": 4, "w": 6, "x": 18, "y": 0},
|
||||
"datasource": "Loki",
|
||||
"fieldConfig": {"defaults": {"color": {"mode": "thresholds"}, "thresholds": {"steps": [{"color": "green", "value": null}, {"color": "red", "value": 1}]}}},
|
||||
"targets": [
|
||||
{"expr": "sum(count_over_time({job=\"langflow\"} |~ \"sk-do-not-leak|hunter2|Bearer xyz\"[$__range]))", "refId": "A"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "timeseries",
|
||||
"title": "Log rate by level",
|
||||
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 4},
|
||||
"datasource": "Loki",
|
||||
"targets": [
|
||||
{"expr": "sum by (level) (rate({job=\"langflow\", service=~\"$service\", environment=~\"$environment\"}[1m]))", "refId": "A", "legendFormat": "{{level}}"}
|
||||
],
|
||||
"options": {"legend": {"displayMode": "table", "placement": "right", "calcs": ["sum"]}}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "timeseries",
|
||||
"title": "Log rate by logger (top 10)",
|
||||
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 4},
|
||||
"datasource": "Loki",
|
||||
"targets": [
|
||||
{"expr": "topk(10, sum by (logger) (rate({job=\"langflow\", service=~\"$service\", environment=~\"$environment\"}[1m])))", "refId": "A", "legendFormat": "{{logger}}"}
|
||||
],
|
||||
"options": {"legend": {"displayMode": "table", "placement": "right", "calcs": ["sum"]}}
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": "logs",
|
||||
"title": "Structured tracebacks (errors with exception array)",
|
||||
"description": "Every error in JSON mode now ships a structured exception array (exc_type, exc_value, frames) instead of being silently dropped. Expand a row to see the structure.",
|
||||
"gridPos": {"h": 12, "w": 24, "x": 0, "y": 12},
|
||||
"datasource": "Loki",
|
||||
"targets": [
|
||||
{"expr": "{job=\"langflow\", level=~\"error|critical\", service=~\"$service\", environment=~\"$environment\"} |= \"exception\" | json", "refId": "A"}
|
||||
],
|
||||
"options": {"showLabels": true, "showTime": true, "wrapLogMessage": true, "prettifyLogMessage": true, "dedupStrategy": "none"}
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": "logs",
|
||||
"title": "PII redaction proof (look for *** sentinels, not the raw value)",
|
||||
"description": "Sensitive keys (password, api_key, token, authorization, cookie) appear as *** before the JSON ever leaves the process.",
|
||||
"gridPos": {"h": 10, "w": 12, "x": 0, "y": 24},
|
||||
"datasource": "Loki",
|
||||
"targets": [
|
||||
{"expr": "{job=\"langflow\", service=~\"$service\"} |~ \"\\\\*\\\\*\\\\*\"", "refId": "A"}
|
||||
],
|
||||
"options": {"showLabels": false, "showTime": true, "wrapLogMessage": true, "prettifyLogMessage": true}
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"type": "logs",
|
||||
"title": "Stdlib intercept: third-party loggers routed into JSON",
|
||||
"description": "uvicorn, sqlalchemy, httpx, langchain, asyncio used to emit unstructured text. With LANGFLOW_LOG_ENV=container they come through the same JSON stream with logger=<name>.",
|
||||
"gridPos": {"h": 10, "w": 12, "x": 12, "y": 24},
|
||||
"datasource": "Loki",
|
||||
"targets": [
|
||||
{"expr": "{job=\"langflow\", logger=~\"uvicorn.*|sqlalchemy.*|httpx.*|langchain.*|third_party.*\", service=~\"$service\"}", "refId": "A"}
|
||||
],
|
||||
"options": {"showLabels": true, "showTime": true, "wrapLogMessage": true, "prettifyLogMessage": true}
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"type": "table",
|
||||
"title": "Top error sources",
|
||||
"gridPos": {"h": 8, "w": 12, "x": 0, "y": 34},
|
||||
"datasource": "Loki",
|
||||
"targets": [
|
||||
{"expr": "topk(10, sum by (logger) (count_over_time({job=\"langflow\", level=~\"error|critical\"}[$__range])))", "refId": "A", "instant": true, "format": "table"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"type": "table",
|
||||
"title": "Service / environment / version coverage",
|
||||
"description": "Confirms every record carries the service metadata Grafana dashboards key off of.",
|
||||
"gridPos": {"h": 8, "w": 12, "x": 12, "y": 34},
|
||||
"datasource": "Loki",
|
||||
"targets": [
|
||||
{"expr": "sum by (service, environment, version) (count_over_time({job=\"langflow\"}[$__range]))", "refId": "A", "instant": true, "format": "table"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
apiVersion: 1
|
||||
providers:
|
||||
- name: langflow
|
||||
folder: ''
|
||||
type: file
|
||||
disableDeletion: false
|
||||
updateIntervalSeconds: 10
|
||||
allowUiUpdates: true
|
||||
options:
|
||||
path: /var/lib/grafana/dashboards
|
||||
@ -0,0 +1,10 @@
|
||||
apiVersion: 1
|
||||
datasources:
|
||||
- name: Loki
|
||||
type: loki
|
||||
access: proxy
|
||||
url: http://loki:3100
|
||||
isDefault: true
|
||||
editable: false
|
||||
jsonData:
|
||||
maxLines: 1000
|
||||
35
deploy/observability/grafana-loki/promtail/config.yml
Normal file
35
deploy/observability/grafana-loki/promtail/config.yml
Normal file
@ -0,0 +1,35 @@
|
||||
server:
|
||||
http_listen_port: 9080
|
||||
grpc_listen_port: 0
|
||||
|
||||
positions:
|
||||
filename: /tmp/positions.yaml
|
||||
|
||||
clients:
|
||||
- url: http://loki:3100/loki/api/v1/push
|
||||
|
||||
scrape_configs:
|
||||
- job_name: langflow
|
||||
static_configs:
|
||||
- targets:
|
||||
- localhost
|
||||
labels:
|
||||
job: langflow
|
||||
__path__: /var/log/langflow/*.log
|
||||
pipeline_stages:
|
||||
# Parse the structlog JSON line and promote useful fields to labels.
|
||||
# Keep this set small; high-cardinality fields (user_id, flow_id,
|
||||
# trace_id) should stay in the log body and be queried with `| json`.
|
||||
- json:
|
||||
expressions:
|
||||
level: level
|
||||
service: service
|
||||
environment: environment
|
||||
version: version
|
||||
logger: logger
|
||||
- labels:
|
||||
level:
|
||||
service:
|
||||
environment:
|
||||
version:
|
||||
logger:
|
||||
@ -29,13 +29,17 @@ RUN apt-get update \
|
||||
# deps for building python deps
|
||||
build-essential \
|
||||
git \
|
||||
# npm
|
||||
npm \
|
||||
# gcc
|
||||
gcc \
|
||||
curl \
|
||||
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
||||
&& apt-get install -y nodejs \
|
||||
&& ARCH=$(dpkg --print-architecture) \
|
||||
&& if [ "$ARCH" = "amd64" ]; then NODE_ARCH="x64"; \
|
||||
elif [ "$ARCH" = "arm64" ]; then NODE_ARCH="arm64"; \
|
||||
else NODE_ARCH="$ARCH"; fi \
|
||||
&& NODE_VERSION="22.14.0" \
|
||||
&& curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${NODE_ARCH}.tar.xz" \
|
||||
| tar -xJ -C /usr/local --strip-components=1 \
|
||||
&& npm install -g npm@latest \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
@ -101,7 +105,8 @@ RUN ARCH=$(dpkg --print-architecture) \
|
||||
| head -1) \
|
||||
&& if [ -z "$NODE_VERSION" ]; then echo "ERROR: Could not determine Node.js version" && exit 1; fi \
|
||||
&& curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${NODE_ARCH}.tar.xz" \
|
||||
| tar -xJ -C /usr/local --strip-components=1
|
||||
| tar -xJ -C /usr/local --strip-components=1 \
|
||||
&& npm install -g npm@latest
|
||||
RUN useradd user -u 1000 -g 0 --no-create-home --home-dir /app/data
|
||||
|
||||
COPY --from=builder --chown=1000 /app/.venv /app/.venv
|
||||
|
||||
@ -30,12 +30,6 @@ RUN apt-get update \
|
||||
COPY ./src/backend ./src/backend
|
||||
COPY ./src/lfx ./src/lfx
|
||||
COPY ./src/sdk ./src/sdk
|
||||
# Workspace bundles (LE-1023 pilot+): each Bundle is shipped as a
|
||||
# separate distribution that langflow-base depends on by name (e.g.
|
||||
# ``lfx-duckduckgo``). Without copying the source tree, the install
|
||||
# below cannot resolve the path-based bundle deps and ends up with a
|
||||
# Langflow image missing components that previously lived in lfx.
|
||||
COPY ./src/bundles ./src/bundles
|
||||
|
||||
# Create venv and install langflow-base with dependencies
|
||||
# Using uv pip instead of uv sync to avoid workspace complexities
|
||||
@ -44,14 +38,15 @@ ENV PATH="/app/.venv/bin:$PATH"
|
||||
ENV VIRTUAL_ENV="/app/.venv"
|
||||
|
||||
# Install langflow-base with all extras except dev (which includes Playwright).
|
||||
# Each pilot-extracted bundle is installed alongside so the runtime image
|
||||
# keeps shipping the same component set users had before LE-1023.
|
||||
# This image ships the langflow-base core only. Extension bundles
|
||||
# (lfx-duckduckgo, lfx-arxiv, lfx-ibm, lfx-docling) are intentionally NOT
|
||||
# installed here -- they belong to the full ``langflow`` distribution, not
|
||||
# the lean core. Use the ``langflow`` image, or ``pip install`` the bundle
|
||||
# alongside this image, to add those components.
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
uv pip install \
|
||||
./src/sdk \
|
||||
./src/lfx \
|
||||
./src/bundles/duckduckgo \
|
||||
./src/bundles/arxiv \
|
||||
"./src/backend/base[complete,postgresql]"
|
||||
|
||||
################################
|
||||
|
||||
@ -74,28 +74,16 @@ RUN npm install \
|
||||
&& rm -rf /tmp/src/frontend
|
||||
|
||||
WORKDIR /app/src/backend/base
|
||||
# ``--extra duckduckgo`` pulls ``ddgs`` (the only dep the bundle adds on
|
||||
# top of langflow-base[complete]) at the version recorded in
|
||||
# ``uv.lock``. Routing the dep through the locked sync
|
||||
# instead of an ad-hoc ``uv pip install ddgs`` keeps the base image
|
||||
# reproducible across builds and prevents future ``ddgs`` releases from
|
||||
# silently drifting from the tested lock state.
|
||||
# langflow-base ships the core framework only. The extension bundles
|
||||
# (lfx-duckduckgo, lfx-arxiv, lfx-ibm, lfx-docling) are intentionally NOT
|
||||
# installed in this image: they are dependencies of the full ``langflow``
|
||||
# distribution, not of the lean ``langflow-base`` core, and we keep that
|
||||
# boundary at the image layer too. Consumers who want those components
|
||||
# should use the ``langflow`` image, or ``pip install`` the bundle (e.g.
|
||||
# ``lfx-duckduckgo``) alongside langflow-base.
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
RUSTFLAGS='--cfg reqwest_unstable' \
|
||||
uv sync --frozen --no-dev --no-editable --extra postgresql --extra duckduckgo
|
||||
|
||||
# Pilot Bundle re-attach (LE-1023): ``langflow-base`` no longer pulls in
|
||||
# DuckDuckGo (it moved to the standalone ``lfx-duckduckgo`` distribution
|
||||
# whose pyproject lives at ``src/bundles/duckduckgo``). The base image
|
||||
# was the user-facing path for that component before the move; install
|
||||
# the extracted bundle so the runtime image keeps the same component
|
||||
# set. ``--no-deps`` is intentional: the bundle's runtime deps (lfx,
|
||||
# langchain-community, ddgs) are now all in the langflow-base lockfile
|
||||
# above, so installing them here would yank duplicates that fight the
|
||||
# locked versions.
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
RUSTFLAGS='--cfg reqwest_unstable' \
|
||||
uv pip install --no-deps /app/src/bundles/duckduckgo
|
||||
uv sync --frozen --no-dev --no-editable --extra postgresql
|
||||
|
||||
################################
|
||||
# RUNTIME
|
||||
|
||||
79
docs/agents/ANTI-PATTERNS.md
Normal file
79
docs/agents/ANTI-PATTERNS.md
Normal file
@ -0,0 +1,79 @@
|
||||
# Anti-Patterns and Scar Tissue
|
||||
|
||||
These are battle scars. Every rule here cites a real recurring failure: a commit, a revert, or an explicit project lesson. Read this file when you're about to "just" change something — most of the don't/do rules below started as a one-line "small fix" that broke production.
|
||||
|
||||
## Don't / Do (with why)
|
||||
|
||||
1. **Don't add or rename a field on a component without regenerating starter projects AND the component index.**
|
||||
*Why:* commits `1a9f4548c`, `5987421bd`, `bbe1dad7c`, `69d29a4c6`, `67af71450`, `4f9beebc0` are all `fix:` commits cleaning up the same "I added a field and shipped" mistake. CI (`9dad1965c`) now syncs the index on label addition — that's a backstop, not a substitute.
|
||||
|
||||
2. **Don't rename a component class, display name, or module path.** If unavoidable, add the new component alongside the old one, set `legacy = True` + `replacement = ["<category>.<NewClassName>"]` on the old class, and update every supported version's `file_names_mapping` in tests.
|
||||
*Why:* class names key saved flows. There is no class-rename map in `setup.py` — the `type_migrations` map there only handles output type strings (`Data` → `JSON`, `DataFrame` → `Table`). See `2a7c56e84` (`IngestionDescriberComponent` → `FileDescriptionGeneratorComponent`) and `f5a0d52197` (starter project rename) for the cleanup churn. See [CONTRACTS.md](./CONTRACTS.md) for the full list.
|
||||
|
||||
3. **Don't mock at a different boundary than production calls.** If production uses `subprocess.Popen`, mock `Popen` — not `run`.
|
||||
*Why:* `a747135d1`, `bc433c375` — mocked tests passed; production failed. **Better: don't mock at all** — project policy is "Avoid mocking in tests." Use real integrations or `MockLanguageModel` for pure-logic LLM paths.
|
||||
|
||||
4. **Don't write a Graph test by poking internals.**
|
||||
*Why:* the canonical pattern is build → connect via `.set()` → call `async_start` → iterate results → validate. Tests that bypass this don't exercise the graph the way users do.
|
||||
|
||||
5. **Don't add a retry, broaden an except, or widen a type to make a symptom go away.**
|
||||
*Why:* "It is not a fix if you didn't have evidence of the change fixing something." See revert chain `9cdbf1b23`, `35aad2f2e`, `e3b90b7351` — fixes that masked rather than solved, then had to be reverted.
|
||||
|
||||
6. **Don't bump a dependency speculatively.** Confirm the conflict reproduces locally and the new version actually resolves it.
|
||||
*Why:* `cf659f0d0`, `ef6a303406`, `38d142a72`, `9029c4b61` — repeated dep-conflict cleanup.
|
||||
|
||||
7. **Don't invent model IDs, MCP tool names, or API surface.** Read the registry, the batch dispatcher, and `models.py` first.
|
||||
*Why:* `9f1402ed9` (`remove fictional gpt-5.3 ids`), `cecc4a6c2` (`expose layout tool as layout_flow to match batch dispatch`).
|
||||
|
||||
8. **Don't use raw `langflow run` / `python` when a `uv` command exists.** Always `uv run`. For lfx tests specifically: `uv sync` inside `src/lfx` (not `src/lfx/src/lfx`) to avoid pulling in langflow.
|
||||
*Why:* lfx must be testable without langflow installed; mixing them masks dependency leaks.
|
||||
|
||||
9. **Don't add a database migration without running `make alembic-upgrade` end to end** and `uv run pytest src/backend/tests/unit/test_database.py` sequentially.
|
||||
*Why:* `d8b9cc38f`, `c30150a2d`, `6585fe661`. `test_database.py` may pass in batch and fail individually — agents have stopped running it.
|
||||
|
||||
10. **Don't claim a race condition is fixed without an integration test that reproduced it.** Agent double-execution and queue leaks keep coming back.
|
||||
*Why:* `8a993ac90`, `42e84d0fd`, `749865b31`, `f30b291f8`.
|
||||
|
||||
11. **Don't add `tool_mode=True` to outputs that confuse agents** (e.g., dataframe surfaces).
|
||||
*Why:* `cbc54108` (`remove tool_mode=True for as_dataframe`), `35aad2f2e` (revert of the original disable). Output tool exposure is a user contract — don't toggle it back and forth.
|
||||
|
||||
12. **Don't widen the scope of a PR.** Closely related follow-ups belong in the existing open PR, not a new stacked PR.
|
||||
|
||||
13. **Don't ship without running the tests locally.** Typing the test command in the chat is not running it.
|
||||
|
||||
14. **Don't make incremental "wip" commits.** One coherent commit per logical change, after everything is done and tested.
|
||||
|
||||
15. **Don't push or open PRs without explicit user confirmation.** Pushing during active CI iteration on an already-open PR is fine; opening a new PR or pushing new branches is not.
|
||||
|
||||
## Things that look like a fix but aren't
|
||||
|
||||
- Adding `try/except Exception: pass` around a flaky call.
|
||||
- Adding a `time.sleep` to dodge a race instead of awaiting a condition.
|
||||
- Bumping a dependency version "just in case."
|
||||
- Marking a failing test `@pytest.mark.skip` without a linked issue.
|
||||
- Switching `# type: ignore` instead of fixing the type.
|
||||
- Reverting symptoms (a UI flag, a tool flag) instead of finding the root.
|
||||
- "Fixing" a test by changing its assertion to match wrong output.
|
||||
|
||||
## Before claiming done
|
||||
|
||||
- [ ] `make format_backend` and `make lint` clean.
|
||||
- [ ] Tests for the changed surface ran and passed locally — not just typed out.
|
||||
- [ ] If a component field/output changed: starter projects regenerated and `component_index.json` rebuilt.
|
||||
- [ ] If a class/module renamed: old class kept with `legacy=True` + `replacement=[...]`; `file_names_mapping` updated for every supported version.
|
||||
- [ ] No incremental "wip" commits. One coherent commit per logical change.
|
||||
- [ ] No "Generated with Claude Code" / `Co-Authored-By: Claude` trailers.
|
||||
- [ ] No `--no-verify` unless explicitly authorized.
|
||||
- [ ] PR not pushed and PR not opened until the user explicitly asks.
|
||||
- [ ] PR description has no test-plan checklist, no Jira links/IDs.
|
||||
|
||||
## Where to look for context — don't invent it
|
||||
|
||||
- **Starter projects:** `src/backend/base/langflow/initial_setup/starter_projects/`. Any field/output change must regenerate these.
|
||||
- **Component index:** `src/lfx/src/lfx/_assets/component_index.json` (rebuilt via the index sync workflow added in `9dad1965c`).
|
||||
- **Output type migrations:** `src/backend/base/langflow/initial_setup/setup.py` `type_migrations` map — handles renames of *output type strings* (e.g., `Data` → `JSON`). It does NOT remap component class names; for class renames use `legacy=True` + `replacement=[...]`.
|
||||
- **Version mapping for component tests:** `src/backend/tests/constants.py::SUPPORTED_VERSIONS` and each test's `file_names_mapping`.
|
||||
- **Release notes / changelog:** `docs/docs/Support/release-notes/` — check before claiming behavior is "new"; many features predate the current task.
|
||||
- **MCP tool catalog:** dispatched via the batch tool — names must match (`cecc4a6c2` is the canonical "I guessed the tool name" failure).
|
||||
- **Models / providers:** real model IDs come from the provider's listing endpoint or our adapter, never from training-data memory (`9f1402ed9`).
|
||||
- **Migrations:** `src/backend/base/langflow/alembic/versions/` — always read the latest two before adding a new one.
|
||||
81
docs/agents/ARCHITECTURE.md
Normal file
81
docs/agents/ARCHITECTURE.md
Normal file
@ -0,0 +1,81 @@
|
||||
# Architecture Boundaries
|
||||
|
||||
Langflow is three Python packages and one frontend, layered with one-way dependencies. Most "off-narrative" code is a boundary violation. Read this before adding a new file.
|
||||
|
||||
## Package dependency graph (one-way only)
|
||||
|
||||
```
|
||||
frontend (TS) ──HTTP──▶ langflow (api routers, integrations, distribution)
|
||||
│
|
||||
▼ may import
|
||||
langflow-base (services, graph, db, alembic)
|
||||
│
|
||||
▼ may import
|
||||
lfx (executor core, base primitives, components)
|
||||
│
|
||||
▼ may import
|
||||
langchain-core, pydantic, third-party SDKs
|
||||
```
|
||||
|
||||
### Dependency rules
|
||||
|
||||
- **`lfx` MUST NOT import `langflow.*` or `langflow-base.*`.** If lfx code needs a service (auth, db, flow lookup), define an interface inside `lfx` and inject the implementation from `langflow`. The repo currently has ~13 upward `from langflow.*` imports inside `src/lfx/src/lfx/components/...` (auth, db, helpers) — these are **known violations**. Do not add more; prefer fixing them.
|
||||
- **`langflow-base` MAY import `lfx`.** It MUST NOT import vendor-specific component modules from `langflow.components.<vendor>`.
|
||||
- **`frontend` talks to `langflow` only via HTTP/WebSocket.** No shared filesystem state.
|
||||
|
||||
## "Where does this code go?" decision tree
|
||||
|
||||
Walk top-down. Stop at the first match.
|
||||
|
||||
1. Is it framework-agnostic flow execution, base component classes, or `Component` primitives?
|
||||
→ `src/lfx/src/lfx/` (`base/` for shared primitives, `components/` for built-ins shipped with lfx).
|
||||
2. Is it a FastAPI route, auth, db model, alembic migration, or a lifecycle-managed singleton?
|
||||
→ `src/backend/base/langflow/` (`api/`, `services/X/`, `alembic/versions/`).
|
||||
3. Is it a vendor integration (OpenAI, Pinecone, Notion, …) — a `Component` subclass that wraps a third-party SDK?
|
||||
→ `src/lfx/src/lfx/components/<category>/` and update its `__init__.py` alphabetically. Never rename the class.
|
||||
4. Is it UI, state, or icons?
|
||||
→ `src/frontend/src/`. If it consumes a new API field, also update `src/frontend/src/types/`.
|
||||
5. Is it CLI behavior for `lfx run` / `lfx serve`?
|
||||
→ `src/lfx/src/lfx/cli/`.
|
||||
6. Is it a SQLAlchemy/SQLModel model change?
|
||||
→ `services/database/models/` AND `make alembic-revision message="..."` AND apply with `make alembic-upgrade`.
|
||||
7. Is it a flow JSON schema change?
|
||||
→ STOP. Existing saved flows must keep loading. Add a version mapping; do not mutate the existing shape. See [CONTRACTS.md](./CONTRACTS.md).
|
||||
8. Is it shared by both `lfx` and `langflow-base`?
|
||||
→ `src/lfx/src/lfx/base/`, never `langflow/base/`.
|
||||
|
||||
## Dependency direction — bad/good examples
|
||||
|
||||
- **Bad:** `from langflow.services.deps import session_scope` inside `src/lfx/...`.
|
||||
**Good:** Define `lfx.interfaces.SessionProvider`, accept it as a constructor arg; `langflow` wires the concrete `session_scope` at startup.
|
||||
- **Bad:** `from langflow.components.openai import ...` inside `langflow-base` core (`api/`, `services/`, `graph/`).
|
||||
**Good:** Components are loaded dynamically via the component registry; core code references `Component` only.
|
||||
- **Bad:** A new `MyHelperService` that's just functions.
|
||||
**Good:** Utility functions go in `langflow/helpers/` or `lfx/utils/`. A service inherits from `services/base.Service` and is registered via `services/factory.py`.
|
||||
|
||||
## API change protocol
|
||||
|
||||
- **`api/v1/`** is the live, stable surface (~25 routers). Existing v1 endpoints MUST stay backwards-compatible: only additive fields, never rename or remove.
|
||||
- **`api/v2/`** is the **active redesign surface** (`files`, `mcp`, `registration`, `workflow`) — both are mounted at runtime in `api/router.py`. v2 is **not** "future"; the old cursor rule was wrong.
|
||||
- Add a new endpoint to v2 only if it (a) replaces a v1 endpoint with a breaking shape change, or (b) belongs to one of the four v2 domains. Otherwise extend v1 additively.
|
||||
- A breaking change to a v1 endpoint is forbidden. Add a v2 sibling and leave v1 in place.
|
||||
|
||||
## Cross-cutting change protocol
|
||||
|
||||
A change that touches a request/response shape MUST update three places in the same PR:
|
||||
|
||||
1. The pydantic model in `langflow/api/v{1,2}/schemas.py` (or the route's local schema).
|
||||
2. The TypeScript type in `src/frontend/src/types/` consumed by the affected page/store. There is no OpenAPI generator — the types are hand-maintained, so the frontend silently breaks at runtime if you skip this.
|
||||
3. If the field is persisted: a new alembic revision (`make alembic-revision message=...`) AND a flow-JSON version mapping if the shape lives inside saved flows.
|
||||
|
||||
If you cannot do all three in one PR, do not start.
|
||||
|
||||
## Service vs utility vs component
|
||||
|
||||
- **Service** (`services/<name>/`): lifecycle-managed singleton, inherits `services.base.Service`, registered through `services/factory.py`, accessed via `services/deps.py`. Use when the thing has state, startup/shutdown, or shared connections (db, cache, queue).
|
||||
- **Utility** (`helpers/`, `utils/`, or `lfx/utils/`): pure or near-pure functions. Use when there is no shared state and no lifecycle.
|
||||
- **Component** (`src/lfx/src/lfx/components/<category>/`): user-visible node in the graph, subclass of `Component`, with `display_name`, `inputs`, `outputs`. Use only when the user must wire it on the canvas. Do not add a Component to expose internal plumbing.
|
||||
|
||||
## `lfx/base/` vs `langflow/base/`
|
||||
|
||||
Both exist. Both have `agents/`, `data/`, `models/`, `prompts/`. New shared primitives go in **`src/lfx/src/lfx/base/`**. The `langflow/base/` tree is legacy; do not add to it.
|
||||
138
docs/agents/COMPONENTS.md
Normal file
138
docs/agents/COMPONENTS.md
Normal file
@ -0,0 +1,138 @@
|
||||
# Component Development
|
||||
|
||||
Components are the unit of work in Langflow. They render as nodes on the canvas, persist into flow JSON, and become tools for Agents. Get the contract right and the rest of the system follows; get it wrong and you orphan every saved flow that referenced your component.
|
||||
|
||||
Components live in **`src/lfx/src/lfx/components/<category>/`**. The mirror tree under `src/backend/base/langflow/components/` is legacy stubs — do not add files there.
|
||||
|
||||
## Before you add a component (decision checklist)
|
||||
|
||||
Stop and check, in order. If any answer is "yes," do not create a new component.
|
||||
|
||||
1. **Does one already exist?** Search:
|
||||
```bash
|
||||
rg -l 'display_name = ".*<keyword>.*"' src/lfx/src/lfx/components/
|
||||
```
|
||||
and check the category folder. The Calculator regret (two of them, see `tools/calculator.py` `legacy = True, replacement = ["helpers.CalculatorComponent"]`) is what happens when this step is skipped.
|
||||
2. **Is this a thin wrapper around an existing tool/SDK that an Agent could call directly via `tool_mode=True`?** If yes, add `tool_mode=True` to the relevant input on the existing component instead of building a new one.
|
||||
3. **Does it belong in a vendor bundle** (e.g., `openai/`, `anthropic/`, `datastax/`) rather than in generic `tools/` or `helpers/`? Vendor logic goes in vendor folders so bundles can ship/version independently.
|
||||
4. **Is this an lfx-runtime concern** (graph execution, schema, IO primitives) or a **Langflow-app concern** (auth, DB, tracing)? Components are lfx; never import `langflow.services.*` from a component.
|
||||
5. **Does an existing base class cover this category?** Use it:
|
||||
- LLMs → `LCModelComponent` (see `openai/openai_chat_model.py`)
|
||||
- Tools → `LCToolComponent` (see `tools/calculator.py`)
|
||||
- Vector stores → `LCVectorStoreComponent` + `@check_cached_vector_store` (see `vectorstores/local_db.py`)
|
||||
- Chat IO → `ChatComponent` (see `input_output/chat.py`)
|
||||
|
||||
## Scope rules (one component, one job)
|
||||
|
||||
- One job per component. If the description has " and " in it, split it.
|
||||
- **Inputs budget:** aim for ≤8 visible inputs; push the rest behind `advanced=True`. If you need a mode switch (Ingest/Retrieve etc.), use `TabInput` + `update_build_config` to hide irrelevant fields — see `vectorstores/local_db.py`.
|
||||
- **Outputs:** one primary `Output(method=...)` is the default. Add more only if downstream nodes genuinely need different shapes (Message vs DataFrame vs Data). Don't add an output you don't use.
|
||||
- Mark inputs an Agent should be able to fill with `tool_mode=True` (e.g., `local_db.py`) — this is how a component becomes an agent tool.
|
||||
|
||||
## Breaking-change list (locked once shipped)
|
||||
|
||||
Saved flows reference components by string identifiers. Once a component is merged, the following are **frozen** — change them and existing user flows break silently:
|
||||
|
||||
- The **class name** (already in our rules).
|
||||
- The **`name = "..."` class attribute**. **This is the flow-JSON identifier**, not the class name. See `openai_chat_model.py`, `chat.py`.
|
||||
- Each input **`name=...`**. Renaming `input_value` → `prompt` orphans every edge pointing at it.
|
||||
- Each output **`name=...`** and its declared return type. Downstream nodes match on both.
|
||||
- **Default values** and default behavior of existing inputs (a flow saved with the default re-loads with the new default).
|
||||
|
||||
To remove or rework a component, **do not edit it in place**. Add the replacement under the right category, then on the old one set:
|
||||
|
||||
```python
|
||||
legacy = True
|
||||
replacement = ["<category>.<NewClassName>"]
|
||||
```
|
||||
|
||||
(see `tools/calculator.py`, `flow_controls/sub_flow.py`). The UI shows "Updates Available" and migrates users; the old class stays importable.
|
||||
|
||||
See [CONTRACTS.md](./CONTRACTS.md) for the full user-facing surface this protects.
|
||||
|
||||
## Conventions agents miss
|
||||
|
||||
These appear all over the codebase but rarely in old docs.
|
||||
|
||||
- **`name`** (class attr): flow-JSON identifier. Always set it explicitly; do not rely on `__class__.__name__`. Example: `chat.py` `name = "ChatInput"`.
|
||||
- **`legacy = True` + `replacement = [...]`**: deprecation pair. Always together. Example: `tools/calculator.py`.
|
||||
- **`tool_mode=True`** on an input: exposes the component as an Agent tool with that input as the tool argument. Example: `vectorstores/local_db.py`.
|
||||
- **`real_time_refresh=True` + `update_build_config(self, build_config, value, name)`**: dynamic form. Use for mode switches and dependent-field hide/show. Examples: `openai/openai_chat_model.py`, `vectorstores/local_db.py`.
|
||||
- **`metadata = {"keywords": [...]}`**: extra search terms for the component picker. Example: `data_source/sql_executor.py`.
|
||||
- **`minimized = True`**: render collapsed by default. Example: `input_output/chat.py`.
|
||||
- **`documentation = "https://docs.langflow.org/..."`**: deep-link from the node UI. Example: `input_output/chat.py`.
|
||||
|
||||
## Placement rules
|
||||
|
||||
- **Vendor-specific code** → vendor folder (`openai/`, `anthropic/`, `datastax/`, `cohere/`, …). Not `tools/`, not `models/`.
|
||||
- **Generic, vendor-agnostic helpers** → `helpers/`, `processing/`, `logic/`, `flow_controls/`.
|
||||
- **IO primitives** → `input_output/`.
|
||||
- Add the import to the category `__init__.py` in **alphabetical order**.
|
||||
- **Do not invent a new top-level category.** If you think you need one, that is a signal to discuss with the team first — categories are visible in the UI sidebar and are part of the product surface.
|
||||
|
||||
## Icons
|
||||
|
||||
Every component needs an icon. Use a Lucide icon when one fits; create a custom SVG only for vendor logos.
|
||||
|
||||
### Lucide icon (default)
|
||||
|
||||
```python
|
||||
icon = "calculator" # any Lucide icon name, lowercase
|
||||
```
|
||||
|
||||
See https://lucide.dev/icons for the catalog.
|
||||
|
||||
### Custom vendor icon
|
||||
|
||||
For brand logos you need a frontend SVG component. The Python `icon` string and the frontend mapping key must match **exactly** (case-sensitive).
|
||||
|
||||
1. **Python:** set `icon = "AstraDB"` on the component.
|
||||
2. **Frontend SVG component** at `src/frontend/src/icons/AstraDB/AstraDB.jsx`:
|
||||
```jsx
|
||||
const AstraSVG = (props) => (
|
||||
<svg {...props}>
|
||||
<path fill={props.isDark ? "#ffffff" : "#0A0A0A"} d="..." />
|
||||
</svg>
|
||||
);
|
||||
```
|
||||
3. **Wrapper** at `src/frontend/src/icons/AstraDB/index.tsx`:
|
||||
```tsx
|
||||
import React, { forwardRef } from "react";
|
||||
import AstraSVG from "./AstraDB";
|
||||
|
||||
export const AstraDBIcon = forwardRef<SVGSVGElement, React.PropsWithChildren<{}>>(
|
||||
(props, ref) => <AstraSVG ref={ref} isDark={isDark} {...props} />
|
||||
);
|
||||
```
|
||||
4. **Lazy import** in `src/frontend/src/icons/lazyIconImports.ts`:
|
||||
```ts
|
||||
AstraDB: () =>
|
||||
import("@/icons/AstraDB").then((mod) => ({ default: mod.AstraDBIcon })),
|
||||
```
|
||||
5. Verify in the UI in **both light and dark mode**.
|
||||
|
||||
### Icon checklist
|
||||
|
||||
- [ ] Python `icon = "..."` set.
|
||||
- [ ] If custom: SVG component with `isDark` prop, wrapper with `forwardRef`, entry in `lazyIconImports.ts`.
|
||||
- [ ] Light and dark mode both verified.
|
||||
|
||||
## Component testing
|
||||
|
||||
See [TESTING.md](./TESTING.md) for the full testing contract. Quick reference:
|
||||
|
||||
- Inherit from `ComponentTestBaseWithClient` (needs API) or `ComponentTestBaseWithoutClient` (pure logic).
|
||||
- Provide three fixtures: `component_class`, `default_kwargs`, `file_names_mapping`.
|
||||
- Use `MockLanguageModel` for pure-logic LLM paths; use `@pytest.mark.api_key_required` for real-API tests.
|
||||
- For graph behavior, use the Graph test pattern: build, `.set()`, `async_start`, validate. Do not poke graph internals.
|
||||
|
||||
## Canonical examples
|
||||
|
||||
When in doubt, read these files before starting:
|
||||
|
||||
- LLM with dynamic provider switching: `src/lfx/src/lfx/components/openai/openai_chat_model.py`
|
||||
- Tool with `tool_mode` and legacy/replacement: `src/lfx/src/lfx/components/tools/calculator.py`
|
||||
- Vector store with cache + tab inputs: `src/lfx/src/lfx/components/vectorstores/local_db.py`
|
||||
- Chat input with `minimized`: `src/lfx/src/lfx/components/input_output/chat.py`
|
||||
- Component with searchable metadata: `src/lfx/src/lfx/components/data_source/sql_executor.py`
|
||||
- Legacy + replacement pattern: `src/lfx/src/lfx/components/flow_controls/sub_flow.py`
|
||||
57
docs/agents/CONTRACTS.md
Normal file
57
docs/agents/CONTRACTS.md
Normal file
@ -0,0 +1,57 @@
|
||||
# User-Facing Contracts
|
||||
|
||||
These are the surfaces users depend on. Breaking them silently is the most common way an agent ships work that "doesn't connect to the story." Read this before changing anything below.
|
||||
|
||||
## The contract surface
|
||||
|
||||
| # | Contract | Source of truth | Rule |
|
||||
|---|----------|-----------------|------|
|
||||
| 1 | Component class `name` attribute | `class XComponent: name = "X"` | NEVER rename. Used to match nodes in saved flow JSON. |
|
||||
| 2 | Component class identifier (Python class name) | `class XComponent` | NEVER rename. Combined with `name` for resolution. |
|
||||
| 3 | Component file path + module | `src/lfx/src/lfx/components/<cat>/<file>.py` | Renames are breaking. The supported workflow is to add the new component alongside the old one and set `legacy = True` + `replacement = ["<category>.<NewClassName>"]` on the old class. Update every component's `file_names_mapping` in tests for past `SUPPORTED_VERSIONS` (`src/backend/tests/constants.py`). Regenerate starter projects and rebuild the component index. |
|
||||
| 4 | Input `name=` and `Output(name=...)` on every component | The component file itself | NEVER rename or remove. Saved flows reference these as keys. Adding new optional inputs is safe; removing or renaming breaks every saved flow that used them. |
|
||||
| 5 | Output order and `Output.types` | Component `outputs = [...]` list | Reordering changes default selection in old flows. Tightening a type is a breaking change; widening is safe. |
|
||||
| 6 | Default values for inputs | `default=` / `value=` in input spec | Changing a default silently changes behavior for users who relied on the default. Treat as a breaking change unless the old default was a bug. |
|
||||
| 7 | Flow JSON schema | `src/backend/base/langflow/initial_setup/starter_projects/*.json` (canonical examples) | Top-level `data.nodes[*].data.node` shape and `data.edges` shape are public. New fields must be optional with defaults. |
|
||||
| 8 | Public REST API | `docs/docs/API-Reference/api-flows-run.mdx`, `api-build.mdx`, `api-files.mdx`, `api-projects.mdx`, `api-logs.mdx`, `api-monitor.mdx`, `api-users.mdx`. Endpoints with `include_in_schema=False` are internal. | Documented endpoints are stable. `POST /api/v1/run/{flow_id_or_name}` and `POST /api/v1/webhook/{flow_id_or_name}` are user contracts — payload shape and status codes are frozen. |
|
||||
| 9 | MCP tool exposure | `src/backend/base/langflow/api/v1/mcp.py`, `mcp_projects.py`. Tool-mode toggled by `Output.tool_mode` / outputs named `component_as_tool`. | Removing `tool_mode=True` from an existing component output, or changing its name, breaks every Agent flow using it as a tool. |
|
||||
| 10 | `Message` / `Data` / `DataFrame` schema | `src/lfx/src/lfx/schema/message.py` (`text`, `sender`, `sender_name`, `session_id`, `flow_id`, `timestamp`, `properties`, `content_blocks`, `category`, `files`, `error`, `edit`, `duration`, `session_metadata`) | Inter-component wire format. Add fields with defaults only. Renaming or retyping any listed field breaks every running flow. |
|
||||
| 11 | Environment variables | `LANGFLOW_*` (server) defined in `src/backend/base/langflow/services/settings/base.py` and `feature_flags.py`. `LFX_*` (executor) defined in `src/lfx/src/lfx/services/settings/base.py`, except a few read directly via `os.getenv` (e.g., `LFX_DEV` in `src/lfx/src/lfx/interface/components.py`). | Public deployment contract. Renaming requires deprecation cycle reading both names. |
|
||||
| 12 | Database schema | `src/backend/base/langflow/services/database/models/` + `alembic/versions/`. | NEVER edit a model without `make alembic-revision`. Never edit a past migration. Custom-component Python source is stored in user DBs — refactoring an import path used by `from langflow.X import Y` breaks loading those rows. |
|
||||
| 13 | Starter project JSON | `src/backend/base/langflow/initial_setup/starter_projects/*.json` | Each references real components by `name`/`type`. Renaming a component, removing an input, or changing an output type breaks loading. After ANY component change, re-load the affected starter project. |
|
||||
| 14 | Webhook payload shape | `POST /api/v1/webhook/{flow_id_or_name}` | External systems POST here. Response status (202 Accepted) and the `dict` body shape are frozen. |
|
||||
| 15 | Component index | `src/lfx/src/lfx/_assets/component_index.json` | Generated artifact consumed by the frontend. Any field/output addition requires regeneration; CI enforces this on label add. |
|
||||
|
||||
## Before-you-change matrix
|
||||
|
||||
| If you are about to... | Check / update |
|
||||
|---|---|
|
||||
| Rename a component file | Add the new file alongside; set `legacy = True` + `replacement = [...]` on the old class; update every test's `file_names_mapping`; grep `starter_projects/*.json` for the old type |
|
||||
| Rename an input `name=` | Don't. Add a new input alongside and deprecate the old via `legacy=True` on the component if necessary; grep `starter_projects/*.json` for the old name |
|
||||
| Add or rename an output | Regenerate starter projects; rebuild component index |
|
||||
| Change a default value | Treat as breaking. Grep starter projects + tests for reliance |
|
||||
| Remove `tool_mode=True` from an output | Search agent flows / starter projects using it as a tool; this is a user-visible regression |
|
||||
| Add a `Message` field | Must be `Optional` with default; never reorder existing |
|
||||
| Add a REST endpoint | If user-facing, add to `docs/docs/API-Reference/`; if internal, set `include_in_schema=False` |
|
||||
| Modify a DB model | `make alembic-revision message="..."`; never edit past revisions |
|
||||
| Change a `LANGFLOW_*` / `LFX_*` env var | Read old name as fallback for at least one minor version; document in release notes |
|
||||
| Change a request/response schema | Update pydantic schema + `src/frontend/src/types/` + alembic if persisted, all in one PR |
|
||||
|
||||
## Breaking changes that look harmless
|
||||
|
||||
These all look like cleanup. They are user-visible regressions.
|
||||
|
||||
- **Renaming `input_value` → `text`** on a component: 100% of saved flows referencing that input lose their wiring.
|
||||
- **Reordering `outputs = [a, b]` to `[b, a]`**: old flows with the default selection now route from the wrong output.
|
||||
- **Tightening `Output(types=["Message", "Data"])` → `["Message"]`**: edges that resolved as `Data` go invalid on load.
|
||||
- **Changing a `default="gpt-4o-mini"` to `default="gpt-5"`**: silently re-bills users.
|
||||
- **Moving `langflow/components/foo/bar.py` → `langflow/components/foo2/bar.py`**: custom components stored in user DBs that `from langflow.components.foo.bar import ...` fail on next load. `file_names_mapping` test passes because it only validates listed historical versions, not user code.
|
||||
- **Dropping `tool_mode=True` from a vector store's `as_dataframe`**: every Agent that called it as a tool now sees the tool disappear.
|
||||
- **Adding a required field to `Message`**: every queued message in a running deployment fails to deserialize on next read.
|
||||
- **Renaming a starter project file**: deep links from docs and tutorials 404.
|
||||
|
||||
## Why this matters
|
||||
|
||||
Langflow flows are **persisted user artifacts** running in production. The system that loads them is forgiving by design: it tolerates new fields, falls back on missing ones, applies type migrations on load. That tolerance is paid for by every contract in the table above. Break one and the system can no longer route around it — the flow stops working.
|
||||
|
||||
Treat this file as a checklist. If your change touches any row, the corresponding rule applies; no exceptions without an explicit deprecation plan.
|
||||
33
docs/agents/PHILOSOPHY.md
Normal file
33
docs/agents/PHILOSOPHY.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Project Philosophy
|
||||
|
||||
Read this file before scoping any non-trivial work. Most off-narrative work comes from skipping it.
|
||||
|
||||
Langflow is a **visual flow builder first**. Every change should make sense to someone whose only interface is dragging nodes onto a canvas. Use these principles to scope work; if a proposed change fails one of them, stop and reconsider.
|
||||
|
||||
## Tenets
|
||||
|
||||
1. **Flows are user artifacts, not implementation details.** Flow JSON lives in users' databases and runs in production. Backwards compatibility for components is non-negotiable: never rename a class name, `name` attribute, input name, or output name; never remove an input or output; never tighten an output type; never remove methods from base classes like `LCModelComponent`. To change a component, add the new one alongside, set `legacy=True` + `replacement=[...]` on the old, and update `file_names_mapping` in tests so saved flows still load. See [CONTRACTS.md](./CONTRACTS.md) for the full surface.
|
||||
|
||||
2. **Every backend feature must land on the canvas.** If a capability cannot be expressed as a component (or a property of one) that a non-Python user can wire up, it is an SDK feature and belongs in `lfx`, not `langflow-base`. Backend changes that have no visual surface are almost always off-narrative.
|
||||
|
||||
3. **Components are the unit of work, not endpoints or services.** Before adding a route, store, or service, ask: which component does this serve? If the answer is "none yet," the route is premature. New REST endpoints without a component or UI consumer are off-narrative.
|
||||
|
||||
4. **`lfx` is the runtime, `langflow-base` is the platform, `langflow` is the distribution.** Components and the graph engine live in `lfx` (`src/lfx/src/lfx/components/`). API, auth, persistence, and multi-user concerns live in `langflow-base`. The `langflow` package is the integration that ships everything together. New code goes in the lowest layer that can host it. Boundaries are enforced one-way: `langflow` → `langflow-base` → `lfx`. See [ARCHITECTURE.md](./ARCHITECTURE.md).
|
||||
|
||||
5. **Don't add a config flag for something a builder-user would set on a node.** If a setting affects flow behavior, it's a component input. If it affects deployment, it's an env var. Hidden global flags that change component semantics break the visible-data-flow contract.
|
||||
|
||||
6. **Visible data flow beats clever magic.** Implicit context, hidden globals, and side-channel state make flows unreadable on the canvas. Pass data through inputs and outputs. If you find yourself reaching for a global, you're probably building something that doesn't belong in a node.
|
||||
|
||||
7. **Composition over capability.** Prefer adding small, single-purpose components users can wire together over one large component with many modes. The `If-Else` / `Current Date` style (one job, clear name, obvious icon) is the target shape.
|
||||
|
||||
8. **Every component needs `display_name`, `description`, `icon`, and a sensible category.** A component that doesn't render legibly in the sidebar shouldn't ship. The icon is part of the API, not decoration — pick a Lucide icon that matches the verb.
|
||||
|
||||
9. **The Playground is the test harness builder-users see.** A component is "done" when it works end-to-end in a flow on the canvas, not when its unit test passes. Use the Graph test pattern (build, `.set()`, `async_start`, validate) before claiming a feature works.
|
||||
|
||||
10. **Two audiences, one product: visual builders and Python devs.** When their needs conflict, the visual builder wins for `langflow-base` features and the Python dev wins for `lfx` SDK features. Don't compromise the canvas to make the SDK prettier, and don't compromise the SDK to make the canvas easier.
|
||||
|
||||
11. **It is not a fix if you didn't have evidence of the change fixing something.** Adding error handling, retries, type widening, or skipping a flaky test does not constitute a fix. Reproduce the failure, demonstrate the fix removes it, then ship. See [ANTI-PATTERNS.md](./ANTI-PATTERNS.md).
|
||||
|
||||
## How to apply
|
||||
|
||||
When scoping a task, walk through the tenets and name the ones the work serves. If a change fails tenet 1, 2, or 3, stop and surface the conflict before writing code. If you can't tell which tenet a change serves, it's probably off-narrative — ask.
|
||||
163
docs/agents/TESTING.md
Normal file
163
docs/agents/TESTING.md
Normal file
@ -0,0 +1,163 @@
|
||||
# Testing
|
||||
|
||||
Conventions for backend tests. Frontend testing follows the standard Jest/Playwright patterns documented in `src/frontend/`.
|
||||
|
||||
## Project policy: avoid mocking
|
||||
|
||||
Prefer real integrations. The pattern of "mocked test passes, production fails" has cost us multiple release cycles — see [ANTI-PATTERNS.md](./ANTI-PATTERNS.md) for the relevant commit history. Use mocks only when:
|
||||
|
||||
- The dependency is an LLM and the test exercises pure logic → use `MockLanguageModel` from `tests/unit/mock_language_model.py`.
|
||||
- The dependency is genuinely unreliable and orthogonal to what's being tested.
|
||||
|
||||
Otherwise: hit the real thing, mark the test with `@pytest.mark.api_key_required` if it needs credentials, and let CI gate it.
|
||||
|
||||
## Built-in fixtures
|
||||
|
||||
### `client` (FastAPI test client)
|
||||
|
||||
Defined in `src/backend/tests/conftest.py`. Async `httpx.AsyncClient` connected to the full app via `ASGITransport` + `LifespanManager`. Auto-configured with in-memory SQLite and mocked env vars. Skip with `@pytest.mark.noclient`.
|
||||
|
||||
```python
|
||||
async def test_login_endpoint(client):
|
||||
response = await client.post("api/v1/login", data={"username": "foo", "password": "bar"})
|
||||
assert response.status_code == 200
|
||||
```
|
||||
|
||||
For authenticated routes, also use the `logged_in_headers` fixture.
|
||||
|
||||
## Component test base classes
|
||||
|
||||
Located in `src/backend/tests/base.py`.
|
||||
|
||||
| Base class | Creates `client`? | Use for |
|
||||
|---|---|---|
|
||||
| `ComponentTestBase` | No | Component version testing core logic |
|
||||
| `ComponentTestBaseWithClient` | Yes | Components that hit backend services during `run()` |
|
||||
| `ComponentTestBaseWithoutClient` | No | Pure-logic components |
|
||||
|
||||
### Required fixtures
|
||||
|
||||
Every subclass provides three fixtures:
|
||||
|
||||
1. **`component_class`** — the component class under test.
|
||||
2. **`default_kwargs`** — dict of kwargs to instantiate the component (can be empty).
|
||||
3. **`file_names_mapping`** — list of `VersionComponentMapping` entries mapping each historical Langflow version (from `src/backend/tests/constants.py::SUPPORTED_VERSIONS`) to module/file names. Use `DID_NOT_EXIST` for versions before the component was added.
|
||||
|
||||
```python
|
||||
from tests.base import ComponentTestBaseWithClient, VersionComponentMapping, DID_NOT_EXIST
|
||||
from langflow.components.my_namespace import MyComponent
|
||||
|
||||
class TestMyComponent(ComponentTestBaseWithClient):
|
||||
@pytest.fixture
|
||||
def component_class(self):
|
||||
return MyComponent
|
||||
|
||||
@pytest.fixture
|
||||
def default_kwargs(self):
|
||||
return {"foo": "bar"}
|
||||
|
||||
@pytest.fixture
|
||||
def file_names_mapping(self):
|
||||
return [
|
||||
VersionComponentMapping(version="1.1.1", module="my_module", file_name="my_component.py"),
|
||||
VersionComponentMapping(version="1.0.19", module="my_module", file_name=DID_NOT_EXIST),
|
||||
]
|
||||
```
|
||||
|
||||
The base class auto-provides:
|
||||
|
||||
- `test_latest_version` — instantiates and asserts `run()` doesn't return `None`.
|
||||
- `test_all_versions_have_a_file_name_defined` — ensures mapping completeness vs `SUPPORTED_VERSIONS`.
|
||||
- `test_component_versions` (parameterized) — builds the component from source for each supported version and asserts execution.
|
||||
|
||||
If you rename or move a component file, you **must** update `file_names_mapping` for every supported version, or saved flows on those versions will fail to load. See [CONTRACTS.md](./CONTRACTS.md) row 3.
|
||||
|
||||
## Graph testing pattern
|
||||
|
||||
The canonical pattern for tests that exercise the graph engine:
|
||||
|
||||
1. Build the graph with connected components.
|
||||
2. Connect them via `.set()` calls.
|
||||
3. Call `async_start` and iterate over the results.
|
||||
4. Validate the results.
|
||||
|
||||
Don't poke graph internals. If a test needs to reach into private state, the test is wrong or the API is wrong — fix the right one.
|
||||
|
||||
## Async patterns
|
||||
|
||||
```python
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_component():
|
||||
result = await component.async_method()
|
||||
assert result is not None
|
||||
```
|
||||
|
||||
**Awaiting conditions, not sleeping:** never use `time.sleep` or `asyncio.sleep` to mask a race. Wait on a condition (`asyncio.wait_for`, an event, a queue read with timeout). Sleep-based tests are flaky by construction.
|
||||
|
||||
## Pytest markers
|
||||
|
||||
- `@pytest.mark.api_key_required` — needs an external API key; CI skips when absent.
|
||||
- `@pytest.mark.no_blockbuster` — skip blockbuster plugin.
|
||||
- `@pytest.mark.noclient` — skip the `client` fixture.
|
||||
- `@pytest.mark.asyncio` — async test (also `pytest-asyncio` auto-mode in some configs).
|
||||
|
||||
## Database tests
|
||||
|
||||
`test_database.py` may fail in batch and pass individually. If you touch DB models or migrations, run it sequentially as part of your verification:
|
||||
|
||||
```bash
|
||||
uv run pytest src/backend/tests/unit/test_database.py
|
||||
```
|
||||
|
||||
Never edit a past alembic migration. Run `make alembic-upgrade` end-to-end before claiming a migration works.
|
||||
|
||||
## API endpoint tests
|
||||
|
||||
```python
|
||||
async def test_flows_endpoint(client, logged_in_headers):
|
||||
flow_data = {"name": "Test", "data": {"nodes": [], "edges": []}}
|
||||
response = await client.post("api/v1/flows/", json=flow_data, headers=logged_in_headers)
|
||||
assert response.status_code == 201
|
||||
```
|
||||
|
||||
For event-stream endpoints, consume the NDJSON stream and validate event order:
|
||||
|
||||
```python
|
||||
async for line in response.aiter_lines():
|
||||
if not line:
|
||||
continue
|
||||
parsed = json.loads(line)
|
||||
# First event should be vertices_sorted; last should be end.
|
||||
```
|
||||
|
||||
## Flow testing with starter JSON
|
||||
|
||||
Use `tests/unit/build_utils.py` helpers:
|
||||
|
||||
```python
|
||||
from tests.unit.build_utils import create_flow, build_flow, get_build_events
|
||||
|
||||
flow_id = await create_flow(client, json_flow, logged_in_headers)
|
||||
build_response = await build_flow(client, flow_id, logged_in_headers)
|
||||
events_response = await get_build_events(client, job_id, logged_in_headers)
|
||||
```
|
||||
|
||||
## Running tests
|
||||
|
||||
```bash
|
||||
make unit_tests # All backend unit tests, parallel
|
||||
make unit_tests async=false # Sequential
|
||||
uv run pytest path/to/test.py # Single file
|
||||
uv run pytest path/to/test.py::test_name # Single test
|
||||
|
||||
# lfx tests specifically — must be run after `uv sync` inside src/lfx
|
||||
cd src/lfx && uv sync && uv run pytest
|
||||
```
|
||||
|
||||
## Verification checklist before claiming "tests pass"
|
||||
|
||||
- [ ] You actually ran the command, not just composed it.
|
||||
- [ ] You ran `test_database.py` sequentially if you touched DB code.
|
||||
- [ ] You ran the specific test file for any component you changed.
|
||||
- [ ] No skipped tests without a linked issue.
|
||||
- [ ] No mocks added at the wrong boundary.
|
||||
@ -1,3 +1,9 @@
|
||||
/* Geist Mono (and other ligature-enabled fonts) merge ~= into one glyph in inline code */
|
||||
code,
|
||||
pre code {
|
||||
font-variant-ligatures: none;
|
||||
}
|
||||
|
||||
.docusaurus-highlight-code-line {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
display: block;
|
||||
|
||||
@ -135,7 +135,7 @@ Instead of manually exporting, sharing, and importing flow JSON files from the L
|
||||
The Flow DevOps SDK can validate that local flows are correctly formed before pushing to the Langflow with `lfx validate`.
|
||||
|
||||
1. To test the Simple Agent starter flow, pass the flow JSON path to the `lfx validate` command:
|
||||
```
|
||||
```bash
|
||||
lfx validate flows/Simple_Agent.json
|
||||
```
|
||||
2. Once validated, push flow changes to the server with `lfx push`.
|
||||
@ -150,13 +150,13 @@ Generate a `requirements.txt` file to capture the minimal Python dependencies, s
|
||||
1. From your project directory, point `lfx requirements` at a flow JSON file.
|
||||
To print the requirements to the terminal:
|
||||
|
||||
```
|
||||
```bash
|
||||
lfx requirements flows/Simple_Agent.json
|
||||
```
|
||||
|
||||
To write a `requirements.txt` file instead of printing, use `-o` or `--output`:
|
||||
|
||||
```
|
||||
```bash
|
||||
lfx requirements flows/Simple_Agent.json -o requirements.txt
|
||||
```
|
||||
|
||||
@ -164,12 +164,12 @@ Generate a `requirements.txt` file to capture the minimal Python dependencies, s
|
||||
To serve the flow without the Langflow UI, do the following:
|
||||
|
||||
1. Create a virtual environment:
|
||||
```
|
||||
```bash
|
||||
uv venv VENV_NAME
|
||||
```
|
||||
|
||||
2. Activate the virtual environment.
|
||||
```
|
||||
```bash
|
||||
source VENV_NAME/bin/activate
|
||||
```
|
||||
|
||||
@ -179,12 +179,12 @@ Generate a `requirements.txt` file to capture the minimal Python dependencies, s
|
||||
```
|
||||
|
||||
4. To set a Langflow API key, run:
|
||||
```
|
||||
```bash
|
||||
export LANGFLOW_API_KEY=LANGFLOW_API_KEY
|
||||
```
|
||||
|
||||
5. To serve the flow without the Langflow UI, pass the flow JSON path to the `lfx serve` command:
|
||||
```
|
||||
```bash
|
||||
lfx serve flows/Simple_Agent.json
|
||||
```
|
||||
|
||||
|
||||
90
docs/docs/Components/bundles-codeagents.mdx
Normal file
90
docs/docs/Components/bundles-codeagents.mdx
Normal file
@ -0,0 +1,90 @@
|
||||
---
|
||||
title: Code Agents
|
||||
slug: /bundles-codeagents
|
||||
---
|
||||
|
||||
import Icon from "@site/src/components/icon";
|
||||
|
||||
<Icon name="Blocks" aria-hidden="true" /> [**Bundles**](/components-bundle-components) contain custom components that support specific third-party integrations with Langflow.
|
||||
|
||||
Langflow integrates with [OpenDsStar](https://github.com/IBM/OpenDsStar) through a bundle of code-writing agent components that can iteratively plan, write, and execute Python code to solve complex tasks.
|
||||
|
||||
:::info
|
||||
Code Agents components are in **beta** and can change in future releases.
|
||||
:::
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* **OpenDsStar package**: Code Agents components require the `OpenDsStar` package and Python 3.11 or later.
|
||||
|
||||
Install the dependency with:
|
||||
|
||||
```bash
|
||||
uv pip install OpenDsStar
|
||||
```
|
||||
|
||||
For more information, see [Install custom dependencies](/install-custom-dependencies).
|
||||
|
||||
## Use Code Agents components in a flow
|
||||
|
||||
For an example of using this component, see the **Structured Data Agent** starter template.
|
||||
|
||||
## Code Agents components
|
||||
|
||||
The following sections describe the purpose and configuration options for each component in the **Code Agents** bundle.
|
||||
|
||||
### CodeAct Agent (Smolagents)
|
||||
|
||||
The **CodeAct Agent (Smolagents)** component is a code-writing agent that uses the [smolagents](https://huggingface.co/docs/smolagents) `CodeAgent` to solve tasks by iteratively generating and executing Python code.
|
||||
|
||||
At each step the agent writes code, executes it in a sandboxed interpreter, and uses the output to determine the next action until it reaches a final answer.
|
||||
|
||||
The component outputs a [`Message`](/data-types#message) containing the final answer.
|
||||
|
||||
#### CodeAct Agent (Smolagents) parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| input_value | String | Input parameter. The message or query to send to the agent. |
|
||||
| llm | LanguageModel | Input parameter. The language model to use for code generation. Required. |
|
||||
| tools | Tool | Input parameter. Optional tools the agent can call in addition to executing code. |
|
||||
| max_iterations | Integer | Input parameter. Maximum number of agent iterations. Default: `5`. Range: 1–50. |
|
||||
| system_prompt | String | Input parameter. A system message to customize agent behavior. |
|
||||
| code_timeout | Integer | Input parameter. Maximum time in seconds for each code execution step. Default: `30`. Range: 5–300. |
|
||||
| show_code_steps | String | Input parameter. How to display coding steps in the **Playground**: `All Steps`, `Final Code Only`, or `None`. Default: `All Steps`. |
|
||||
| handle_parsing_errors | Boolean | Input parameter. Whether the agent should attempt to recover from input parsing errors. Default: `true`. |
|
||||
| verbose | Boolean | Input parameter. Enable verbose logging. Default: `true`. |
|
||||
| chat_history | Data | Input parameter. Chat history for multi-turn conversations. |
|
||||
| agent_description | String | Input parameter. Description of the agent shown when used in Tool Mode. |
|
||||
|
||||
### OpenDsStar Agent
|
||||
|
||||
The **OpenDsStar Agent** component is a structured data analysis agent that uses [OpenDsStar](https://github.com/IBM/OpenDsStar) and LangGraph to answer complex questions over structured data files.
|
||||
|
||||
The agent executes a multi-step LangGraph workflow: planning → code generation → code execution → verification → routing → finalization.
|
||||
Each step is streamed to the **Playground** as it runs.
|
||||
Charts and images returned by the agent are rendered inline.
|
||||
|
||||
The component outputs a [`Message`](/data-types#message) containing the final answer.
|
||||
|
||||
#### OpenDsStar Agent parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| input_value | String | Input parameter. The message or query to send to the agent. |
|
||||
| llm | LanguageModel | Input parameter. The language model to use for the agent. Required. |
|
||||
| tools | Tool | Input parameter. Optional tools the agent can use, such as **File Content Retriever** or vector store search. |
|
||||
| max_iterations | Integer | Input parameter. Maximum number of agent steps. Default: `10`. Range: 1–100. |
|
||||
| code_execution_mode | String | Input parameter. Code execution mode. `stepwise` executes each step separately; `full` executes all steps together. Default: `stepwise`. |
|
||||
| system_prompt | String | Input parameter. A system message to customize agent behavior. |
|
||||
| handle_parsing_errors | Boolean | Input parameter. Whether the agent should attempt to recover from input parsing errors. Default: `true`. |
|
||||
| code_timeout | Integer | Input parameter. Maximum execution time in seconds for each code step. Default: `60`. Range: 10–300. |
|
||||
| verbose | Boolean | Input parameter. Enable verbose logging. Default: `true`. |
|
||||
| chat_history | Data | Input parameter. Chat history for multi-turn conversations. |
|
||||
| agent_description | String | Input parameter. Description of the agent shown when used in Tool Mode. |
|
||||
|
||||
## See also
|
||||
|
||||
* [File Processing bundle](./bundles-files-ingestion) — file ingestion and retrieval components designed to work with Code Agents
|
||||
* [OpenDsStar GitHub repository](https://github.com/IBM/OpenDsStar)
|
||||
* [smolagents documentation](https://huggingface.co/docs/smolagents)
|
||||
@ -13,26 +13,60 @@ import PartialDockerDoclingDeps from '@site/docs/_partial-docker-docling-deps.md
|
||||
|
||||
Langflow integrates with [Docling](https://docling-project.github.io/docling/) through a bundle of components for parsing and chunking documents.
|
||||
|
||||
## Install the Docling bundle
|
||||
|
||||
The Docling bundle is included in the `lfx-docling` Extension bundle, which is installed automatically as part of `uv pip install langflow`.
|
||||
|
||||
If you need to install it separately, run:
|
||||
|
||||
```bash
|
||||
uv pip install lfx-docling
|
||||
uv run langflow run
|
||||
```
|
||||
|
||||
To verify the bundle is loaded in your environment:
|
||||
|
||||
```bash
|
||||
lfx extension list
|
||||
```
|
||||
|
||||
### Optional extras
|
||||
|
||||
Some Docling components require additional dependencies that are not installed by default.
|
||||
|
||||
**Local model** (required for the **Docling** local model component):
|
||||
|
||||
```bash
|
||||
uv pip install "lfx-docling[local]"
|
||||
```
|
||||
|
||||
Alternatively, if you installed Langflow as a package:
|
||||
|
||||
```bash
|
||||
uv pip install "langflow[docling]"
|
||||
```
|
||||
|
||||
**Chunking** (`HybridChunker` and `HierarchicalChunker` support for the **Chunk DoclingDocument** component):
|
||||
|
||||
```bash
|
||||
uv pip install "lfx-docling[chunking]"
|
||||
```
|
||||
|
||||
**Image description** (vision model support for image-rich documents):
|
||||
|
||||
```bash
|
||||
uv pip install "lfx-docling[image-description]"
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* **Enable Developer Mode for Windows**:
|
||||
|
||||
<PartialDevModeWindows />
|
||||
|
||||
* **Install Docling dependency**:
|
||||
The Docling dependency is required to use the Docling components in Langflow.
|
||||
* **Langflow Desktop**: Set `LANGFLOW_DOCLING=True` in your `.env` file to enable Docling dependency installation. For more information, see [Set environment variables for Langflow Desktop](/environment-variables#set-environment-variables-for-langflow-desktop).
|
||||
|
||||
* **Langflow Desktop**: Set `LANGFLOW_DOCLING=True` in your `.env` file to enable Docling dependency installation. For more information, see [Set environment variables for Langflow Desktop](/environment-variables#set-environment-variables-for-langflow-desktop).
|
||||
|
||||
* **Langflow OSS**:
|
||||
* If you installed `langflow` (`uv pip install langflow`), Docling is included automatically through bundled extras.
|
||||
* If you installed `langflow-base` directly, install Docling with an extra, for example `uv pip install "langflow-base[docling]"`.
|
||||
|
||||
* **macOS Intel (x86_64)**: Use the [Docling installation guide](https://docling-project.github.io/docling/installation/) to install the Docling dependency.
|
||||
|
||||
* **Earlier versions**: Langflow versions earlier than 1.6 don't include the Docling dependency by default.
|
||||
|
||||
For more information, see [Install custom dependencies](/install-custom-dependencies).
|
||||
* **macOS Intel (x86_64)**: Use the [Docling installation guide](https://docling-project.github.io/docling/installation/) to install the Docling dependency.
|
||||
|
||||
<PartialDockerDoclingDeps />
|
||||
|
||||
@ -103,6 +137,8 @@ For more information, see the [Docling serve project repository](https://github.
|
||||
|
||||
The **Chunk DoclingDocument** component splits `DoclingDocument` objects into chunks.
|
||||
|
||||
This component requires Docling's optional chunking dependencies.
|
||||
|
||||
It outputs the chunked documents as a [`Table`](/data-types#table).
|
||||
|
||||
For more information, see the [Docling core project repository](https://github.com/docling-project/docling-core).
|
||||
|
||||
88
docs/docs/Components/bundles-files-ingestion.mdx
Normal file
88
docs/docs/Components/bundles-files-ingestion.mdx
Normal file
@ -0,0 +1,88 @@
|
||||
---
|
||||
title: File Processing
|
||||
slug: /bundles-files-ingestion
|
||||
---
|
||||
|
||||
import Icon from "@site/src/components/icon";
|
||||
|
||||
<Icon name="Blocks" aria-hidden="true" /> [**Bundles**](/components-bundle-components) contain custom components that support specific third-party integrations with Langflow.
|
||||
|
||||
Langflow integrates with [OpenDsStar](https://github.com/IBM/OpenDsStar) through a bundle of file processing components for ingesting, indexing, and retrieving content from large collections of files in agent workflows.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* **OpenDsStar package (File Description Generator only)**: The **File Description Generator** component requires the `OpenDsStar` package and Python 3.11 or later.
|
||||
|
||||
Install the dependency with:
|
||||
|
||||
```bash
|
||||
uv pip install OpenDsStar
|
||||
```
|
||||
|
||||
For more information, see [Install custom dependencies](/install-custom-dependencies).
|
||||
|
||||
## Use File Processing components in a flow
|
||||
|
||||
For an example of using this component, see the **Structured Data Agent** starter template.
|
||||
|
||||
## File Processing components
|
||||
|
||||
The following sections describe the purpose and configuration options for each component in the **File Processing** bundle.
|
||||
|
||||
### File Content Retriever
|
||||
|
||||
The **File Content Retriever** component takes file outputs from a [**Read File** component](/read-file) and exposes two tools so an agent can look up file content by path:
|
||||
|
||||
- **File Content** (`retrieve_content`): Returns the file content as text ([`Message`](/data-types#message)).
|
||||
- **Table** (`retrieve_content_as_dataframe`): Returns the file content as a [`Table`](/data-types#table) for tabular formats (CSV, Excel, Parquet, SON, and TSV).
|
||||
|
||||
File maps are built once and cached in memory after the first build. Set **Persistent Directory** to cache maps to disk and preserve them across flow runs.
|
||||
|
||||
#### File Content Retriever parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| file_data | Data, Table, or Message | Input parameter. Output from a **Read File** component. |
|
||||
| persistent_dir | String | Input parameter. Optional path to a directory for persisting file maps across runs. If empty, maps are kept in memory only. |
|
||||
| file_path | String | Input parameter (Tool Mode). The full file path as a string, for example `/path/to/file.csv`. Used by agents to request a specific file's content. |
|
||||
|
||||
### File Description Generator
|
||||
|
||||
The **File Description Generator** component runs the OpenDsStar Docling-based ingestion pipeline to produce natural-language descriptions of each file.
|
||||
|
||||
For each file, the pipeline converts the document with [Docling](https://docling-project.github.io/docling/), shortens the Markdown output, and prompts the connected LLM to write a searchable description. Processing runs in a subprocess to avoid memory pressure when handling large files.
|
||||
|
||||
The component outputs a list of [`Data`](/data-types#data) objects, each containing `file_path` and the generated description text. Connect this output to a vector store's **Ingest Data** input to make the files searchable by an agent.
|
||||
|
||||
Descriptions are cached in the **Cache Directory** to avoid regenerating them on subsequent runs with the same files.
|
||||
|
||||
#### File Description Generator parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| file_data | Data, Table, or Message | Input parameter. Output from a **Read File** component. |
|
||||
| llm | LanguageModel | Input parameter. The LLM used to generate file descriptions. |
|
||||
| cache_dir | String | Input parameter. Directory for caching Docling analysis and LLM-generated descriptions. Default: `./opendsstar_cache`. |
|
||||
| embedding_model | String | Input parameter. Embedding model name used for cache keying. Default: `ibm-granite/granite-embedding-english-r2`. |
|
||||
| timeout | Integer | Input parameter. Maximum time in seconds allowed for the ingestion subprocess. Default: `3600`. Increase this value for large file sets. |
|
||||
| batch_size | Integer | Input parameter. Number of files to process per LLM batch. Default: `8`. |
|
||||
|
||||
### Merge Flows
|
||||
|
||||
The **Merge Flows** component connects multiple upstream component outputs and triggers all of them when the component executes.
|
||||
|
||||
Use this component to synchronize parallel setup pipelines, such as running the **File Description Generator** ingestion flow and the **File Content Retriever** initialization together before starting an agent.
|
||||
|
||||
The component outputs a [`Message`](/data-types#message) that confirms how many upstream flows completed.
|
||||
|
||||
#### Merge Flows parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| inputs | Data, Table, Message, Tool, or JSON | Input parameter. Connect any number of upstream component outputs here. All connected components will run when this component executes. |
|
||||
|
||||
## See also
|
||||
|
||||
* [Code Agents bundle](./bundles-codeagents) — CodeAct Agent and OpenDsStar Agent for analyzing the retrieved file content
|
||||
* [OpenDsStar GitHub repository](https://github.com/IBM/OpenDsStar)
|
||||
* [Docling documentation](https://docling-project.github.io/docling/)
|
||||
77
docs/docs/Components/bundles-firecrawl.mdx
Normal file
77
docs/docs/Components/bundles-firecrawl.mdx
Normal file
@ -0,0 +1,77 @@
|
||||
---
|
||||
title: Firecrawl
|
||||
slug: /bundles-firecrawl
|
||||
---
|
||||
|
||||
import Icon from "@site/src/components/icon";
|
||||
|
||||
<Icon name="Blocks" aria-hidden="true" /> [**Bundles**](/components-bundle-components) contain custom components that support specific third-party integrations with Langflow.
|
||||
|
||||
This page describes the components that are available in the **Firecrawl** bundle.
|
||||
|
||||
For more information, see the [Firecrawl documentation](https://docs.firecrawl.dev).
|
||||
|
||||
## Firecrawl Scrape API
|
||||
|
||||
This component scrapes a single URL and returns its content.
|
||||
|
||||
It outputs the API response as [`Data`](/data-types#data).
|
||||
|
||||
### Firecrawl Scrape API parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| Firecrawl API Key (`api_key`) | SecretString | Input parameter. The API key to use Firecrawl API. |
|
||||
| URL (`url`) | String | Input parameter. The URL to scrape. |
|
||||
| Timeout (`timeout`) | Integer | Input parameter. Timeout in milliseconds for the request. |
|
||||
| Scrape Options (`scrapeOptions`) | Data | Input parameter. The page options to send with the request. |
|
||||
| Extractor Options (`extractorOptions`) | Data | Input parameter. The extractor options to send with the request. |
|
||||
|
||||
## Firecrawl Crawl API
|
||||
|
||||
This component crawls a URL and its accessible sub-pages, returning the results.
|
||||
|
||||
It outputs the API response as [`Data`](/data-types#data).
|
||||
|
||||
### Firecrawl Crawl API parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| Firecrawl API Key (`api_key`) | SecretString | Input parameter. The API key to use Firecrawl API. |
|
||||
| URL (`url`) | String | Input parameter. The URL to crawl. |
|
||||
| Timeout (`timeout`) | Integer | Input parameter. Timeout in milliseconds for the request. |
|
||||
| Idempotency Key (`idempotency_key`) | String | Input parameter. Optional idempotency key to ensure unique requests. |
|
||||
| Crawler Options (`crawlerOptions`) | Data | Input parameter. The crawler options to send with the request. |
|
||||
| Scrape Options (`scrapeOptions`) | Data | Input parameter. The page options to send with the request. |
|
||||
|
||||
## Firecrawl Map API
|
||||
|
||||
This component maps a URL and returns a list of related URLs.
|
||||
|
||||
It outputs the API response as [`Data`](/data-types#data).
|
||||
|
||||
### Firecrawl Map API parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| Firecrawl API Key (`api_key`) | SecretString | Input parameter. The API key to use Firecrawl API. |
|
||||
| URLs (`urls`) | String | Input parameter. List of URLs to create maps from (separated by commas or new lines). |
|
||||
| Ignore Sitemap (`ignore_sitemap`) | Boolean | Input parameter. When true, the `sitemap.xml` file is ignored during crawling. |
|
||||
| Sitemap Only (`sitemap_only`) | Boolean | Input parameter. When true, only links found in the sitemap are returned. |
|
||||
| Include Subdomains (`include_subdomains`) | Boolean | Input parameter. When true, subdomains of the provided URL are also scanned. |
|
||||
|
||||
## Firecrawl Extract API
|
||||
|
||||
This component extracts structured data from one or more URLs using a prompt or schema.
|
||||
|
||||
It outputs the API response as [`Data`](/data-types#data).
|
||||
|
||||
### Firecrawl Extract API parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| Firecrawl API Key (`api_key`) | SecretString | Input parameter. The API key to use Firecrawl API. |
|
||||
| URLs (`urls`) | String | Input parameter. List of URLs to extract data from (separated by commas or new lines). |
|
||||
| Prompt (`prompt`) | String | Input parameter. Prompt to guide the extraction process. |
|
||||
| Schema (`schema`) | Data | Input parameter. Schema to define the structure of the extracted data. |
|
||||
| Enable Web Search (`enable_web_search`) | Boolean | Input parameter. When true, the extraction uses web search to find additional data. |
|
||||
@ -14,6 +14,23 @@ import PartialVectorStoreInstance from '@site/docs/_partial-vector-store-instanc
|
||||
The **IBM** bundle provides access to IBM watsonx.ai models for text and embedding generation, plus an IBM Db2 Vector Store.
|
||||
These components require an IBM watsonx.ai deployment with API credentials, and/or a reachable IBM Db2 instance with the `ibm-db` driver.
|
||||
|
||||
## Install the IBM bundle
|
||||
|
||||
The **IBM** bundle is included in the `lfx-ibm` Extension bundle, which is installed automatically as part of `uv pip install langflow`.
|
||||
|
||||
If you need to install it separately, run:
|
||||
|
||||
```bash
|
||||
uv pip install lfx-ibm
|
||||
uv run langflow run
|
||||
```
|
||||
|
||||
To verify the bundle is loaded in your environment:
|
||||
|
||||
```bash
|
||||
lfx extension list
|
||||
```
|
||||
|
||||
## IBM watsonx.ai
|
||||
|
||||
The **IBM watsonx.ai** component generates text using [supported foundation models](https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx) in [IBM watsonx.ai](https://www.ibm.com/products/watsonx-ai).
|
||||
|
||||
@ -250,7 +250,6 @@ It accepts the following parameters:
|
||||
<!--
|
||||
* AgentQL
|
||||
* Confluence
|
||||
* Firecrawl
|
||||
* Git
|
||||
* Home Assistant
|
||||
* Jigsawstack
|
||||
|
||||
@ -43,7 +43,7 @@ The following example uses the **If-Else** component to check incoming chat mess
|
||||
|
||||
The **Case True** message is sent from the **True** output port when the match condition evaluates to true.
|
||||
|
||||
No message is set for **Case False** so the component doesn't emit a message when the condition evaluates to false.
|
||||
**Case False** is left blank, so when the condition evaluates to false the component routes the original **Text Input** to the **False** output port.
|
||||
|
||||
3. Depending on what you want to happen when the outcome is **True**, add components to your flow to execute that logic:
|
||||
|
||||
@ -92,3 +92,12 @@ The chat output should reflect the instructions in your prompts based on the reg
|
||||
| true_result | Message | Output parameter. The output produced when the condition is true. |
|
||||
| false_result | Message | Output parameter. The output produced when the condition is false. |
|
||||
|
||||
:::note Case True and Case False behavior
|
||||
**Case True** and **Case False** are optional override messages for the **True** and **False** output ports.
|
||||
|
||||
- When you set a value, that message is emitted on the matching branch.
|
||||
- When you leave one blank, the component routes the original **Text Input** to that branch instead of emitting an empty message.
|
||||
|
||||
To emit something other than the input on a matched branch, set the corresponding **Case** message.
|
||||
:::
|
||||
|
||||
|
||||
@ -8,6 +8,11 @@ import PartialLegacy from '@site/docs/_partial-legacy.mdx';
|
||||
|
||||
<PartialLegacy />
|
||||
|
||||
## Legacy Input / Output components
|
||||
|
||||
* **Text Input**: Replaced by the [**Chat Input** component](/chat-input-and-output).
|
||||
* **Text Output**: Replaced by the [**Chat Output** component](/chat-input-and-output).
|
||||
|
||||
## Legacy Data components
|
||||
|
||||
The following Data components are in legacy status:
|
||||
|
||||
@ -81,7 +81,7 @@ If you don't include the package imports in the chat, the agent can still create
|
||||
|
||||
To pass inputs to the **Python Interpreter** component, you need to customize the component's code to add input fields.
|
||||
After the input field is added to the component code, the port becomes available for connections.
|
||||
For example, to connect a [**Text** component](/text-input-and-output) and pass a URL value to the **Python Interpreter** component, do the following:
|
||||
For example, to connect a [**Chat Input** component](/chat-input-and-output) and pass a URL value to the **Python Interpreter** component, do the following:
|
||||
|
||||
1. Add a **Python Interpreter** component to your flow.
|
||||
2. To modify the **Python Interpreter** component's code, click <Icon name="Code" aria-hidden="true"/> **Edit Code**.
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
---
|
||||
title: Text Input and Output
|
||||
slug: /text-input-and-output
|
||||
---
|
||||
|
||||
import Icon from "@site/src/components/icon";
|
||||
import PartialParams from '@site/docs/_partial-hidden-params.mdx';
|
||||
|
||||
:::warning
|
||||
**Text Input and Output** components aren't supported in the **Playground**.
|
||||
Because the data isn't formatted as a chat message, the data doesn't appear in the **Playground**, and you can't chat with your flow in the **Playground**.
|
||||
|
||||
If you want to chat with a flow in the **Playground**, you must use the [**Chat Input and Output** components](/chat-input-and-output).
|
||||
:::
|
||||
|
||||
**Text Input and Output** components are designed for flows that ingest or emit simple text strings.
|
||||
These components don't support full conversational interactions.
|
||||
|
||||
Passing chat-like metadata to a **Text Input and Output** component doesn't change the component's behavior; the result is still a simple text string.
|
||||
|
||||
## Text Input
|
||||
|
||||
The **Text Input** component accepts a text string input that is passed to other components as [`Message` data](/data-types) containing only the provided input text string in the `text` attribute.
|
||||
|
||||
It accepts only **Text** (`input_value`), which is the text supplied as input to the component.
|
||||
This can be entered directly into the component or passed as `Message` data from other components.
|
||||
|
||||
Initial input _shouldn't_ be provided as a complete `Message` object because the **Text Input** component constructs the `Message` object that is then passed to other components in the flow.
|
||||
|
||||
## Text Output
|
||||
|
||||
The **Text Output** component ingests [`Message` data](/data-types#message) from other components, emitting only the `text` attribute in a simplified `Message` object.
|
||||
|
||||
It accepts only **Text** (`input_value`), which is the text to be ingested and output as a string.
|
||||
This can be entered directly into the component or passed as `Message` data from other components.
|
||||
|
||||
@ -47,6 +47,18 @@ To allow-list custom components, use the `LANGFLOW_COMPONENTS_PATH` environment
|
||||
|
||||
If both environment variables are set, Langflow builds one combined set of components from the custom index _and_ from `LANGFLOW_COMPONENTS_PATH`. If the same component category name exists in both, `LANGFLOW_COMPONENTS_PATH` replaces that whole category from the custom index.
|
||||
|
||||
### Disable the allow-list bypass
|
||||
|
||||
The allow-list behavior above assumes the admin controls which env vars are set at startup. In deployments where that assumption doesn't hold, set:
|
||||
|
||||
```bash
|
||||
LANGFLOW_ALLOW_COMPONENTS_PATHS_OVERRIDE=false
|
||||
```
|
||||
|
||||
When this is `false` **and** `LANGFLOW_ALLOW_CUSTOM_COMPONENTS=false`, components contributed by `LANGFLOW_COMPONENTS_PATH` and `LANGFLOW_COMPONENTS_INDEX_PATH` are ignored and no longer bypass the block.
|
||||
|
||||
Default is `true`, which preserves existing behavior. The setting has no effect while `LANGFLOW_ALLOW_CUSTOM_COMPONENTS=true`, since nothing is being bypassed.
|
||||
|
||||
For more information, see:
|
||||
|
||||
* [Environment variables](/environment-variables#visual-editor-and-playground-behavior)
|
||||
|
||||
@ -1,194 +0,0 @@
|
||||
---
|
||||
title: Production install pattern for Extensions
|
||||
description: How Langflow discovers and runs extension bundles in production across Modes A, B, and C.
|
||||
sidebar_position: 40
|
||||
slug: /deployment-extensions-production
|
||||
---
|
||||
|
||||
This guide describes the production install pattern for Langflow Extensions in Modes A, B, and C of the Bundle Separation iteration. The mechanism is intentionally simple: **Python distributions installed into the environment are the primary install path**, and an optional **seed directory** is supported for Docker images that prefer an explicit on-disk layout.
|
||||
|
||||
:::info First-delivery scope
|
||||
This page documents the first-delivery slice of the Bundle Separation work. It covers discovery and registration of installed Extensions; the corresponding loader, dev server, and reload features ship in the same first-delivery cycle. Mutation verbs (`enable`, `disable`, `install`, `uninstall`) ship in follow-up work; **runtime mutation of installed Extensions is intentionally not supported**.
|
||||
:::
|
||||
|
||||
## How discovery works
|
||||
|
||||
Langflow scans two production install sources at server startup and registers each Extension at the `@official` slot:
|
||||
|
||||
1. **Installed Python distributions** — every distribution returned by `importlib.metadata.distributions()` that ships an `extension.json` (or a `[tool.langflow.extension]` section in `pyproject.toml`).
|
||||
2. **Seed directories** — every immediate subdirectory of `$LANGFLOW_SEED_DIR` (or `/opt/langflow/bundles/` when the env var is unset) that contains a valid manifest.
|
||||
|
||||
Each Extension registered via these paths is **read-only at runtime**:
|
||||
|
||||
* `autoUpdate` is forced to `false`.
|
||||
* Mutation through the registry service raises a typed error with code `installed-extension-immutable` or `seed-directory-immutable`.
|
||||
* No HTTP route exists for installing or uninstalling Extensions; the router-trust CI guard blocks any attempt to add one.
|
||||
|
||||
To change which Extensions are installed in Mode B/C, **rebuild the image** (or modify the seed directory layout) and restart the server.
|
||||
|
||||
## Mode A: developer laptop
|
||||
|
||||
In Mode A you control the Python environment directly:
|
||||
|
||||
```bash
|
||||
pip install langflow lfx-openai lfx-anthropic
|
||||
langflow run
|
||||
```
|
||||
|
||||
On the next start-up Langflow discovers `lfx-openai` and `lfx-anthropic`, registers each at `@official`, and loads their components into the palette. To inspect the result, use the read-only list CLI:
|
||||
|
||||
```bash
|
||||
lfx extension list
|
||||
```
|
||||
|
||||
```text
|
||||
ID VERSION BUNDLE SLOT SOURCE STATUS
|
||||
lfx-anthropic 0.4.1 anthropic @official installed discovered
|
||||
lfx-openai 1.2.0 openai @official installed discovered
|
||||
```
|
||||
|
||||
`lfx extension list --format json` emits the same data in a stable JSON envelope suitable for piping into deployment tooling.
|
||||
|
||||
## Mode B: self-hosted Docker image
|
||||
|
||||
The Mode B production install pattern is `pip install` baked into the image. There are no runtime install commands.
|
||||
|
||||
### Dockerfile template
|
||||
|
||||
```dockerfile
|
||||
# syntax=docker/dockerfile:1.6
|
||||
FROM langflowai/langflow:latest
|
||||
|
||||
# Install the bundles you want available in this image. Each `lfx-*`
|
||||
# distribution must ship a v0 extension manifest (extension.json or a
|
||||
# [tool.langflow.extension] section in pyproject.toml).
|
||||
RUN pip install --no-cache-dir \
|
||||
lfx-openai==1.2.0 \
|
||||
lfx-anthropic==0.4.1 \
|
||||
lfx-qdrant==2.5.0
|
||||
|
||||
# Optional: bake an explicit on-disk seed directory. Useful when an
|
||||
# Extension does not yet have a published wheel but you want it pinned
|
||||
# alongside the other bundles in the image. The directory layout is
|
||||
# `<seed-root>/<extension-id>/extension.json` plus the bundle contents.
|
||||
COPY bundles/ /opt/langflow/bundles/
|
||||
|
||||
CMD ["langflow", "run", "--host", "0.0.0.0", "--port", "7860"]
|
||||
```
|
||||
|
||||
Verify the install at image-build time:
|
||||
|
||||
```bash
|
||||
docker run --rm langflow-image lfx extension list --format json
|
||||
```
|
||||
|
||||
A non-zero exit code means at least one manifest failed to discover. The JSON output includes an `errors` array with typed `code` / `message` / `hint` fields so build pipelines can surface the failure in their logs.
|
||||
|
||||
### Sealing in extension state
|
||||
|
||||
Because installed Extensions are immutable at runtime, you can safely mount the Langflow state directory as a writable volume without worrying about runtime drift in which Extensions are loaded. Any change to the installed Extensions requires rebuilding the image.
|
||||
|
||||
## Mode C: managed SaaS / Kubernetes
|
||||
|
||||
The Mode C platform operator owns the Dockerfile in exactly the same way as Mode B; the only difference is that operator-controlled image promotion replaces direct `docker build` access. The example Kubernetes layout below is suitable for both Astra and self-hosted Langflow Cloud:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: langflow
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: langflow
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: langflow
|
||||
spec:
|
||||
containers:
|
||||
- name: langflow
|
||||
image: registry.example.com/langflow:1.10.0-with-bundles
|
||||
env:
|
||||
# Override the seed-directory location. Multiple paths can be
|
||||
# joined with the OS path separator (':' on Linux); the same
|
||||
# syntax that LANGFLOW_COMPONENTS_PATH uses.
|
||||
- name: LANGFLOW_SEED_DIR
|
||||
value: "/opt/langflow/bundles:/etc/langflow/operator-bundles"
|
||||
ports:
|
||||
- containerPort: 7860
|
||||
volumeMounts:
|
||||
- name: state
|
||||
mountPath: /var/lib/langflow
|
||||
volumes:
|
||||
- name: state
|
||||
persistentVolumeClaim:
|
||||
claimName: langflow-state
|
||||
```
|
||||
|
||||
Operators who want to inspect the running Extensions can `kubectl exec` into the pod and run `lfx extension list`. The command is read-only and safe to run against a live instance.
|
||||
|
||||
### Layered image strategy
|
||||
|
||||
When a platform team needs to ship many provider-specific images from a common base, the conventional layout is a base image with the core Langflow runtime and per-team images that add Extensions on top:
|
||||
|
||||
```dockerfile
|
||||
# base.Dockerfile
|
||||
FROM langflowai/langflow:1.10.0
|
||||
# ... shared configuration ...
|
||||
|
||||
# team-a.Dockerfile
|
||||
FROM langflowai/langflow:1.10.0
|
||||
RUN pip install --no-cache-dir lfx-openai==1.2.0 lfx-team-a-private==0.3.0
|
||||
```
|
||||
|
||||
Because Extension discovery is fully driven by `importlib.metadata`, no additional configuration is required to surface the layered installs.
|
||||
|
||||
## Packaging convention for bundle authors
|
||||
|
||||
`pip install` only ships files declared as part of the wheel. Files at the source-tree root (`extension.json` next to `pyproject.toml`) do **not** survive into `site-packages` unless they are explicitly added via `package-data`. The reliable convention is to ship the manifest **inside** the Python package:
|
||||
|
||||
```
|
||||
lfx-openai/ # source tree
|
||||
├── pyproject.toml # declares the distribution
|
||||
└── lfx_openai/ # the importable package
|
||||
├── __init__.py
|
||||
├── extension.json # the manifest, shipped via package-data
|
||||
└── openai/ # the bundle (path matches bundles[].path)
|
||||
├── __init__.py
|
||||
└── component.py
|
||||
```
|
||||
|
||||
The corresponding `pyproject.toml` declaration:
|
||||
|
||||
```toml
|
||||
[tool.setuptools]
|
||||
packages = ["lfx_openai", "lfx_openai.openai"]
|
||||
include-package-data = true
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"lfx_openai" = ["extension.json", "**/*"]
|
||||
|
||||
[project]
|
||||
name = "lfx-openai"
|
||||
version = "1.2.0"
|
||||
```
|
||||
|
||||
Discovery walks `importlib.metadata.distributions()` and looks for `extension.json` anywhere in the distribution's `RECORD`. With the layout above, the manifest lands at `site-packages/lfx_openai/extension.json` and is found automatically; `bundles[].path = "openai"` resolves to the sibling `openai/` directory.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Likely cause | Fix |
|
||||
|---------|--------------|-----|
|
||||
| `lfx extension list` returns empty | The bundle distribution did not include `extension.json` in its built artifact | Move the manifest inside the Python package and declare it under `[tool.setuptools.package-data]` (see the layout above). |
|
||||
| `manifest-invalid` error in list output | A bundle's manifest fails Pydantic validation | Run `lfx extension validate` against the source tree to see per-field errors. |
|
||||
| `seed-directory-not-found` error | `$LANGFLOW_SEED_DIR` is set to a path that does not exist | Either point the env var at an existing directory or unset it to fall back to `/opt/langflow/bundles`. |
|
||||
| `duplicate-extension-id` error | The same `extension_id` appears in both an installed distribution and a seed directory | Pick exactly one source. The runtime refuses to register the same id twice. |
|
||||
| `installed-extension-immutable` raised when calling the registry service directly | Code is attempting a runtime mutation that the immutability invariant blocks | Mutate the install (rebuild the image, edit the lockfile) and restart Langflow. There is no runtime equivalent. |
|
||||
|
||||
## See also
|
||||
|
||||
* `lfx extension validate` — the offline manifest checker run before publish.
|
||||
* `lfx extension list` — the read-only inspector documented above.
|
||||
* The Bundle Separation Developer Guide for the wider iteration scope and the full list of deferred features.
|
||||
105
docs/docs/Deployment/deployment-lfx-compatibility.mdx
Normal file
105
docs/docs/Deployment/deployment-lfx-compatibility.mdx
Normal file
@ -0,0 +1,105 @@
|
||||
---
|
||||
title: LFX and Langflow version compatibility
|
||||
slug: /lfx-compatibility
|
||||
---
|
||||
|
||||
Langflow and LFX are versioned together on the same `major.minor` line, so you can always tell which LFX release runs which flows.
|
||||
|
||||
## Compatibility contract
|
||||
|
||||
**LFX `X.Y.N` is guaranteed compatible with any flow exported from Langflow `X.Y.M`.**
|
||||
|
||||
The major and minor numbers must match.
|
||||
The patches `.N` for LFX and `.M` for Langflow are released independently. An LFX patch fix does not require a Langflow patch release, and vice versa.
|
||||
|
||||
When LFX and a flow share the same `major` version but differ on `minor`, such as a flow exported from Langflow 1.9.x run with LFX 1.10.x, individual components may have been updated since the flow was saved.
|
||||
Use `lfx upgrade` to check compatibility and apply safe automatic fixes before running or serving such a flow.
|
||||
For more information, see [Check and upgrade flows with lfx upgrade](#check-and-upgrade-flows-with-lfx-upgrade).
|
||||
|
||||
## Compatibility matrix
|
||||
|
||||
| Langflow version | Compatible LFX version | Notes |
|
||||
|---|---|---|
|
||||
| 1.10.x | 1.10.x | Fully compatible. Same `major.minor` line. |
|
||||
| 1.11.x | 1.11.x | Fully compatible. Same `major.minor` line. |
|
||||
| 1.9.x or earlier | 1.10.x | Use `lfx upgrade --upgrade-flow=safe` to apply safe component schema upgrades before running. |
|
||||
| Any 1.x.x | 0.5.x | LFX 0.5.x was a standalone release before version alignment. It is no longer compatible with Langflow 1.10+ flows. |
|
||||
|
||||
## Check and upgrade flows with `lfx upgrade`
|
||||
|
||||
The `lfx upgrade` command inspects a flow against the built-in component registry and reports any components that have become incompatible or that have safe automatic upgrades available.
|
||||
|
||||
`lfx upgrade` compares each component in the flow against a bundled index at `_assets/component_index.json` that ships with the LFX release.
|
||||
|
||||
Run it with no flags to get a read-only compatibility report. The command reads your flow JSON, walks through every component node, and prints one line per node with its status. It does not modify the file.
|
||||
|
||||
```bash
|
||||
lfx upgrade my-flow.json
|
||||
```
|
||||
|
||||
Example output:
|
||||
|
||||
```text
|
||||
[SAFE] Agent (AgentComponent) - id: abc123
|
||||
[OK] Chat Output (ChatOutput) - id: def456
|
||||
```
|
||||
|
||||
| CLI output | What it means | Example |
|
||||
|---|---|---|
|
||||
| `[OK]` | Component matches the current registry. | No action needed. |
|
||||
| `[SAFE]` | Component code changed, but ports and fields your flow uses still line up. `lfx upgrade --write` can apply this update automatically. | An output that previously emitted only `Message` now also supports `Data`. |
|
||||
| `[BREAKING]` | A port or field your flow relies on was removed. | An input that accepted `Message` and `Data` now accepts only `Message` and your flow uses a `Data` component. |
|
||||
| `[BLOCKED]` | The component type no longer exists in this LFX build. | A bundle-only component is not installed in your environment. |
|
||||
|
||||
To apply fixes or enforce stricter checks, run:
|
||||
|
||||
```bash
|
||||
# Apply all safe upgrades and overwrite the file
|
||||
lfx upgrade my-flow.json --write
|
||||
|
||||
# Fail if any safe upgrades are still pending (use after a dry run before running `--write`)
|
||||
lfx upgrade my-flow.json --strict
|
||||
```
|
||||
|
||||
## Inline compatibility checking with `--upgrade-flow`
|
||||
|
||||
Both `lfx run` and `lfx serve` accept an `--upgrade-flow` option so you can apply compatibility checks at execution without a separate `lfx upgrade` step.
|
||||
|
||||
```bash
|
||||
# Check only — fail if any component is blocked
|
||||
lfx run my-flow.json --upgrade-flow=check "Hello world"
|
||||
|
||||
# Apply safe upgrades in memory, then run
|
||||
lfx run my-flow.json --upgrade-flow=safe "Hello world"
|
||||
|
||||
# Same options for serve
|
||||
lfx serve my-flow.json --upgrade-flow=safe
|
||||
```
|
||||
|
||||
| Mode | Behavior |
|
||||
|---|---|
|
||||
| `check` | Reports compatibility issues and exits non-zero if any component is blocked. No changes are written to disk. |
|
||||
| `safe` | Applies all safe upgrades in memory, then runs or serves the flow. Blocked components still cause a non-zero exit. |
|
||||
|
||||
## Pin versions
|
||||
|
||||
Pin `lfx~=1.10.0` in `requirements.txt` to track all patch releases for a given Langflow minor without crossing into the next minor automatically.
|
||||
|
||||
```text
|
||||
# requirements.txt — allows 1.10.1, 1.10.2, … but not 1.11.0
|
||||
lfx~=1.10.0
|
||||
```
|
||||
|
||||
## Migrating from LFX 0.5.x to 1.10.0
|
||||
|
||||
LFX was realigned from its standalone `0.5.x` line onto Langflow's `major.minor` line, so the version number jumps from `0.5.0` to `1.10.0` in one release. This is a version-numbering change, not 95 minor releases of new features.
|
||||
|
||||
This jump can affect existing dependency pins in unexpected ways:
|
||||
|
||||
| Pin style | Effect |
|
||||
|---|---|
|
||||
| `lfx==0.5.x` or `lfx<1.0` | Does not upgrade. The deployment stays on 0.5.x. |
|
||||
| `lfx>=0.5,<1` | Does not upgrade. The upper bound excludes 1.10.0. |
|
||||
| `lfx>=0.5` (no upper bound) | Upgrades automatically to 1.10.0 on the next install. |
|
||||
|
||||
Going forward, pin with `lfx~=1.10.0` so you receive compatible patches without silently crossing minor lines.
|
||||
@ -3,12 +3,20 @@ title: Deploy Langflow with multiple workers
|
||||
slug: /deployment-multi-worker
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
By default, Langflow runs with a single worker process and stores build job state in memory.
|
||||
|
||||
A single-worker process is fine for development, but it doesn't scale when you run more than one worker.
|
||||
A flow build started on worker A cannot be polled or streamed from worker B because the in-memory job queue is _per-process_.
|
||||
A flow build started on worker **A** cannot be polled or streamed from worker **B** because the in-memory job queue is _per-process_.
|
||||
|
||||
A multi-worker deployment runs more than one worker process on the same host. Concurrency can be increased by increasing the number of `LANGFLOW_WORKERS`, but each process keeps its own in-memory build queue unless you add a shared store.
|
||||
|
||||
A Redis-backed job queue stores build events in [Redis Streams](https://redis.io/docs/latest/develop/data-types/streams/), so any worker can pick up and serve any job's events.
|
||||
To configure a multi-worker Langflow process, follow the steps to enable the Redis job queue.
|
||||
|
||||
After the Redis job queue is configured, you can optionally follow [recommended Gunicorn settings](#recommended-gunicorn-settings) to reduce memory use and keep workers healthy. This tuning applies to Linux production hosts only. On Windows and macOS, `langflow run` uses a single Uvicorn process.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@ -325,3 +333,62 @@ The Redis backend response includes the following fields:
|
||||
| `cancel_stats.activity_touch_errors` | Number of errors writing the client heartbeat key to Redis. |
|
||||
| `cancel_stats.activity_get_errors` | Number of errors reading the client heartbeat key from Redis. |
|
||||
| `cancel_stats.activity_parse_errors` | Number of malformed heartbeat values encountered by the watchdog. |
|
||||
|
||||
## Recommended Gunicorn settings
|
||||
|
||||
:::tip
|
||||
This tuning is optional, and it applies to Linux only. It does not replace the Redis job queue.
|
||||
|
||||
On Windows and macOS, `langflow run` uses one Uvicorn process, so `LANGFLOW_GUNICORN_PRELOAD` and `GUNICORN_CMD_ARGS` have no effect.
|
||||
:::
|
||||
|
||||
These recommendations are starting points from Langflow engineering benchmarks on Linux multi-worker deployments.
|
||||
After you apply these values, monitor performance, and then make adjustments using `htop` or `btop` while you run flows.
|
||||
|
||||
Add the starter values to your server's `.env` file after the [Redis job queue](#enable-the-redis-job-queue) settings, then restart Langflow. Copy the block that best matches your server:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="small" label="Small / dev (4–8 GB RAM)" default>
|
||||
|
||||
Start with fewer workers so Langflow and local databases do not hit OOM.
|
||||
|
||||
```text
|
||||
LANGFLOW_WORKERS=5
|
||||
LANGFLOW_WORKER_TIMEOUT=120
|
||||
LANGFLOW_GUNICORN_PRELOAD=true
|
||||
GUNICORN_CMD_ARGS="--max-requests 100 --max-requests-jitter 20"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="standard" label="Standard API (12 GB RAM)">
|
||||
|
||||
A balanced default for a dedicated Langflow host.
|
||||
|
||||
```text
|
||||
LANGFLOW_WORKERS=15
|
||||
LANGFLOW_WORKER_TIMEOUT=300
|
||||
LANGFLOW_GUNICORN_PRELOAD=true
|
||||
GUNICORN_CMD_ARGS="--max-requests 250 --max-requests-jitter 50"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="heavy" label="Heavy multi-agent (24 GB+ RAM)">
|
||||
|
||||
Agent loops use more RAM per request, so a lower `--max-requests` value restarts workers more often to keep long-term usage consistent.
|
||||
|
||||
```text
|
||||
LANGFLOW_WORKERS=30
|
||||
LANGFLOW_WORKER_TIMEOUT=600
|
||||
LANGFLOW_GUNICORN_PRELOAD=true
|
||||
GUNICORN_CMD_ARGS="--max-requests 150 --max-requests-jitter 30"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
- **`LANGFLOW_WORKERS`** — How many Gunicorn worker processes run for concurrency.
|
||||
- **`LANGFLOW_WORKER_TIMEOUT`** — How long a worker may handle a single request before Gunicorn kills it. Raise it if you expect long agent runs.
|
||||
- **`LANGFLOW_GUNICORN_PRELOAD`** — Loads the app once in the Gunicorn master before workers fork so Linux can share memory across workers through Copy-on-Write. Recommended to leave enabled in multi-worker deployments for memory savings. Safe to leave off; behavior matches older releases when `false`.
|
||||
- **`GUNICORN_CMD_ARGS`** — Recycles workers after a set number of requests so memory usage growth does not continuously accumulate. `--max-requests` restarts a worker after it processes that many requests; `--max-requests-jitter` adds a random extra 0–N requests on top of that limit for each worker. Spreading restarts over time avoids every worker reloading at once. If RAM usage increases over time, lower `--max-requests` before you lower worker count.
|
||||
|
||||
For more information, see the [Scaling Langflow blog post](https://www.langflow.org/blog/scaling-langflow).
|
||||
@ -1,19 +1,26 @@
|
||||
---
|
||||
title: Deploy Langflow on watsonx Orchestrate
|
||||
title: Deploy flows on watsonx Orchestrate
|
||||
slug: /deployment-wxo
|
||||
---
|
||||
|
||||
import Icon from "@site/src/components/icon";
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import PartialGlobalModelProviders from '@site/docs/_partial-global-model-providers.mdx';
|
||||
|
||||
:::tip
|
||||
As of Langflow 1.10.x, the IBM watsonx Orchestrate deployments feature is behind a feature flag. To enable it, set the following environment variable before starting Langflow:
|
||||
|
||||
```bash
|
||||
LANGFLOW_FEATURE_WXO_DEPLOYMENTS=true
|
||||
```
|
||||
:::
|
||||
|
||||
Create a flow and deploy it to [IBM watsonx Orchestrate](https://www.ibm.com/docs/en/watsonx/watson-orchestrate/base?topic=getting-started-watsonx-orchestrate).
|
||||
|
||||
Deploying a flow on IBM watsonx Orchestrate is different from the other Langflow deployment options.
|
||||
This workflow **does not** deploy a full-featured Langflow server and flow builder UI.
|
||||
Instead, Langflow packages your selected flow and flow version, and then publishes it to IBM watsonx Orchestrate as a tool that an IBM watsonx Orchestrate agent can call.
|
||||
Langflow is used to build and configure the flow, while IBM watsonx Orchestrate hosts the agent experience and invokes the deployed flow as part of that agent's toolset.
|
||||
Instead, Langflow packages your selected flow and flow version, and then publishes it to IBM watsonx Orchestrate as a wxO Tool that a wxO Agent can execute.
|
||||
Langflow is used to build and configure the flow, while IBM watsonx Orchestrate hosts the wxO Agent experience and invokes the deployed flow as part of that wxO Agent's toolset.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@ -25,7 +32,7 @@ Langflow is used to build and configure the flow, while IBM watsonx Orchestrate
|
||||
|
||||
1. Create a flow in the Langflow UI, such as the Simple Agent starter flow in the [Quickstart](/get-started-quickstart).
|
||||
|
||||
2. Click <Icon name="Rocket" aria-hidden="true"/> **Deploy**.
|
||||
2. To deploy this flow to watsonx Orchestrate, click <Icon name="Rocket" aria-hidden="true"/> **Deploy**.
|
||||
The **Provider** pane opens.
|
||||
|
||||
3. Enter the **Name**, **Service Instance URL**, and **API Key** from your IBM watsonx Orchestrate instance.
|
||||
@ -40,18 +47,29 @@ These values are found in the **Settings** page of your IBM watsonx Orchestrate
|
||||
|
||||
4. Click **Next**.
|
||||
The **Deployment Type** pane opens.
|
||||
5. Enter a **Type**, **Agent Name**, **Model**, and **Description**.
|
||||
5. Enter a **Type**, **Name**, **Model**, and **Description** for your wxO Agent, and then click **Next**.
|
||||
|
||||
The **Type** is always **Agent**. The deployed flow is an IBM watsonx Orchestrate agent with your flow available as a tool the agent can call.
|
||||
The **Type** is always **Agent**. The deployed flow is a wxO Agent with your flow available as a wxO Tool the wxO Agent can call.
|
||||
|
||||
The **Model** list is populated from the connected watsonx Orchestrate instance, not Langflow.
|
||||
|
||||
6. To open the **Attach Flows** pane, click the **Attach Flows** tab. Select a flow and flow version to deploy.
|
||||
7. To open the **Create Connections** pane, click the **Create Connections** tab. Create a new connection, or select an existing connection to bind to the flow.
|
||||
6. In the **Flows** pane, in the **Available** list, select a flow and flow version to deploy.
|
||||
For this example, select the simple agent flow you created.
|
||||
By default, the flow is version 1.
|
||||
|
||||
Optionally, to attach an additional version, select the same flow again from the list and choose a different version.
|
||||
You can attach multiple versions of the same flow to a single wxO Agent.
|
||||
Each flow version is deployed as a separate wxO Tool with a unique name to prevent naming collisions in watsonx Orchestrate.
|
||||
|
||||
|
||||
7. To configure the watsonx Orchestrate connection, click **Create Connection**.
|
||||
In this tab, you can create a new connection, or select an existing connection to bind to the flow.
|
||||
|
||||
To create a new connection, do the following:
|
||||
|
||||
1. Enter a **Connection Name** and any environment variables the flow requires, such as the `OPENAI_API_KEY`. Langflow auto-detects global variables from the flow JSON file, and you can add additional variables.
|
||||
1. Enter a **Connection Name** and any environment variables the flow requires, such as the `OPENAI_API_KEY` for the simple agent flow.
|
||||
Langflow auto-detects global variables from the flow JSON file.
|
||||
Click <Icon name="Plus" aria-hidden="true"/> **Add variable** to add any additional environment variables.
|
||||
|
||||
2. To add the new connection to the list of available connections, click **Create Connection**.
|
||||
|
||||
@ -60,8 +78,6 @@ These values are found in the **Settings** page of your IBM watsonx Orchestrate
|
||||
:::tip
|
||||
To bind the connection to the flow **without** environment variable binding, click **Skip**, and then click **Next**.
|
||||
:::
|
||||
|
||||
For more information, see [Build flows](../Flows/concepts-flows.mdx#save-and-restore-flow-versions).
|
||||
8. Click **Next**. The **Review & Confirm** pane opens.
|
||||
9. Confirm the deployment values are correct, and then click <Icon name="Rocket" aria-hidden="true"/> **Deploy**.
|
||||
|
||||
@ -70,12 +86,20 @@ These values are found in the **Settings** page of your IBM watsonx Orchestrate
|
||||
In the Langflow UI, `Deployment successful` indicates your deployment succeeded.
|
||||
|
||||
:::tip
|
||||
If you get an error that the tool name already exists on your deployment, click <Icon name="Pencil" aria-hidden="true"/> **Edit** to change the tool name.
|
||||
If you get an error that the wxO Tool name already exists on your deployment, click <Icon name="Pencil" aria-hidden="true"/> **Edit** to change the wxO Tool name.
|
||||
:::
|
||||
|
||||
10. Click **Test** to open a chat window with your agent on watsonx Orchestrate.
|
||||
Enter a question, and the agent responds using the connected flow as a tool.
|
||||
11. Navigate to your IBM watsonx Orchestrate deployment, and then confirm that your Langflow flow is listed as an agent.
|
||||
10. Click **Test** to open a chat window with your wxO Agent on watsonx Orchestrate.
|
||||
Enter a question, and the wxO Agent responds using the connected flow as a wxO Tool.
|
||||
11. In your IBM watsonx Orchestrate deployment, navigate to **Agent chat** > **Manage Agents**, and confirm that a new wxO Agent is listed with your Langflow flow attached as a wxO Tool.
|
||||
12. To test the wxO Agent in watsonx Orchestrate, click **Talk to agent**, and then enter a question.
|
||||
The wxO Agent responds using your Langflow flow as a wxO Tool.
|
||||
13. To deploy the wxO Agent to a live environment, click **Deploy**.
|
||||
14. Optionally, click **Activate agent monitoring**.
|
||||
15. To view your wxO Agent's metrics, click **Agent chat** > **Agent analytics**, and then select your wxO Agent.
|
||||
This page includes metrics for your wxO Agent, such as Total messages, Failed messages, and Latency average.
|
||||
To view traces for each wxO Agent request, click **Trace Detail**.
|
||||
For more information, see [Monitoring agents](https://www.ibm.com/docs/en/watsonx/watson-orchestrate/base?topic=monitoring-agents).
|
||||
|
||||
## Manage deployments in Langflow
|
||||
|
||||
@ -83,7 +107,7 @@ From the **Projects** page, click **Deployments** to open the deployment managem
|
||||
|
||||
* **Deployments**:
|
||||
|
||||
A **Deployment** is a published watsonx Orchestrate agent created from a specific Langflow flow version. Deployment details include the agent name, type, attached flows, model, and the IBM watsonx Orchestrate environment it belongs to.
|
||||
A **Deployment** is a published wxO Agent equipped with one or more Langflow flows as wxO Tools. Deployment details include the wxO Agent name, type, attached flows, model, and the IBM watsonx Orchestrate environment it belongs to.
|
||||
|
||||
Use the **Deployments** tab to create, update, view, and delete flow deployments in Langflow.
|
||||
|
||||
@ -97,18 +121,22 @@ From the **Projects** page, click **Deployments** to open the deployment managem
|
||||
|
||||
## Send requests to your flow
|
||||
|
||||
After you deploy your flow to IBM watsonx Orchestrate, you can connect to it through the Langflow deployment run endpoints.
|
||||
After you deploy your flow to IBM watsonx Orchestrate, you can chat with the wxO Agent through the Langflow deployment run endpoints.
|
||||
|
||||
Don't use the `/run` endpoint for flows deployed to IBM watsonx Orchestrate.
|
||||
Instead use `POST /api/v1/deployments/{deployment_id}/runs` to start a run, and `GET /api/v1/deployments/{deployment_id}/runs/{run_id}` to check its status.
|
||||
|
||||
Endpoint paths must be prefixed with your Langflow server URL, such as `http://localhost:7860`.
|
||||
|
||||
To find your `deployment_id`, navigate to the **Projects** page, click **Deployments**, and select the deployment. The ID is displayed in the deployment details.
|
||||
|
||||
These endpoints require a Langflow API key passed in the `x-api-key` header. To create one, see [API keys and authentication](/api-keys-and-authentication).
|
||||
|
||||
### Create deployment run endpoint
|
||||
|
||||
**Endpoint:** `POST /api/v1/deployments/{deployment_id}/runs`
|
||||
|
||||
**Description:** Start a run for a deployed flow and return a provider-owned run ID that you can poll for status.
|
||||
**Description:** Start a run for a deployed wxO Agent and return a provider-owned run ID that you can poll for status.
|
||||
|
||||
#### Example request
|
||||
|
||||
@ -187,7 +215,7 @@ curl --request POST \
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `provider_data.input` | `string` | Yes | The prompt or message content to send to the deployed agent. |
|
||||
| `provider_data.input` | `string` | Yes | The prompt or message content to send to the deployed wxO Agent. |
|
||||
| `provider_data.thread_id` | `string` | No | Optional thread identifier to continue an existing conversation. |
|
||||
|
||||
#### Example response
|
||||
@ -306,3 +334,5 @@ curl --request GET \
|
||||
|
||||
Check `provider_data.status` to determine whether the run is still processing or has finished.
|
||||
When the status is `completed`, read the output from `provider_data.result`.
|
||||
|
||||
If `provider_data.status` is `failed`, the `provider_data.failed_at` timestamp and `provider_data.last_error` field contain details about what went wrong.
|
||||
|
||||
@ -459,6 +459,17 @@ SSRF protection prevents requests to internal or private network resources, such
|
||||
| `LANGFLOW_SSRF_PROTECTION_ENABLED` | Boolean | `False` | Enable SSRF protection for the **API Request** component. When enabled, the component blocks requests to private IP addresses. When disabled, requests are not blocked. |
|
||||
| `LANGFLOW_SSRF_ALLOWED_HOSTS` | List[String] | Not set | A comma-separated list of allowed hosts, IP addresses, or CIDR ranges that can bypass SSRF protection checks. For example: `192.168.1.0/24,10.0.0.5,*.internal.company.local`.|
|
||||
|
||||
### Login rate limiting {#login-rate-limiting}
|
||||
|
||||
The following environment variables configure IP-based rate limiting on the `/login` endpoint to protect against brute-force attacks.
|
||||
When the limit is exceeded, Langflow returns HTTP 429 with a `Retry-After: 60` header.
|
||||
|
||||
| Variable | Format | Default | Description |
|
||||
|----------|--------|---------|-------------|
|
||||
| `LANGFLOW_RATE_LIMIT_PER_MINUTE` | Integer | `5` | Maximum number of login attempts allowed per minute from a single IP address. |
|
||||
| `LANGFLOW_RATE_LIMIT_STORAGE_URI` | String | `memory://` | Storage backend for rate limit counters. Use `memory://` for single-server deployments or `redis://host:port` for multi-server deployments where the limit should be shared across instances. |
|
||||
| `LANGFLOW_RATE_LIMIT_TRUST_PROXY` | Boolean | `False` | When `true`, Langflow reads the client IP from the rightmost `X-Forwarded-For` header entry instead of the direct connection IP. Enable only when Langflow is behind a trusted reverse proxy or load balancer. Do not enable if users can reach Langflow directly, as this allows header spoofing. |
|
||||
|
||||
### LANGFLOW_WEBHOOK_AUTH_ENABLE {#langflow-webhook-auth-enable}
|
||||
|
||||
This variable controls whether API key authentication is required for webhook endpoints.
|
||||
|
||||
@ -231,10 +231,10 @@ In flows with [**Chat Input and Output** components](/chat-input-and-output), `M
|
||||
In these flows, the **Playground** chat interface prints only the `Message` attributes that are relevant to the conversation, such as `text`, `files`, and error messages from `content_blocks`.
|
||||
To see all `Message` attributes, inspect the message logs in the **Playground**.
|
||||
|
||||
In flows with [**Text Input and Output** components](/text-input-and-output), `Message` data is used to pass simple text strings without the chat-related metadata.
|
||||
In flows with **Text Input and Output** components (now legacy), `Message` data is used to pass simple text strings without the chat-related metadata.
|
||||
These components handle `Message` data as independent text strings, not as part of an ongoing conversation.
|
||||
For this reason, a flow with only **Text Input and Output** components isn't compatible with the **Playground**.
|
||||
For more information, see [Text input and output components](/text-input-and-output).
|
||||
Replace them with the [**Chat Input and Output** components](/chat-input-and-output).
|
||||
|
||||
When using the Langflow API, the response includes the `Message` object along with other response data from the flow run.
|
||||
Langflow API responses can be extremely verbose, so your applications must include code to extract relevant data from the response to return to the user.
|
||||
|
||||
@ -112,7 +112,7 @@ If it detects a supported environment variable, then it automatically adopts the
|
||||
LANGFLOW_STORE_ENVIRONMENT_VARIABLES=True
|
||||
LANGFLOW_SUPERUSER=adminuser
|
||||
LANGFLOW_SUPERUSER_PASSWORD=adminpass
|
||||
LANGFLOW_WORKER_TIMEOUT=60000
|
||||
LANGFLOW_WORKER_TIMEOUT=300
|
||||
LANGFLOW_WORKERS=3
|
||||
```
|
||||
|
||||
@ -191,7 +191,7 @@ LANGFLOW_SECRET_KEY=somesecretkey
|
||||
LANGFLOW_STORE_ENVIRONMENT_VARIABLES=True
|
||||
LANGFLOW_SUPERUSER=adminuser
|
||||
LANGFLOW_SUPERUSER_PASSWORD=adminpass
|
||||
LANGFLOW_WORKER_TIMEOUT=60000
|
||||
LANGFLOW_WORKER_TIMEOUT=300
|
||||
LANGFLOW_WORKERS=3
|
||||
```
|
||||
|
||||
@ -239,7 +239,7 @@ Environment="LANGFLOW_SECRET_KEY=somesecretkey"
|
||||
Environment="LANGFLOW_STORE_ENVIRONMENT_VARIABLES=true"
|
||||
Environment="LANGFLOW_SUPERUSER=adminuser"
|
||||
Environment="LANGFLOW_SUPERUSER_PASSWORD=adminpass"
|
||||
Environment="LANGFLOW_WORKER_TIMEOUT=60000"
|
||||
Environment="LANGFLOW_WORKER_TIMEOUT=300"
|
||||
Environment="LANGFLOW_WORKERS=3"
|
||||
```
|
||||
|
||||
@ -292,7 +292,7 @@ Create or edit the `.vscode/tasks.json` file in your project root.
|
||||
"LANGFLOW_STORE_ENVIRONMENT_VARIABLES": "true",
|
||||
"LANGFLOW_SUPERUSER": "adminuser",
|
||||
"LANGFLOW_SUPERUSER_PASSWORD": "adminpass",
|
||||
"LANGFLOW_WORKER_TIMEOUT": "60000",
|
||||
"LANGFLOW_WORKER_TIMEOUT": "300",
|
||||
"LANGFLOW_WORKERS": "3"
|
||||
}
|
||||
},
|
||||
@ -395,7 +395,7 @@ See [Use Langflow as an MCP server](/mcp-server).
|
||||
|
||||
### Multi-worker deployments
|
||||
|
||||
For multi-worker job queue environment variables, including Redis connection settings, TTLs, and cancellation configuration, see [Deploy Langflow with multiple workers](./deployment-multi-worker#configuration-reference).
|
||||
For multi-worker tuning—including the Redis job queue and Gunicorn worker settings—see [Deploy Langflow with multiple workers](./deployment-multi-worker).
|
||||
|
||||
### Monitoring and metrics
|
||||
|
||||
@ -413,30 +413,16 @@ The following environment variables set base Langflow server configuration, such
|
||||
| `LANGFLOW_DEV` | Boolean | `False` | Whether to run Langflow in development mode (may contain bugs). |
|
||||
| `LANGFLOW_OPEN_BROWSER` | Boolean | `False` | Open the system web browser on startup. |
|
||||
| `LANGFLOW_HEALTH_CHECK_MAX_RETRIES` | Integer | `5` | Set the maximum number of retries for Langflow's server status health checks. |
|
||||
| `LANGFLOW_WORKERS` | Integer | `1` | Number of worker processes. |
|
||||
| `LANGFLOW_WORKER_TIMEOUT` | Integer | `300` | Worker timeout in seconds. |
|
||||
| `LANGFLOW_GUNICORN_PRELOAD` | Boolean | `False` | **Experimental.** When `true`, enables Gunicorn `preload_app` (non-Windows): the app is loaded in the master process before worker processes fork. See [Multi-worker deployments](#multi-worker-deployments). |
|
||||
| `LANGFLOW_JOB_QUEUE_TYPE` | String | `asyncio` | Job queue backend. Use `redis` to share queue events across workers. See [Multi-worker deployments](#multi-worker-deployments). |
|
||||
| `LANGFLOW_WORKERS` | Integer | `1` | Number of worker processes. See [Deploy Langflow with multiple workers](./deployment-multi-worker#recommended-gunicorn-settings). |
|
||||
| `LANGFLOW_WORKER_TIMEOUT` | Integer | `300` | Worker timeout in seconds. See [Deploy Langflow with multiple workers](./deployment-multi-worker#recommended-gunicorn-settings). |
|
||||
| `LANGFLOW_GUNICORN_PRELOAD` | Boolean | `False` | Enables Gunicorn `preload_app`. Non-Windows only. See [Deploy Langflow with multiple workers](./deployment-multi-worker#recommended-gunicorn-settings). |
|
||||
| `LANGFLOW_JOB_QUEUE_TYPE` | String | `asyncio` | Job queue backend. Use `redis` for multi-worker deployments. See [Deploy Langflow with multiple workers](./deployment-multi-worker#configuration-reference). |
|
||||
| `LANGFLOW_SSL_CERT_FILE` | String | Not set | Path to the SSL certificate file for enabling HTTPS on the Langflow web server. This is separate from [database SSL connections](/configuration-custom-database#connect-langflow-to-a-local-postgresql-database). |
|
||||
| `LANGFLOW_SSL_KEY_FILE` | String | Not set | Path to the SSL key file for enabling HTTPS on the Langflow web server. This is separate from [database SSL connections](/configuration-custom-database#connect-langflow-to-a-local-postgresql-database). |
|
||||
| `LANGFLOW_DEACTIVATE_TRACING` | Boolean | `False` | Deactivate tracing functionality. |
|
||||
| `LANGFLOW_CELERY_ENABLED` | Boolean | `False` | Enable Celery for distributed task processing. |
|
||||
| `LANGFLOW_ALEMBIC_LOG_TO_STDOUT` | Boolean | `False` | Whether to log Alembic database migration output to stdout instead of a log file. If `true`, Alembic logs to `stdout` and the default log file is ignored. |
|
||||
|
||||
#### Gunicorn configuration
|
||||
|
||||
When running Langflow in a production environment using Gunicorn, you can configure Gunicorn workers using the `GUNICORN_CMD_ARGS` environment variable.
|
||||
|
||||
This is particularly useful to manage worker memory and prevent potential memory leaks over time. You can add it to your `.env` file like this:
|
||||
|
||||
```text
|
||||
GUNICORN_CMD_ARGS="--max-requests 100 --max-requests-jitter 20"
|
||||
```
|
||||
|
||||
Here is what these arguments do:
|
||||
- `--max-requests 100`: Automatically restarts a worker after it has processed 100 requests. This helps prevent memory leaks from accumulating over time.
|
||||
- `--max-requests-jitter 20`: Adds a random jitter of up to 20 requests to the `--max-requests` value. This ensures that all workers don't restart simultaneously, which could cause a brief downtime or spikes in latency.
|
||||
|
||||
### Storage
|
||||
|
||||
For file storage environment variables, see [File storage environment variables](/concepts-file-management#file-storage-environment-variables).
|
||||
@ -457,6 +443,7 @@ See [Telemetry](/contributing-telemetry).
|
||||
| `LANGFLOW_COMPONENTS_PATH` | String | Not set | Path to a directory containing custom components. Typically used if you have local custom components or you are building a Docker image with custom components. |
|
||||
| `LANGFLOW_COMPONENTS_INDEX_PATH` | String | Not set | File path or URL (`http://` or `https://`) to a prebuilt component index JSON file used to populate built-in components in the visual editor. When not set, Langflow uses the included index. Useful for supplying a curated component index, for example in airgapped deployments. For more information, see [Block custom components](../Deployment/deployment-block-custom-components.mdx). |
|
||||
| `LANGFLOW_ALLOW_CUSTOM_COMPONENTS` | Boolean | `True` | If `false`, disables custom components and in-editor editing of component code. This feature is in beta. For more information, see [Block custom components](../Deployment/deployment-block-custom-components.mdx). |
|
||||
| `LANGFLOW_ALLOW_COMPONENTS_PATHS_OVERRIDE` | Boolean | `True` | When `false` alongside `LANGFLOW_ALLOW_CUSTOM_COMPONENTS=false`, components contributed by `LANGFLOW_COMPONENTS_PATH` and `LANGFLOW_COMPONENTS_INDEX_PATH` no longer bypass the block. Has no effect when `LANGFLOW_ALLOW_CUSTOM_COMPONENTS=true`. For more information, see [Block custom components](../Deployment/deployment-block-custom-components.mdx). |
|
||||
| `LANGFLOW_LOAD_FLOWS_PATH` | String | Not set | Path to a directory containing flow JSON files to be loaded on startup. Typically used when creating a Docker image with prepackaged flows. Requires `LANGFLOW_AUTO_LOGIN=True`. |
|
||||
| `LANGFLOW_LOAD_FLOWS_OVERWRITE_ON_NAME_MATCH` | Boolean | `False` | When a flow file in `LANGFLOW_LOAD_FLOWS_PATH` shares a name with an existing DB row but has a different `id`, controls whether to overwrite the existing row. `False` (default) skips with a warning so UI edits are preserved on restart when file UUIDs regenerate. Set to `True` to opt into prepackaged-flows-are-source-of-truth semantics, typically for CI/CD pipelines. |
|
||||
| `LANGFLOW_CREATE_STARTER_PROJECTS` | Boolean | `True` | Whether to create templates during initialization. If `false`, Langflow doesn't create templates, and `LANGFLOW_UPDATE_STARTER_PROJECTS` is treated as `false`. |
|
||||
|
||||
@ -1,137 +0,0 @@
|
||||
---
|
||||
title: Author Langflow Extensions
|
||||
slug: /extensions-author-guide
|
||||
---
|
||||
|
||||
This guide covers how to design, build, and ship a Langflow Extension Bundle from scratch, plus the conventions that make the result safe to maintain alongside the rest of Langflow.
|
||||
|
||||
## Concepts
|
||||
|
||||
Three terms appear constantly in the codebase and the manifest schema. Get them straight before reading further:
|
||||
|
||||
* **Component** — a `Component` subclass with `build()`, `inputs`, and `outputs`. Same definition as in pre-extensions Langflow.
|
||||
* **Bundle** — a named group of components, addressed at runtime as `ext:<bundle>:<Class>@<slot>`. Bundles are the unit of palette grouping and reload.
|
||||
* **Extension** — the **distribution** that ships a Bundle plus a manifest. This is what you `pip install`. v0 requires exactly one Bundle per Extension.
|
||||
|
||||
The runtime "slot" tells you where a Bundle came from. There are two:
|
||||
|
||||
* `@official` — installed pip distributions, the seed directory (`/opt/langflow/bundles` or `$LANGFLOW_SEED_DIR`), and Extensions registered through `lfx extension dev`.
|
||||
* `@extra` — loose subdirectories under `LANGFLOW_COMPONENTS_PATH`. Useful for ad-hoc local dev that you don't want to package.
|
||||
|
||||
The full vocabulary lives in [`BUNDLE_API.md`](https://github.com/langflow-ai/langflow/blob/main/BUNDLE_API.md). That document is the stable surface — anything outside it is internal, and your bundle should not import it.
|
||||
|
||||
## Decide whether to ship as an Extension
|
||||
|
||||
You should ship as an Extension when:
|
||||
|
||||
* The component's runtime dependencies are heavy enough that bundling them with everyone's `pip install langflow` is wasteful.
|
||||
* The release cadence differs from Langflow core (you want to fix a bug in your component without waiting for a Langflow release).
|
||||
* You want to own the issue tracker and the PR review for the component.
|
||||
|
||||
Stick with an in-tree component when:
|
||||
|
||||
* The component is part of a tightly-integrated set with the rest of `lfx.components.*` and would have circular dependencies if extracted.
|
||||
* The runtime dependencies are already in Langflow's transitive set.
|
||||
|
||||
If you're unsure, the deciding criterion in practice is "would I want to release this independently of Langflow?". If the answer is yes, extract it.
|
||||
|
||||
## Repository layout
|
||||
|
||||
The convention every shipped bundle follows (the DuckDuckGo pilot is the reference: see [`src/bundles/duckduckgo`](https://github.com/langflow-ai/langflow/tree/main/src/bundles/duckduckgo)):
|
||||
|
||||
```
|
||||
my-extension/
|
||||
├── README.md
|
||||
├── extension.json
|
||||
├── pyproject.toml
|
||||
└── src/
|
||||
└── lfx_my_extension/
|
||||
├── __init__.py
|
||||
├── extension.json
|
||||
└── components/
|
||||
└── my_bundle/
|
||||
├── __init__.py
|
||||
└── my_component.py
|
||||
```
|
||||
|
||||
Why two `extension.json` files? The outer one is for the developer running `lfx extension validate` against the source tree. The inner one is the copy that ships **inside the wheel**, so `importlib.metadata.files()` can find it after `pip install`. The two should stay byte-identical; the porting script keeps them in sync.
|
||||
|
||||
The component path declared in `extension.json` (`bundles[].path`) is the inner `components/<bundle_name>/` directory. Keeping that name aligned with the bundle name lets saved flows that reference the legacy import path migrate cleanly via a single migration-table entry. See [Manifest reference](./extensions-manifest.mdx) for the full set of fields.
|
||||
|
||||
## Authoring rules
|
||||
|
||||
A few constraints turn into hard CI gates:
|
||||
|
||||
1. **Import only from `lfx.*`.** The bundle is installed against the public `BUNDLE_API` surface, not Langflow internals. `from langflow...` is rejected by validate. Run `grep -r "from langflow" src/lfx_my_extension/` before pushing.
|
||||
2. **Bundle module imports must be side-effect-free.** Top-level I/O (file reads, network calls, `print`) is rejected with `top-level-io-disallowed`. If you need expensive setup, do it in `Component.build()`.
|
||||
3. **No wildcard imports at module top-level** (`from foo import *`). Validate flags this with `import-star-disallowed`.
|
||||
4. **Every Component class must declare `build()`.** Stub it with `return None` if you're scaffolding; the validator surfaces `build-method-missing` otherwise.
|
||||
5. **Bundle names are unique.** If your bundle name collides with an existing one (e.g. shipping a second `openai` bundle), the loader keeps the first-discovered one and emits `duplicate-component-name` for any colliding class names.
|
||||
|
||||
Each rule is enforced by a typed error code with a concrete `hint`. Run `lfx extension validate .` early and often — the validator is intentionally cheap so it fits in a pre-commit hook.
|
||||
|
||||
## Migration table for moved components
|
||||
|
||||
If your Extension extracts a component that was previously in-tree (i.e. anyone might have a saved flow referencing the old path), add a migration-table entry in the same PR that lands the extraction. The table lives at [`src/lfx/src/lfx/extension/migration/migration_table.json`](https://github.com/langflow-ai/langflow/blob/main/src/lfx/src/lfx/extension/migration/migration_table.json) and is **append-only** — CI rejects any removal.
|
||||
|
||||
Three legacy reference forms each get their own entry:
|
||||
|
||||
```json
|
||||
{
|
||||
"import_path": "lfx.components.duckduckgo.duck_duck_go_search_run.DuckDuckGoSearchComponent",
|
||||
"target": "ext:duckduckgo:DuckDuckGoSearchComponent@official",
|
||||
"added_in": "1.10.0"
|
||||
}
|
||||
```
|
||||
|
||||
| Legacy form | Always added? | Why |
|
||||
| --- | --- | --- |
|
||||
| Full import path (`lfx.components.foo.bar.Class`) | Yes | The historical canonical reference in saved flows. |
|
||||
| Package import path (`lfx.components.foo.Class`) | Yes | Some flows referenced the package re-export. |
|
||||
| Bare class name (`Class`) | Only if globally unique | A bare name can match more than one bundle; ambiguous names go in `ambiguous_bare_names` instead and surface `component-name-ambiguous` so the user gets a fix hint instead of the wrong component. |
|
||||
| Pre-Phase-A slot (`ext:foo:Class@official-pre-a`) | Yes | Reserved for future phases that change the slot layout. |
|
||||
|
||||
CI enforces bare-name uniqueness via [`scripts/migrate/check_bare_names.py`](https://github.com/langflow-ai/langflow/blob/main/scripts/migrate/check_bare_names.py). Add the entry once, run the check locally, and the deserializer will rewrite saved flows on load.
|
||||
|
||||
## Reload semantics
|
||||
|
||||
Atomic-swap reload (Mode A only) is what makes the dev loop tolerable. The five stages:
|
||||
|
||||
1. **Stage 1 — load** the new bundle into a `__reload_staging__.<id>` namespace so `sys.modules` for the live bundle is untouched.
|
||||
2. **Stage 2 — validate** the staging load. Any error aborts here; the live bundle is unchanged.
|
||||
3. **Stage 3 — swap** under the registry write-lock. New flows resolve to the new class; in-flight flows keep their pre-swap class reference.
|
||||
4. **Stage 4 — clean up** the staging namespace.
|
||||
5. **Stage 5 — notify**. Today this is a logger emission of the added/removed component lists plus a registered post-swap hook that refreshes the component cache; the typed `bundle_reloaded` events pipeline (`ExtensionEventsService`, `GET /api/v1/extensions/events`, the `use-extension-events` React hook) is deferred to LE-1017. The reload route's HTTP response body already carries the typed result, so the palette's reload action gets immediate feedback through the existing mutation hook ([`use-reload-bundle.ts`](https://github.com/langflow-ai/langflow/blob/main/src/frontend/src/controllers/API/queries/extensions/use-reload-bundle.ts)) until the events stream lands.
|
||||
|
||||
:::note First reload after boot
|
||||
On the **first** reload after a fresh server boot, `components_added` and `components_removed` are always empty: the bundle is already registered at startup, so the post-swap diff is against an identical pre-swap snapshot. This is expected — non-empty deltas only appear once you edit a component (add/remove a class, rename, etc.) and reload again.
|
||||
:::
|
||||
|
||||
Reload is **not** a trust boundary. Bundle code was trusted at install time; the reload pipeline only handles the swap mechanics. Mode B/C deployments rebuild the Docker image rather than reload.
|
||||
|
||||
## Porting an in-tree component
|
||||
|
||||
The mechanical steps for extracting `src/lfx/src/lfx/components/<name>/` into a standalone bundle live in [`src/bundles/PORTING.md`](https://github.com/langflow-ai/langflow/blob/main/src/bundles/PORTING.md). That's the canonical recipe; this page covers the design choices around it. The DuckDuckGo extraction (LE-1023) is the reference implementation — diff `src/bundles/duckduckgo/` against any in-tree component to see the shape.
|
||||
|
||||
A few things that surprise people:
|
||||
|
||||
* **The user-visible `pip install langflow` does not change.** The metapackage adds the new bundle as a regular pip dependency, so a flat `pip install langflow` keeps the same component set as before. Decoupling that (where you'd opt into bundles) is a later milestone.
|
||||
* **You can ship before the in-tree copy is removed.** The extracted bundle gets `@official` precedence; the in-tree copy stays as a fallback during the deprecation window so users on older saved flows are never broken.
|
||||
* **Bare-name uniqueness is the most common gotcha.** Components like `MergeDataComponent`, `SplitTextComponent`, and `SubFlowComponent` already exist in multiple bundles. Run the bare-names check before opening the PR.
|
||||
|
||||
## Publishing checklist
|
||||
|
||||
Before merging the extraction PR or releasing a fresh bundle:
|
||||
|
||||
1. `lfx extension validate <path>` exits zero against your manifest.
|
||||
2. `pytest src/bundles/<name>/tests` passes.
|
||||
3. Migration table entries cover every legacy form (full path, package path, bare name if unique, pre-A slot).
|
||||
4. `scripts/migrate/check_bare_names.py` passes.
|
||||
5. `scripts/migrate/check_migration_append_only.py --base origin/main` passes.
|
||||
6. `scripts/migrate/check_bundle_api_changelog.py --base origin/main` passes if you touched the `BUNDLE_API.md` surface.
|
||||
7. The dogfood checklist (template at [`src/bundles/duckduckgo/M1_DOGFOOD_CHECKLIST.md`](https://github.com/langflow-ai/langflow/blob/main/src/bundles/duckduckgo/M1_DOGFOOD_CHECKLIST.md)) is filled in by an engineer who is **not** on the Extension team. Save a flow on the pre-migration release, upgrade, confirm it loads and runs identically.
|
||||
8. The PR description links to the migration-table entries and the dogfood evidence.
|
||||
|
||||
If any step fails, the extraction is not done. The dogfood gate especially is non-negotiable — the foundation tests prove the migration table rewrites references; only an end-to-end run proves the rewritten flow still does what the user expects.
|
||||
|
||||
## See also
|
||||
@ -16,6 +16,17 @@ When you run `uv pip install langflow`, you install:
|
||||
Some bundles that were previously included in `langflow` are moved into standalone packages.
|
||||
The `langflow` metapackage adds them as regular pip dependencies, so `pip install langflow` continues to include them.
|
||||
|
||||
## Current Extension bundles
|
||||
|
||||
The following Extension bundles are included with `uv pip install langflow`:
|
||||
|
||||
| Package | Bundle | Components |
|
||||
|---------|--------|------------|
|
||||
| `lfx-arxiv` | [arXiv](/bundles-arxiv) | arXiv search |
|
||||
| `lfx-docling` | [Docling](/bundles-docling) | Document parsing and chunking |
|
||||
| `lfx-duckduckgo` | [DuckDuckGo](/bundles-duckduckgo) | Web search |
|
||||
| `lfx-ibm` | [IBM](/bundles-ibm) | IBM watsonx.ai LLM and embeddings, IBM Db2 Vector Store |
|
||||
|
||||
The internal identifier for each component changes.
|
||||
For example, a DuckDuckGo component previously referenced as a `DuckDuckGoSearchComponent` class is now `ext:duckduckgo:DuckDuckGoSearchComponent@official`.
|
||||
Langflow rewrites these references automatically when you open a saved flow, so in most cases the migration is invisible.
|
||||
|
||||
@ -7,6 +7,12 @@ slug: /integrations-langwatch
|
||||
|
||||
## Integrate LangWatch observability
|
||||
|
||||
:::note LangWatch is unavailable in the default Docker images
|
||||
The official Langflow Docker images (`langflowai/langflow` and `langflowai/langflow-nightly`) run on Python 3.14, and the `langwatch` package doesn't yet support Python 3.14 (it requires Python `<3.14`). As a result, LangWatch tracing is **not available in the default Docker images** even when `LANGWATCH_API_KEY` is set. Langflow logs a warning on the first flow run and continues without LangWatch tracing.
|
||||
|
||||
To use LangWatch, run Langflow on Python 3.10–3.13, such as the PyPI distribution (`pip install langflow`) or the Langflow desktop app. If you need a container, build a custom image on a Python 3.10–3.13 base.
|
||||
:::
|
||||
|
||||
To integrate with Langflow, add your LangWatch API key as a Langflow environment variable:
|
||||
|
||||
1. Get a LangWatch API key from your LangWatch account.
|
||||
|
||||
@ -9,47 +9,61 @@ import TabItem from '@theme/TabItem';
|
||||
|
||||
Langflow produces logs for individual flows and the Langflow application itself using the [structlog](https://www.structlog.org) library for logging.
|
||||
|
||||
The default, primary logfile is named `langflow.log`.
|
||||
|
||||
Log files are stored in JSON format with structured metadata.
|
||||
By default, OSS Langflow writes logs to stdout. To write logs to a file, set `LANGFLOW_LOG_FILE` to the desired path.
|
||||
|
||||
## Log storage
|
||||
|
||||
Langflow logs are stored in the config directory specified in the `LANGFLOW_CONFIG_DIR` environment variable.
|
||||
The default config directory location depends on your operating system and installation method:
|
||||
The `LANGFLOW_CONFIG_DIR` environment variable controls where Langflow stores its configuration and data files. The default location depends on your operating system:
|
||||
|
||||
- **Langflow Desktop**:
|
||||
|
||||
- **macOS**: `/Users/<username>/Library/Logs/com.LangflowDesktop`
|
||||
- **Windows**: `C:\Users\<username>\AppData\Local\com.LangflowDesktop\logs`
|
||||
|
||||
- **OSS Langflow**:
|
||||
- **OSS Langflow** (all installation methods, including `uv pip install` and `git clone`):
|
||||
|
||||
- **macOS with `uv pip install`**: `/Users/<username>/Library/Caches/langflow`
|
||||
- **Linux with `uv pip install`**: `/home/<username>/.cache/langflow`
|
||||
- **Windows/WSL with `uv pip install`**: `C:\Users\<username>\AppData\Local\langflow\langflow\Cache`
|
||||
- **macOS/Windows/Linux/WSL with `git clone`**: `<path_to_clone>/src/backend/base/langflow/`
|
||||
- **macOS**: `/Users/<username>/Library/Caches/langflow`
|
||||
- **Linux**: `/home/<username>/.cache/langflow`
|
||||
- **Windows/WSL**: `C:\Users\<username>\AppData\Local\langflow\langflow\Cache`
|
||||
|
||||
To customize log storage locations and behaviors, set the following [Langflow environment variables](/environment-variables) in your Langflow `.env` file, and then start Langflow with `uv run langflow run --env-file .env`:
|
||||
|
||||
| Variable | Format | Default | Description |
|
||||
|----------|--------|---------|-------------|
|
||||
| `LANGFLOW_CONFIG_DIR` | String | Varies | Set the Langflow configuration directory where files and logs are stored. Default path depends on your installation, as described in the preceding list. |
|
||||
| `LANGFLOW_LOG_LEVEL` | String | `ERROR` | Sets the log level as one of `DEBUG`, `ERROR`, `INFO`, `WARNING`, and `CRITICAL`. For example, `LANGFLOW_LOG_LEVEL=DEBUG`. |
|
||||
| `LANGFLOW_LOG_FILE` | String | Not set | Sets the log file storage location if you want to use a non-default location. For example, `LANGFLOW_LOG_FILE=path/to/logfile.log`. If not set, logs are written to stdout. |
|
||||
| `LANGFLOW_LOG_ENV` | String | `default` | This variable is the primary log format controller. `container`: JSON format for Docker/structured logging. `container_csv`: Key-value format for data analysis. `default` or unset: Uses `LANGFLOW_PRETTY_LOGS` to determine format. |
|
||||
| `LANGFLOW_PRETTY_LOGS` | Boolean | `True` | This variable controls log output format when `LANGFLOW_LOG_ENV=default` or unset. When `true`, uses structlog's [ConsoleRenderer](https://www.structlog.org/en/stable/console-output.html). When `false`, outputs logs in JSON format. |
|
||||
| `LANGFLOW_LOG_FORMAT` | String | Not set | Switch between key-value format and console format. Set to `key_value` for key-value format or `console` to use structlog's [ConsoleRenderer](https://www.structlog.org/en/stable/console-output.html). This variable only works when `LANGFLOW_LOG_ENV=default` and `LANGFLOW_PRETTY_LOGS=true`. |
|
||||
| `LANGFLOW_LOG_ROTATION` | String | `1 day` | Controls when the log file is rotated, either based on time or file size. For time-based rotation, set to `1 day`, `12 hours`, or `1 week`. For size-based rotation, set to `10 MB` or `1 GB`. To disable rotation, set to `None`. If disabled, log files grow without limit. |
|
||||
| `LANGFLOW_ENABLE_LOG_RETRIEVAL` | Boolean | `False` | Enables retrieval of logs from your Langflow instance with [Logs endpoints](/api-logs). |
|
||||
| `LANGFLOW_LOG_RETRIEVER_BUFFER_SIZE` | Integer | `10000` | Set the buffer size for log retrieval if `LANGFLOW_ENABLE_LOG_RETRIEVAL=True`. Must be greater than `0` for log retrieval to function. |
|
||||
| `LANGFLOW_NATIVE_TRACING` | Boolean | `true` | Enables the tracer to record execution traces directly in the Langflow database for use in Trace View. Set to `false` to disable tracing. |
|
||||
| `LANGFLOW_ENVIRONMENT` | String | Not set | Adds an `environment` field to every JSON log record when set (`staging`, `production`, etc.). Omitted otherwise. |
|
||||
| `LANGFLOW_LOG_ENV` | String | `default` | Primary log format controller. `container` or `container_json`: JSON format via [`JSONRenderer`](https://www.structlog.org/en/stable/api.html#structlog.processors.JSONRenderer). `container_csv`: Key-value format. `default` or unset: uses `LANGFLOW_PRETTY_LOGS` to determine format. See [JSON log format](#json-log-format). |
|
||||
| `LANGFLOW_LOG_FILE` | String | Not set | Sets the log file storage location. For example, `LANGFLOW_LOG_FILE=path/to/logfile.log`. If not set, logs are written to stdout. |
|
||||
| `LANGFLOW_LOG_FORMAT` | String | Not set | Set to `key_value` for key-value format or `console` for [`ConsoleRenderer`](https://www.structlog.org/en/stable/console-output.html). Only applies when `LANGFLOW_LOG_ENV=default` and `LANGFLOW_PRETTY_LOGS=true`. |
|
||||
| `LANGFLOW_LOG_LEVEL` | String | `ERROR` | Global log level. One of `DEBUG`, `INFO`, `WARNING`, `ERROR`, or `CRITICAL`. |
|
||||
| `LANGFLOW_LOG_LEVELS` | String | Not set | Per-logger level overrides as `name=LEVEL,name=LEVEL`. Overrides `LANGFLOW_LOG_LEVEL` for specific loggers without changing the global default. Malformed entries raise a `UserWarning` at startup. Example: `sqlalchemy.engine=WARNING,httpx=INFO`. |
|
||||
| `LANGFLOW_LOG_REDACT_KEYS` | String | Not set | Comma-separated extra keys to scrub from JSON log records, on top of the defaults (`password`, `api_key`, `token`, `authorization`, `cookie`, etc.). |
|
||||
| `LANGFLOW_LOG_RETRIEVER_BUFFER_SIZE` | Integer | `10000` | Buffer size for log retrieval when `LANGFLOW_ENABLE_LOG_RETRIEVAL=True`. Must be greater than `0`. |
|
||||
| `LANGFLOW_LOG_ROTATION` | String | `1 day` | Controls log file rotation by time (`1 day`, `12 hours`, `1 week`) or size (`10 MB`, `1 GB`). Set to `None` to disable rotation. |
|
||||
| `LANGFLOW_LOG_TRACE_LOCALS` | Boolean | `false` | Include frame locals in structured tracebacks. Off by default because locals can leak secrets; enable only for local debugging. |
|
||||
| `LANGFLOW_NATIVE_TRACING` | Boolean | `true` | Enables the tracer to record execution traces in the Langflow database for use in Trace View. Set to `false` to disable. |
|
||||
| `LANGFLOW_PRETTY_LOGS` | Boolean | `True` | Controls output format when `LANGFLOW_LOG_ENV=default` or unset. `true`: [`ConsoleRenderer`](https://www.structlog.org/en/stable/console-output.html) (human-readable). `false`: JSON format. |
|
||||
| `LANGFLOW_SERVICE_NAME` | String | `langflow` | Value of the `service` field on every JSON log record. Used as a label by log aggregators. See [Grafana and Loki](./observability-grafana-loki.mdx). |
|
||||
| `LANGFLOW_VERSION` | String | Not set | Adds a `version` field to every JSON log record when set. Omitted otherwise. |
|
||||
|
||||
## View logs in real-time
|
||||
|
||||
To monitor Langflow logs as they are generated, you can follow the log file:
|
||||
By default, OSS Langflow writes logs to stdout. To write logs to a file that you can tail, do the following:
|
||||
|
||||
1. Change to your [Langflow config directory](#log-storage):
|
||||
1. Set `LANGFLOW_LOG_FILE` to a file path in your `.env` file:
|
||||
|
||||
```text
|
||||
LANGFLOW_LOG_FILE=/path/to/langflow.log
|
||||
```
|
||||
|
||||
2. Start Langflow with your `.env` file:
|
||||
|
||||
```bash
|
||||
uv run langflow run --env-file .env
|
||||
```
|
||||
|
||||
3. Change to the directory where you set `LANGFLOW_LOG_FILE`:
|
||||
|
||||
<Tabs groupId="cd-command">
|
||||
<TabItem value="macOS" label="macOS" default>
|
||||
@ -68,7 +82,7 @@ To monitor Langflow logs as they are generated, you can follow the log file:
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
2. Tail the main log file:
|
||||
4. Tail the log file:
|
||||
|
||||
<Tabs groupId="tail-command">
|
||||
<TabItem value="macOS" label="macOS" default>
|
||||
@ -89,9 +103,10 @@ To monitor Langflow logs as they are generated, you can follow the log file:
|
||||
|
||||
If you don't see new log entries, check that Langflow is running, and perform some actions to generate logs events. You can also check the terminal where you started Langflow to see if logs are being printed there.
|
||||
|
||||
|
||||
## Flow and component logs
|
||||
|
||||
After you run a flow, you can inspect the logs for the each component and flow run.
|
||||
After you run a flow, you can inspect the logs for each component and flow run.
|
||||
For example, you can inspect `Message` objects ingested and generated by [Input and Output components](/chat-input-and-output).
|
||||
|
||||
### View flow logs
|
||||
@ -179,8 +194,78 @@ You can use the log file to investigate the issue on your own, add context to a
|
||||
|
||||
The log file is only created when Langflow Desktop runs. If you don't see a log file, try starting Langflow Desktop first, then check for the log file.
|
||||
|
||||
## Log format
|
||||
|
||||
By default, structlog uses [`ConsoleRenderer`](https://www.structlog.org/en/stable/api.html#structlog.dev.ConsoleRenderer) to produce human-readable output with the following structure:
|
||||
|
||||
For example:
|
||||
|
||||
```text
|
||||
2026-05-17T18:18:29Z [info ] incoming request logger=langflow.api.run flow_id=flow-718 user_id=user-4823
|
||||
```
|
||||
|
||||
### JSON log format
|
||||
|
||||
Set `LANGFLOW_LOG_ENV=container` to switch structlog's terminal processor to [`JSONRenderer`](https://www.structlog.org/en/stable/api.html#structlog.processors.JSONRenderer), so every line written to stdout is a JSON object containing the event message, level, timestamp, logger name, exception structure, and service metadata.
|
||||
|
||||
A typical record in JSON mode looks like the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "incoming request",
|
||||
"level": "info",
|
||||
"logger": "langflow.api.run",
|
||||
"timestamp": "2026-05-17T18:18:29.100798Z",
|
||||
"service": "langflow",
|
||||
"version": "1.10.0",
|
||||
"environment": "production",
|
||||
"user_id": "user-4823",
|
||||
"flow_id": "flow-718",
|
||||
"authorization": "***",
|
||||
"cookie": "***",
|
||||
"request_body": {
|
||||
"input": "Tell me a joke",
|
||||
"api_key": "***",
|
||||
"session_id": "Session Apr 21, 17:37:04"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When a log record includes an exception, the `exception` field contains a structured traceback instead of a plain string:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "flow run failed",
|
||||
"level": "error",
|
||||
"logger": "langflow.api.run",
|
||||
"timestamp": "2026-05-17T18:18:32.412Z",
|
||||
"service": "langflow",
|
||||
"exception": [
|
||||
{
|
||||
"exc_type": "ConnectionError",
|
||||
"exc_value": "upstream returned 503",
|
||||
"frames": [
|
||||
{"filename": "/app/.../runner.py", "lineno": 142, "name": "run_flow"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Frame locals, which are local variables at each stack frame, are intentionally excluded by default because they can contain API keys, request bodies, and environment values. Set `LANGFLOW_LOG_TRACE_LOCALS=true` to include them for local debugging.
|
||||
|
||||
#### Stdlib loggers
|
||||
|
||||
Third-party libraries that use Python's standard logging module (`uvicorn`, `sqlalchemy`, `httpx`, `langchain`, `asyncio`) are routed through the same JSON stream in container mode. Their original logger name is preserved on the `logger` field, so log queries that work for application loggers also work for library output.
|
||||
|
||||
#### OpenTelemetry trace correlation
|
||||
|
||||
If `opentelemetry-api` is installed and an active span is present, every log record automatically picks up `trace_id` and `span_id`. OpenTelemetry is not a hard dependency; if it is not installed, the processor is a no-op and nothing else changes.
|
||||
|
||||
## See also
|
||||
|
||||
* [Grafana and Loki](./observability-grafana-loki.mdx)
|
||||
* [Logs endpoints](/api-logs)
|
||||
* [Memory management options](/memory)
|
||||
* [Configure an external PostgreSQL database](/configuration-custom-database)
|
||||
* [Configure an external PostgreSQL database](/configuration-custom-database)
|
||||
* [Grafana and Loki](./observability-grafana-loki.mdx)
|
||||
57
docs/docs/Develop/observability-grafana-loki.mdx
Normal file
57
docs/docs/Develop/observability-grafana-loki.mdx
Normal file
@ -0,0 +1,57 @@
|
||||
---
|
||||
title: Grafana and Loki
|
||||
slug: /observability-grafana-loki
|
||||
---
|
||||
|
||||
Langflow can emit structured JSON logs for ingestion by [Grafana Loki](https://grafana.com/oss/loki/).
|
||||
|
||||
This page demonstrates how to connect Langflow to the [reference Grafana stack](https://github.com/langflow-ai/langflow/tree/main/deploy/observability/grafana-loki) shipped with the Langflow repository.
|
||||
For more information, see the [README](https://github.com/langflow-ai/langflow/tree/main/deploy/observability/grafana-loki).
|
||||
|
||||
To consume structured Langflow logs with other ingestion providers, see [Logs](/logging).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Clone the Langflow repository](/contributing-how-to-contribute#install-langflow-from-source) to access the [reference Grafana stack](https://github.com/langflow-ai/langflow/tree/main/deploy/observability/grafana-loki). If you are using your own Grafana and Loki instances, cloning the repository is not required.
|
||||
- [Docker and Docker Compose](https://docs.docker.com/)
|
||||
|
||||
## Configure environment variables
|
||||
|
||||
1. In the root folder of your Langflow application, edit your existing `.env` file or create a new one.
|
||||
2. Add the following environment variables. Replace the placeholders with the values for your deployment:
|
||||
|
||||
```text
|
||||
LANGFLOW_LOG_ENV=container
|
||||
LANGFLOW_LOG_LEVEL=INFO
|
||||
LANGFLOW_LOG_FILE=/var/log/langflow/langflow.log
|
||||
LANGFLOW_SERVICE_NAME=langflow
|
||||
LANGFLOW_VERSION=1.10.0
|
||||
LANGFLOW_ENVIRONMENT=production
|
||||
```
|
||||
|
||||
Setting `LANGFLOW_LOG_ENV=container` switches structlog's terminal processor to [`JSONRenderer`](https://www.structlog.org/en/stable/api.html#structlog.processors.JSONRenderer), so every line written to stdout is a JSON object containing the event message, level, timestamp, logger name, exception structure, and service metadata. For more information, see [Logs](/logging).
|
||||
|
||||
The shipped Promtail configuration scrapes `*.log` files from a directory rather than stdout. `LANGFLOW_LOG_FILE` must point at a file inside the directory that Promtail watches. Set `LANGFLOW_LOG_DIR` to that same directory so Langflow creates the file in the right place.
|
||||
|
||||
3. Start the reference Loki + Promtail + Grafana stack from the repository:
|
||||
|
||||
```bash
|
||||
cd deploy/observability/grafana-loki
|
||||
export LANGFLOW_LOG_DIR=/var/log/langflow
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
4. Launch Langflow with your `.env` file:
|
||||
|
||||
```bash
|
||||
uv run langflow run --env-file .env
|
||||
```
|
||||
|
||||
5. Run a flow in Langflow to generate log traffic.
|
||||
|
||||
6. Open Grafana at `http://localhost:3000` and navigate to **Dashboards** > **Langflow Logs** to view structured log output, including panels for structured tracebacks, PII redaction, stdlib library output, and service/environment/version coverage.
|
||||
|
||||
## See also
|
||||
|
||||
* [Logs](/logging)
|
||||
* [Logs endpoints](/api-logs)
|
||||
@ -1,15 +1,15 @@
|
||||
---
|
||||
title: Build components with Langflow Assistant
|
||||
title: Build flows and components with Langflow Assistant
|
||||
slug: /langflow-assistant
|
||||
---
|
||||
|
||||
import Icon from "@site/src/components/icon";
|
||||
|
||||
**Langflow Assistant** is an in-app virtual assistant pane within the **Playground**.
|
||||
**Langflow Assistant** is an in-app virtual assistant pane accessible from the canvas toolbar.
|
||||
|
||||
It can answer questions about the application and help you get more out of Langflow.
|
||||
|
||||
**Langflow Assistant** understands the structure of the Langflow graph, so it can create components from natural language prompts.
|
||||
**Langflow Assistant** understands the structure of the Langflow graph, so it can build complete flows or create individual components from natural language prompts.
|
||||
|
||||
Behind the scenes, **Langflow Assistant** runs a built-in Langflow flow on your Langflow server each time you send a message.
|
||||
This flow is distinct from the flow that is open in the canvas, and has its own language model.
|
||||
@ -19,6 +19,11 @@ To give **Langflow Assistant** context for a different flow, switch to that flow
|
||||
## Prerequisites
|
||||
|
||||
* Connect an LLM provider in the [Global model providers page](/components-models) for **Langflow Assistant** to use
|
||||
* Custom component creation must be enabled and permitted for your user role:
|
||||
* `LANGFLOW_ALLOW_CUSTOM_COMPONENTS` must be `true` (default).
|
||||
* `LANGFLOW_CUSTOM_COMPONENT_ADMIN_ONLY` must be `false` (default) for non-superusers. For superusers, this option is irrelevant.
|
||||
|
||||
For more information, see [Restrict custom component creation to superusers](/deployment-block-custom-components).
|
||||
|
||||
## Create a custom component with Langflow Assistant
|
||||
|
||||
@ -26,7 +31,7 @@ In this example, you'll prompt **Langflow Assistant** to create a custom compone
|
||||
|
||||
You'll then iterate on the code based on the results in the **Playground**.
|
||||
|
||||
1. In the **Playground**, click the <Icon name="Langflow" aria-hidden="true"/> **Langflow** icon.
|
||||
1. In the canvas toolbar, click the <Icon name="Langflow" aria-hidden="true"/> **Langflow** icon.
|
||||
The **Langflow Assistant** pane opens.
|
||||
2. Optionally, ask `What can you help me with?` for a list of **Langflow Assistant's** capabilities.
|
||||
3. Prompt **Langflow Assistant** to generate a custom component. For example:
|
||||
@ -224,3 +229,55 @@ You'll then iterate on the code based on the results in the **Playground**.
|
||||
| https://langflow.org | Langflow \| Low-code AI builder for agentic and RAG applications | 200 |
|
||||
| https://github.com/langflow-ai/langflow | GitHub - langflow-ai/langflow: Langflow is a powerful tool for building and deploying AI-powered agents and workflows. - GitHub | 200 |
|
||||
| https://python.org | Welcome to Python.org | 200 |
|
||||
|
||||
## Build an agent flow with Langflow Assistant
|
||||
|
||||
:::warning
|
||||
Building a flow reads and writes the entire Langflow graph on every turn, which can use significantly more tokens than single-component generation.
|
||||
:::
|
||||
|
||||
In addition to generating individual components, **Langflow Assistant** can create a flow from a single prompt.
|
||||
|
||||
Continuing from the [Create a custom component with Langflow Assistant](#create-a-custom-component-with-langflow-assistant) example, ask **Langflow Assistant** to build a simple agent flow that uses the **URLTitleExtractor** custom component as a tool.
|
||||
|
||||
1. With the **URLTitleExtractor** component still on the canvas, open **Langflow Assistant** and prompt it to build the agent flow:
|
||||
```
|
||||
Build a simple agent flow using the URLTitleExtractor custom component as a tool.
|
||||
```
|
||||
|
||||
2. Langflow Assistant presents a diagram of the flow it is proposing.
|
||||
To approve, click **Add to Canvas**.
|
||||
|
||||
**Langflow Assistant** adds an **Agent** component, connects the **URLTitleExtractor** to its **Tools** input, and connects **Chat Input** and **Chat Output** to complete the flow.
|
||||
Flow generation is model-driven, so the exact wiring may differ from this example.
|
||||
3. To iterate, describe changes to **Langflow Assistant**, and then click **Replace canvas** to replace the entire flow currently on the canvas. For example:
|
||||
```
|
||||
Connect the URLTitleExtractor component's Toolset port to the Agent's Tools port.
|
||||
```
|
||||
4. Open the **Playground** and ask the agent to check a URL:
|
||||
```
|
||||
What is the title of the page at https://langflow.org?
|
||||
```
|
||||
The agent uses the **URLTitleExtractor** tool to fetch the page and return the title.
|
||||
|
||||
5. **Langflow Assistant** can further iterate on the flow, or answer questions about Langflow.
|
||||
For example, ask Langflow Assistant `How do I send an API request to chat with this flow in Python?`
|
||||
The response includes a request sourced from the Langflow documentation that you can copy and paste into your application.
|
||||
|
||||
## Reference canvas components in your prompts
|
||||
|
||||
Reference components and their fields directly in a **Langflow Assistant** prompt using the `@` and `.` selectors.
|
||||
|
||||
In the **Langflow Assistant** prompt input, enter `@` to list all available components, and then select a component to insert its reference.
|
||||
To select a specific field within a component, enter `.` after the component.
|
||||
|
||||
For example, to prompt **Langflow Assistant** to update the system prompt field on an Agent component, enter the following:
|
||||
```
|
||||
Update @Agent.System Prompt to always respond in bullet points.
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Build flows](/concepts-flows)
|
||||
- [Custom components](/components-custom-components)
|
||||
- [Trigger flows with the Langflow API](/concepts-publish)
|
||||
@ -69,6 +69,12 @@ For all changes, see the [Changelog](https://github.com/langflow-ai/langflow/rel
|
||||
|
||||
### New features and enhancements
|
||||
|
||||
- **Langflow Assistant**: build complete flows
|
||||
|
||||
**Langflow Assistant** can now build entire flows, and not only individual components.
|
||||
|
||||
For more information, see [Build flows and components with Langflow Assistant](/langflow-assistant).
|
||||
|
||||
- Redis-backed job queue for multi-worker deployments
|
||||
|
||||
Langflow now supports a Redis-backed job queue that allows flow build events to be shared across multiple Gunicorn/Uvicorn workers and across multiple replicas behind a load balancer.
|
||||
@ -82,7 +88,7 @@ For all changes, see the [Changelog](https://github.com/langflow-ai/langflow/rel
|
||||
Memory bases are per-flow vector stores that automatically ingest conversation messages.
|
||||
The **Memory Base** component retrieves context from the vector store, offering long-term semantic memory for flows.
|
||||
For more information, see [Manage memory bases](../Develop/memory-bases.mdx).
|
||||
|
||||
|
||||
- Python 3.14 support
|
||||
|
||||
Langflow now supports Python 3.10 through 3.14 on macOS, Linux, and Windows.
|
||||
@ -102,7 +108,7 @@ For all changes, see the [Changelog](https://github.com/langflow-ai/langflow/rel
|
||||
- OpenDsStar
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
- **Agent** component: structured response output
|
||||
|
||||
The **Agent** component now includes a **Structured Response** (`structured_response`) output that returns the agent's reply as structured data according to an **Output Schema** you define.
|
||||
@ -115,12 +121,20 @@ For all changes, see the [Changelog](https://github.com/langflow-ai/langflow/rel
|
||||
Existing flows are not affected.
|
||||
|
||||
For more information, see [Agent instructions and input](/agents#agent-instructions-and-input).
|
||||
|
||||
|
||||
- Extension bundles
|
||||
|
||||
Langflow 1.10 introduces Extension bundles: component providers that are packaged and versioned as standalone pip packages, independent of the core Langflow server.
|
||||
The [arXiv](/bundles-arxiv) and [DuckDuckGo](/bundles-duckduckgo) bundles are the pilot examples.
|
||||
Both bundles are still included in `uv pip install langflow`, so nothing changes for existing users.
|
||||
All bundles are still included in `uv pip install langflow`, so nothing changes for existing users.
|
||||
|
||||
The following Extension bundles ship with Langflow 1.10:
|
||||
|
||||
| Package | Bundle | Components |
|
||||
|---------|--------|------------|
|
||||
| `lfx-arxiv` | [arXiv](/bundles-arxiv) | arXiv search |
|
||||
| `lfx-docling` | [Docling](/bundles-docling) | Document parsing and chunking |
|
||||
| `lfx-duckduckgo` | [DuckDuckGo](/bundles-duckduckgo) | Web search |
|
||||
| `lfx-ibm` | [IBM](/bundles-ibm) | IBM watsonx.ai LLM and embeddings, IBM Db2 Vector Store |
|
||||
|
||||
To view all Extension bundles currently loaded in your environment:
|
||||
|
||||
@ -189,13 +203,49 @@ For all changes, see the [Changelog](https://github.com/langflow-ai/langflow/rel
|
||||
Set `LANGFLOW_CUSTOM_COMPONENT_ADMIN_ONLY=true` to restrict custom component creation and code editing to superusers.
|
||||
|
||||
For more information, see [Restrict custom component creation to superusers](/deployment-block-custom-components#restrict-custom-components-to-superusers).
|
||||
|
||||
|
||||
- macOS support matrix
|
||||
|
||||
A new [macOS support](./macos-support-matrix.mdx) page documents feature availability across Apple Silicon and Intel Macs.
|
||||
|
||||
- IBM Db2 Vector Store component
|
||||
|
||||
The **IBM Db2 Vector Store** component is now available in the [IBM bundle](/bundles-ibm).
|
||||
|
||||
For more information, see [IBM Db2 Vector Store](/bundles-ibm#ibm-db2-vector-store).
|
||||
|
||||
- Internationalization
|
||||
|
||||
The Langflow interface is now available in multiple languages.
|
||||
To change the display language, click your **Profile Picture**, select **Settings**, and then select a language from the **Language** dropdown.
|
||||
|
||||
Available languages include English, French (Français), Spanish (Español), German (Deutsch), Portuguese (Português), Japanese (日本語), and Chinese (中文).
|
||||
|
||||
- Login endpoint rate limiting
|
||||
|
||||
Langflow now applies IP-based rate limiting to the `/login` endpoint to protect against brute-force attacks.
|
||||
|
||||
For more information, see [Login rate limiting](/api-keys-and-authentication#login-rate-limiting).
|
||||
|
||||
- Code Agents bundle (beta)
|
||||
|
||||
The **Code Agents** bundle adds new beta agent components for code generation with the `smolagents` and `OpenDsStar` libraries.
|
||||
|
||||
For more information, see [Code Agents bundle](./bundles-codeagents).
|
||||
|
||||
- File Processing bundle (beta)
|
||||
|
||||
The **File Processing** bundle adds components for ingesting and retrieving file content in agent workflows.
|
||||
|
||||
For more information, see [File Processing bundle](./bundles-files-ingestion).
|
||||
|
||||
### Deprecations
|
||||
|
||||
- **Text Input** and **Text Output** components are legacy
|
||||
|
||||
The **Text Input** and **Text Output** components are now legacy and may be removed in a future release.
|
||||
Replace them with the [**Chat Input** and **Chat Output** components](/chat-input-and-output).
|
||||
|
||||
- Voice mode is removed
|
||||
|
||||
The <Icon name="Mic" aria-hidden="true"/> **Microphone** button in the **Playground** now only enables speech-to-text, with no additional voice mode functionality.
|
||||
|
||||
@ -112,15 +112,19 @@ const config = {
|
||||
sidebarPath: require.resolve("./sidebars.js"), // Use sidebars.js file
|
||||
sidebarCollapsed: true,
|
||||
// Versioning configuration
|
||||
lastVersion: "1.9.0",
|
||||
lastVersion: "1.10.0",
|
||||
versions: {
|
||||
current: {
|
||||
label: "1.10.x (Next)",
|
||||
label: "1.11.x (Next)",
|
||||
path: "next",
|
||||
},
|
||||
"1.10.0": {
|
||||
label: "1.10.x",
|
||||
path: "",
|
||||
},
|
||||
"1.9.0": {
|
||||
label: "1.9.x",
|
||||
path: "",
|
||||
path: "1.9.0",
|
||||
},
|
||||
"1.8.0": {
|
||||
label: "1.8.x",
|
||||
@ -415,7 +419,10 @@ const config = {
|
||||
},
|
||||
{
|
||||
to: "/legacy-core-components",
|
||||
from: "/directory",
|
||||
from: [
|
||||
"/directory",
|
||||
"/text-input-and-output",
|
||||
]
|
||||
},
|
||||
// add more redirects like this
|
||||
// {
|
||||
|
||||
@ -54,6 +54,19 @@
|
||||
> classifier now bypasses one full LLM round-trip on plan approval the
|
||||
> same way it already does for edit continuation.
|
||||
|
||||
> **2026-06-03 revision** — **@-mention of canvas components and fields in the
|
||||
> assistant input.** Typing `@` opens a filterable list of the canvas
|
||||
> components; selecting one inserts a quoted, space-free reference token
|
||||
> `'<componentId>'` that the agent resolves to that component's details/code via
|
||||
> the existing `get_flow_component_details` MCP tool. Typing `.` adjacent to a
|
||||
> confirmed token re-triggers the list in *field mode*, listing that component's
|
||||
> user-facing template fields (sourced client-side from `node.data.node.template`,
|
||||
> no network call); selecting one inserts a single terminal token
|
||||
> `'<componentId>.<fieldName>'` resolved to the field's current value via
|
||||
> `get_flow_component_field_value`. Frontend-only parsing
|
||||
> (`mention-parsing.ts` / `use-component-mentions.ts`); no backend change. See
|
||||
> ADR-031.
|
||||
|
||||
> **Companion docs**: end-to-end architecture (with Mermaid sequence/flow
|
||||
> diagrams) lives at
|
||||
> `src/backend/base/langflow/agentic/ARCHITECTURE.md`. This document covers the
|
||||
@ -162,6 +175,8 @@ This context owns:
|
||||
| **AgentToolCompatibilitySection** | "Agent Tool Compatibility" block in the `LangflowAssistant.json` system prompt teaching the generator (1) action `verb_noun` method naming, (2) class-level `description` as the LLM-facing tool description, (3) `tool_mode=True` discipline + clear `info=`, (4) NEVER use the reserved `component_as_tool`/`to_toolkit` names. The complementary defense to the runtime guardrails | `LangflowAssistant.json` system prompt |
|
||||
| **OpenProviderModalEmptyState** | The "No Models Configured" empty-state button now opens a `ModelProviderModal` dialog inline (`modelType="llm"`) instead of navigating to `/settings/model-providers`. Lets the user configure providers without leaving the assistant panel | `AssistantNoModelsState` in `assistant-no-models-state.tsx`; `data-testid="assistant-no-models-configure-providers"` |
|
||||
| **MaxFlowVerificationAttempts** | Hard cost ceiling (`MAX_FLOW_VERIFICATION_ATTEMPTS = 3`) for the post-build flow-verification loop. Each attempt costs one full execution plus at most one agent fix turn — so the cap doubles as the user-visible "after N attempt(s)" caveat string | `flow_types.MAX_FLOW_VERIFICATION_ATTEMPTS` |
|
||||
| **ComponentMention** | Typing `@` in the assistant input opens a filterable list of the canvas components; arrow/Tab navigate, Enter confirms, Esc cancels, and the highlighted component is selected on the canvas as a live preview. Confirming inserts a quoted, space-free token `'<componentId>'` the agent resolves to that component's details/code | `detectMention`, `formatMentionToken` in `mention-parsing.ts`; `use-component-mentions.ts`; `assistant-mention-popover.tsx` |
|
||||
| **FieldMention** | Chaining `.` directly after a confirmed component token (`'<id>'.`) re-opens the list in *field mode*, showing that component's user-facing template fields by display name; selecting one inserts a single terminal token `'<id>.<fieldName>'` the agent resolves to the field's current value. The field list is sourced client-side from `node.data.node.template` (no network call); underscore-prefixed/internal keys (`_type`, `_frontend_node_*`) and `code`, plus `show: false` and label-less fields, are excluded | `detectFieldMention`, `formatFieldMentionToken`, `toFieldItems` in `use-component-mentions.ts` |
|
||||
|
||||
---
|
||||
|
||||
@ -589,6 +604,29 @@ The frontend implements automatic model selection to ensure a valid model is alw
|
||||
- **And** `get_llm` should NOT fall back to `provider="Unknown"` → `ValueError: missing a provider`
|
||||
- **And** bare model-name strings like `"gpt-4o"` should be left untouched so the catalog path still runs
|
||||
|
||||
### Scenario: Reference a canvas component with an @-mention
|
||||
- **Given** the assistant panel is open and the canvas has components
|
||||
- **When** I type `@` in the input
|
||||
- **Then** a list of the canvas components should open, filterable by display name
|
||||
- **And** the highlighted component should be selected on the canvas as a preview
|
||||
- **When** I press Enter (or click an item)
|
||||
- **Then** a quoted reference token like `'LanguageModelComponent-XSmrK'` should be inserted (no trailing space)
|
||||
- **And** the agent should resolve it to that component's details/code via `get_flow_component_details`
|
||||
|
||||
### Scenario: Reference a single field's value with @component.field
|
||||
- **Given** I have just inserted a component reference token
|
||||
- **When** I type `.` immediately after the token
|
||||
- **Then** the list should re-open in field mode showing that component's user-facing fields by display name
|
||||
- **And** internal fields (underscore-prefixed keys such as `_type` / `_frontend_node_*`, and `code`) should NOT appear
|
||||
- **When** I select a field
|
||||
- **Then** a single terminal token like `'LanguageModelComponent-XSmrK.api_key'` should replace the reference
|
||||
- **And** the agent should resolve it to that field's current value via `get_flow_component_field_value`
|
||||
|
||||
### Scenario: Keyboard navigation scrolls the mention list into view
|
||||
- **Given** the mention list is open and taller than the popover
|
||||
- **When** I move the highlight past the visible area with the arrow keys
|
||||
- **Then** the highlighted option should scroll into view (`scrollIntoView({ block: "nearest" })`)
|
||||
|
||||
---
|
||||
|
||||
## 5. Architecture Decision Records
|
||||
@ -1452,6 +1490,39 @@ the modal is rendered with `modelType="llm"`.
|
||||
|
||||
---
|
||||
|
||||
### ADR-031: @-Mention of Canvas Components and Fields in the Assistant Input
|
||||
|
||||
**Status**: Accepted
|
||||
|
||||
#### Context
|
||||
The assistant always receives a (capped) canvas summary, but users had no way to point it at a *specific* component or the *current value* of a single field without describing it in prose. On large canvases this is ambiguous and burns tokens re-describing what is already on screen.
|
||||
|
||||
#### Decision
|
||||
Add an `@`-mention affordance to the assistant input, parsed entirely on the frontend:
|
||||
1. `@` opens a filterable list of canvas components (`detectMention`). Confirming inserts a quoted, space-free token `'<componentId>'` (`formatMentionToken`).
|
||||
2. Typing `.` adjacent to a confirmed token re-triggers the list in *field mode* (`detectFieldMention`), sourcing the component's user-facing fields from `node.data.node.template` client-side (no network call, via `toFieldItems`). Confirming inserts one terminal token `'<componentId>.<fieldName>'` (`formatFieldMentionToken`).
|
||||
|
||||
The agent resolves these tokens with the **existing** MCP tools `get_flow_component_details` (component) and `get_flow_component_field_value` (field) — no backend change required.
|
||||
|
||||
#### Consequences
|
||||
|
||||
**Benefits:**
|
||||
- Precise, low-token references to a component or a single field value
|
||||
- The field list is instant (client-side template) and exact (keyed off the component id, not a fuzzy name)
|
||||
- Reuses the server-side resolution tools that already existed
|
||||
|
||||
**Trade-offs:**
|
||||
- The component token no longer auto-appends a trailing space — the `.` must sit adjacent to the closing quote to chain into a field mention, so users type their own space to continue prose. The terminal field token keeps a trailing space.
|
||||
- Only fields with a `display_name` and `show !== false` are listed; underscore-prefixed/internal keys (`_type`, `_frontend_node_*`) and `code` are excluded. A valid field that lacks a `display_name` would not appear.
|
||||
|
||||
**Key Files:**
|
||||
- `src/frontend/.../assistantPanel/helpers/mention-parsing.ts` — `detectMention`, `detectFieldMention`, `formatMentionToken`, `formatFieldMentionToken`
|
||||
- `src/frontend/.../assistantPanel/hooks/use-component-mentions.ts` — mode state, `toFieldItems`, confirm + canvas-highlight logic
|
||||
- `src/frontend/.../assistantPanel/components/assistant-mention-popover.tsx` — list rendering (field rows show the name only) and active-item `scrollIntoView`
|
||||
- `src/backend/.../agentic/utils/flow_component.py` — `get_component_details`, `get_component_field_value` (resolution, unchanged)
|
||||
|
||||
---
|
||||
|
||||
## 6. Technical Specification
|
||||
|
||||
### 6.1 Dependencies
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
"info": {
|
||||
"title": "Langflow V2 Workflow API",
|
||||
"description": "Filtered API for Langflow V2 workflow operations (3 endpoints)",
|
||||
"version": "1.9.0"
|
||||
"version": "1.10.0"
|
||||
},
|
||||
"paths": {
|
||||
"/api/v2/workflows": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -81,7 +81,7 @@ module.exports = {
|
||||
{
|
||||
type: "doc",
|
||||
id: "Flows/langflow-assistant",
|
||||
label: "Build components with Langflow Assistant"
|
||||
label: "Build flows and components with Langflow Assistant"
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -154,12 +154,14 @@ module.exports = {
|
||||
label: "Observability",
|
||||
items: [
|
||||
"Develop/logging",
|
||||
"Develop/observability-grafana-loki",
|
||||
"Develop/traces",
|
||||
{
|
||||
type: "category",
|
||||
label: "Monitoring",
|
||||
items: [
|
||||
"Develop/integrations-arize",
|
||||
"Develop/observability-grafana-loki",
|
||||
"Develop/integrations-langfuse",
|
||||
"Develop/integrations-langsmith",
|
||||
"Develop/integrations-langwatch",
|
||||
@ -217,6 +219,11 @@ module.exports = {
|
||||
id: "Deployment/deployment-nginx-ssl",
|
||||
label: "Deploy Langflow with Nginx and SSL"
|
||||
},
|
||||
{
|
||||
type: "doc",
|
||||
id: "Deployment/deployment-wxo",
|
||||
label: "Deploy flows on watsonx Orchestrate"
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Containerized deployments",
|
||||
@ -274,11 +281,6 @@ module.exports = {
|
||||
id: "Deployment/deployment-hugging-face-spaces",
|
||||
label: "Hugging Face Spaces"
|
||||
},
|
||||
{
|
||||
type: "doc",
|
||||
id: "Deployment/deployment-wxo",
|
||||
label: "IBM watsonx Orchestrate"
|
||||
},
|
||||
{
|
||||
type: "doc",
|
||||
id: "Deployment/deployment-railway",
|
||||
@ -294,7 +296,12 @@ module.exports = {
|
||||
{
|
||||
type: "doc",
|
||||
id: "Deployment/deployment-multi-worker",
|
||||
label: "Deploy with multiple workers",
|
||||
label: "Deploy Langflow with multiple workers",
|
||||
},
|
||||
{
|
||||
type: "doc",
|
||||
id: "Deployment/deployment-lfx-compatibility",
|
||||
label: "LFX and Langflow version compatibility",
|
||||
},
|
||||
{
|
||||
type: "doc",
|
||||
@ -328,7 +335,6 @@ module.exports = {
|
||||
label: "Input / Output",
|
||||
items: [
|
||||
"Components/chat-input-and-output",
|
||||
"Components/text-input-and-output",
|
||||
"Components/webhook",
|
||||
]
|
||||
},
|
||||
@ -433,6 +439,7 @@ module.exports = {
|
||||
"Components/bundles-cassandra",
|
||||
"Components/bundles-chroma",
|
||||
"Components/bundles-cleanlab",
|
||||
"Components/bundles-codeagents",
|
||||
"Components/bundles-clickhouse",
|
||||
"Components/bundles-cloudflare",
|
||||
"Components/bundles-cohere",
|
||||
@ -447,6 +454,8 @@ module.exports = {
|
||||
"Components/bundles-elastic",
|
||||
"Components/bundles-exa",
|
||||
"Components/bundles-faiss",
|
||||
"Components/bundles-files-ingestion",
|
||||
"Components/bundles-firecrawl",
|
||||
"Components/bundles-glean",
|
||||
"Components/bundles-google",
|
||||
"Components/bundles-groq",
|
||||
|
||||
25
docs/versioned_docs/version-1.10.0/API-Reference/README.md
Normal file
25
docs/versioned_docs/version-1.10.0/API-Reference/README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# API Examples (Local Test Harness)
|
||||
|
||||
Run all API example suites against a local Langflow server:
|
||||
|
||||
```bash
|
||||
make api_examples_local
|
||||
```
|
||||
|
||||
Run one test suite:
|
||||
|
||||
```bash
|
||||
make api_examples_local suites=python
|
||||
make api_examples_local suites=javascript
|
||||
make api_examples_local suites=curl
|
||||
```
|
||||
|
||||
The following examples are not executed in this harness:
|
||||
|
||||
- `api-build/build-flow-and-stream-events-2.py`
|
||||
- `api-build/build-flow-and-stream-events-3.py`
|
||||
- `api-flows-run/stream-llm-token-responses.py`
|
||||
- `api-openai-responses/example-streaming-request.py`
|
||||
- `api-logs/stream-logs.py`
|
||||
- `api-logs/retrieve-logs-with-optional-parameters.py`
|
||||
- `api-users/reset-password.py`
|
||||
198
docs/versioned_docs/version-1.10.0/API-Reference/api-build.mdx
Normal file
198
docs/versioned_docs/version-1.10.0/API-Reference/api-build.mdx
Normal file
@ -0,0 +1,198 @@
|
||||
---
|
||||
title: Build endpoints
|
||||
slug: /api-build
|
||||
---
|
||||
import CodeSnippet from '@site/src/components/CodeSnippet';
|
||||
import exampleApiBuildBuildFlowAndStreamEvents from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-build/build-flow-and-stream-events.sh';
|
||||
import resultApiBuildResultBuildFlowAndStreamEvents from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-build/result-build-flow-and-stream-events.json';
|
||||
import exampleApiBuildBuildFlowAndStreamEvents2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-build/build-flow-and-stream-events-2.sh';
|
||||
import resultApiBuildResultBuildFlowAndStreamEvents2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-build/result-build-flow-and-stream-events-2.json';
|
||||
import exampleApiBuildBuildFlowAndStreamEvents3 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-build/build-flow-and-stream-events-3.sh';
|
||||
import exampleApiBuildSetStartAndStopPoints from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-build/set-start-and-stop-points.sh';
|
||||
import exampleApiBuildOverrideFlowParameters from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-build/override-flow-parameters.sh';
|
||||
import resultApiBuildResultOverrideFlowParameters from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-build/result-override-flow-parameters.json';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import examplePythonApiBuildBuildFlowAndStreamEvents from '!!raw-loader!@site/docs/API-Reference/python-examples/api-build/build-flow-and-stream-events.py';
|
||||
import exampleJavascriptApiBuildBuildFlowAndStreamEvents from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-build/build-flow-and-stream-events.js';
|
||||
import examplePythonApiBuildBuildFlowAndStreamEvents2 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-build/build-flow-and-stream-events-2.py';
|
||||
import exampleJavascriptApiBuildBuildFlowAndStreamEvents2 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-build/build-flow-and-stream-events-2.js';
|
||||
import examplePythonApiBuildBuildFlowAndStreamEvents3 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-build/build-flow-and-stream-events-3.py';
|
||||
import exampleJavascriptApiBuildBuildFlowAndStreamEvents3 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-build/build-flow-and-stream-events-3.js';
|
||||
import examplePythonApiBuildSetStartAndStopPoints from '!!raw-loader!@site/docs/API-Reference/python-examples/api-build/set-start-and-stop-points.py';
|
||||
import exampleJavascriptApiBuildSetStartAndStopPoints from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-build/set-start-and-stop-points.js';
|
||||
import examplePythonApiBuildOverrideFlowParameters from '!!raw-loader!@site/docs/API-Reference/python-examples/api-build/override-flow-parameters.py';
|
||||
import exampleJavascriptApiBuildOverrideFlowParameters from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-build/override-flow-parameters.js';
|
||||
|
||||
|
||||
|
||||
:::info
|
||||
The `/build` endpoints are used by Langflow's frontend visual editor code.
|
||||
These endpoints are part of the internal Langflow codebase.
|
||||
|
||||
Don't use these endpoints to run flows in applications that use your Langflow flows.
|
||||
To run flows in your apps, see [Flow trigger endpoints](/api-flows-run).
|
||||
:::
|
||||
|
||||
The `/build` endpoints support Langflow's frontend code for building flows in the Langflow visual editor.
|
||||
You can use these endpoints to build vertices and flows, as well as execute flows with streaming event responses.
|
||||
You might need to use or understand these endpoints when contributing to the Langflow codebase.
|
||||
|
||||
## Build flow and stream events
|
||||
|
||||
This endpoint builds and executes a flow, returning a job ID that can be used to stream execution events.
|
||||
|
||||
1. Send a POST request to the `/build/$FLOW_ID/flow` endpoint:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiBuildBuildFlowAndStreamEvents} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiBuildBuildFlowAndStreamEvents} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiBuildBuildFlowAndStreamEvents} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiBuildResultBuildFlowAndStreamEvents} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
2. After receiving a job ID from the build endpoint, use the `/build/$JOB_ID/events` endpoint to stream the execution results:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiBuildBuildFlowAndStreamEvents2} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiBuildBuildFlowAndStreamEvents2} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiBuildBuildFlowAndStreamEvents2} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiBuildResultBuildFlowAndStreamEvents2} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
The `/build/$FLOW_ID/events` endpoint has a `stream` query parameter that defaults to `true`.
|
||||
To disable streaming and get all events at once, set `?stream=false`.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiBuildBuildFlowAndStreamEvents3} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiBuildBuildFlowAndStreamEvents3} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiBuildBuildFlowAndStreamEvents3} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Build headers
|
||||
|
||||
| Header | Info | Example |
|
||||
|--------|------|---------|
|
||||
| Content-Type | Required. Specifies the JSON format. | "application/json" |
|
||||
| accept | Optional. Specifies the response format. | "application/json" |
|
||||
| x-api-key | Optional. Required only if authentication is enabled. | "sk-..." |
|
||||
|
||||
## Build parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| inputs | object | Optional. Input values for flow components. |
|
||||
| data | object | Optional. Flow data to override stored configuration. |
|
||||
| files | array[string] | Optional. List of file paths to use. |
|
||||
| start_component_id | string | Optional. ID of the component where the execution should start. Component `id` values can be found in [Langflow JSON files](/concepts-flows-import#langflow-json-file-contents) |
|
||||
| stop_component_id | string | Optional. ID of the component where the execution should stop. Component `id` values can be found in [Langflow JSON files](/concepts-flows-import#langflow-json-file-contents).|
|
||||
| log_builds | Boolean | Whether to record build logs. Default: Enabled (`true`). |
|
||||
|
||||
### Set start and stop points
|
||||
|
||||
The `/build` endpoint accepts optional values for `start_component_id` and `stop_component_id` to control where the flow run starts and stops.
|
||||
Setting `stop_component_id` for a component triggers the same behavior as clicking **Run component** on that component in the visual editor: The specified component and all dependent components leading up to that component will run.
|
||||
|
||||
The following example stops flow execution at an **OpenAI** component:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiBuildSetStartAndStopPoints} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiBuildSetStartAndStopPoints} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiBuildSetStartAndStopPoints} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Override flow parameters
|
||||
|
||||
The `/build` endpoint also accepts inputs for `data` directly, instead of using the values stored in the Langflow database.
|
||||
This is useful for running flows without having to pass custom values through the visual editor.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiBuildOverrideFlowParameters} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiBuildOverrideFlowParameters} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiBuildOverrideFlowParameters} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiBuildResultOverrideFlowParameters} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## See also
|
||||
|
||||
- [Get Vertex builds](/api-monitor#get-vertex-builds)
|
||||
- [Delete Vertex builds](/api-monitor#delete-vertex-builds)
|
||||
- [Session ID](/session-id)
|
||||
576
docs/versioned_docs/version-1.10.0/API-Reference/api-files.mdx
Normal file
576
docs/versioned_docs/version-1.10.0/API-Reference/api-files.mdx
Normal file
@ -0,0 +1,576 @@
|
||||
---
|
||||
title: Files endpoints
|
||||
slug: /api-files
|
||||
---
|
||||
import CodeSnippet from '@site/src/components/CodeSnippet';
|
||||
import exampleApiFilesUploadFileV1 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/upload-file-v1.sh';
|
||||
import resultApiFilesResultUploadFileV1 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/result-upload-file-v1.json';
|
||||
import exampleApiFilesUploadImageFilesV1 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/upload-image-files-v1.sh';
|
||||
import exampleApiFilesUploadImageFilesV12 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/upload-image-files-v1-2.sh';
|
||||
import exampleApiFilesListFilesV1 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/list-files-v1.sh';
|
||||
import resultApiFilesResultListFilesV1 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/result-list-files-v1.json';
|
||||
import exampleApiFilesDownloadFileV1 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/download-file-v1.sh';
|
||||
import resultApiFilesResultDownloadFileV1 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/result-download-file-v1.txt';
|
||||
import exampleApiFilesDeleteFileV1 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/delete-file-v1.sh';
|
||||
import resultApiFilesResultDeleteFileV1 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/result-delete-file-v1.json';
|
||||
import exampleApiFilesUploadFileV2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/upload-file-v2.sh';
|
||||
import resultApiFilesResultUploadFileV2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/result-upload-file-v2.txt';
|
||||
import exampleApiFilesUploadFileV22 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/upload-file-v2-2.sh';
|
||||
import exampleApiFilesSendFilesToYourFlowsV2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/send-files-to-your-flows-v2.sh';
|
||||
import exampleApiFilesSendFilesToYourFlowsV22 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/send-files-to-your-flows-v2-2.sh';
|
||||
import exampleApiFilesListFilesV2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/list-files-v2.sh';
|
||||
import resultApiFilesResultListFilesV2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/result-list-files-v2.json';
|
||||
import exampleApiFilesDownloadFileV2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/download-file-v2.sh';
|
||||
import resultApiFilesResultDownloadFileV2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/result-download-file-v2.txt';
|
||||
import exampleApiFilesEditFileNameV2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/edit-file-name-v2.sh';
|
||||
import resultApiFilesResultEditFileNameV2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/result-edit-file-name-v2.json';
|
||||
import exampleApiFilesDeleteFileV2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/delete-file-v2.sh';
|
||||
import resultApiFilesResultDeleteFileV2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/result-delete-file-v2.json';
|
||||
import exampleApiFilesDeleteAllFilesV2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/delete-all-files-v2.sh';
|
||||
import resultApiFilesResultDeleteAllFilesV2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-files/result-delete-all-files-v2.json';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import examplePythonApiFilesUploadFileV1 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/upload-file-v1.py';
|
||||
import exampleJavascriptApiFilesUploadFileV1 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/upload-file-v1.js';
|
||||
import examplePythonApiFilesUploadImageFilesV1 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/upload-image-files-v1.py';
|
||||
import exampleJavascriptApiFilesUploadImageFilesV1 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/upload-image-files-v1.js';
|
||||
import examplePythonApiFilesUploadImageFilesV12 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/upload-image-files-v1-2.py';
|
||||
import exampleJavascriptApiFilesUploadImageFilesV12 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/upload-image-files-v1-2.js';
|
||||
import examplePythonApiFilesListFilesV1 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/list-files-v1.py';
|
||||
import exampleJavascriptApiFilesListFilesV1 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/list-files-v1.js';
|
||||
import examplePythonApiFilesDownloadFileV1 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/download-file-v1.py';
|
||||
import exampleJavascriptApiFilesDownloadFileV1 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/download-file-v1.js';
|
||||
import examplePythonApiFilesDeleteFileV1 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/delete-file-v1.py';
|
||||
import exampleJavascriptApiFilesDeleteFileV1 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/delete-file-v1.js';
|
||||
import examplePythonApiFilesUploadFileV2 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/upload-file-v2.py';
|
||||
import exampleJavascriptApiFilesUploadFileV2 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/upload-file-v2.js';
|
||||
import examplePythonApiFilesUploadFileV22 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/upload-file-v2-2.py';
|
||||
import exampleJavascriptApiFilesUploadFileV22 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/upload-file-v2-2.js';
|
||||
import examplePythonApiFilesSendFilesToYourFlowsV2 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/send-files-to-your-flows-v2.py';
|
||||
import exampleJavascriptApiFilesSendFilesToYourFlowsV2 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/send-files-to-your-flows-v2.js';
|
||||
import examplePythonApiFilesSendFilesToYourFlowsV22 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/send-files-to-your-flows-v2-2.py';
|
||||
import exampleJavascriptApiFilesSendFilesToYourFlowsV22 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/send-files-to-your-flows-v2-2.js';
|
||||
import examplePythonApiFilesListFilesV2 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/list-files-v2.py';
|
||||
import exampleJavascriptApiFilesListFilesV2 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/list-files-v2.js';
|
||||
import examplePythonApiFilesDownloadFileV2 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/download-file-v2.py';
|
||||
import exampleJavascriptApiFilesDownloadFileV2 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/download-file-v2.js';
|
||||
import examplePythonApiFilesEditFileNameV2 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/edit-file-name-v2.py';
|
||||
import exampleJavascriptApiFilesEditFileNameV2 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/edit-file-name-v2.js';
|
||||
import examplePythonApiFilesDeleteFileV2 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/delete-file-v2.py';
|
||||
import exampleJavascriptApiFilesDeleteFileV2 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/delete-file-v2.js';
|
||||
import examplePythonApiFilesDeleteAllFilesV2 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-files/delete-all-files-v2.py';
|
||||
import exampleJavascriptApiFilesDeleteAllFilesV2 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-files/delete-all-files-v2.js';
|
||||
|
||||
|
||||
|
||||
Use the `/files` endpoints to move files between your local machine and Langflow.
|
||||
|
||||
All `/files` endpoints (both `/v1/files` and `/v2/files`) require authentication with a Langflow API key.
|
||||
You can only access files that belong to your own user account, even as a superuser.
|
||||
|
||||
## Differences between `/v1/files` and `/v2/files`
|
||||
|
||||
There are two versions of the `/files` endpoints.
|
||||
|
||||
`/v2/files` offers the following improvements over `/v1/files`:
|
||||
|
||||
- `/v2` files are organized by `user_id` instead of `flow_id`.
|
||||
This means files are owned by users, and they aren't attached to specific flows.
|
||||
You can upload a file to Langflow one time, and use it with multiple flows.
|
||||
- `/v2` files are tracked in the Langflow database.
|
||||
- `/v2` supports bulk upload and delete.
|
||||
- `/v2` responses contain more descriptive metadata.
|
||||
|
||||
However, `/v2/files` doesn't support image files.
|
||||
To send image files to your flows through the API, use [Upload image files (v1)](#upload-image-files-v1).
|
||||
|
||||
## Files/V1 endpoints
|
||||
|
||||
Use the `/files` endpoints to move files between your local machine and Langflow.
|
||||
|
||||
### Upload file (v1)
|
||||
|
||||
Upload a file to the `v1/files/upload/$FLOW_ID` endpoint:
|
||||
Replace **FILE_NAME** with the uploaded file name.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesUploadFileV1} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesUploadFileV1} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesUploadFileV1} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Replace `FILE_NAME.txt` with the name and extension of the file you want to upload.
|
||||
Not all file types are supported.
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFilesResultUploadFileV1} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
### Upload image files (v1)
|
||||
|
||||
Send image files to Langflow to use them in flows.
|
||||
|
||||
The default file limit is 1024 MB.
|
||||
To change this limit, set the `LANGFLOW_MAX_FILE_SIZE_UPLOAD` [environment variable](/environment-variables).
|
||||
|
||||
1. Attach the image to a `POST /v1/files/upload/$FLOW_ID` request with `--form` (`-F`) and the file path:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesUploadImageFilesV1} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesUploadImageFilesV1} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesUploadImageFilesV1} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
A successful request returns the `file_path` for the image in the Langflow file management system in the format `FLOW_ID/TIMESTAMP_FILENAME.TYPE`.
|
||||
For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"flowId": "a430cc57-06bb-4c11-be39-d3d4de68d2c4",
|
||||
"file_path": "a430cc57-06bb-4c11-be39-d3d4de68d2c4/2024-11-27_14-47-50_image-file.png"
|
||||
}
|
||||
```
|
||||
|
||||
2. Use the returned `file_path` to send the image file to other components that can accept file input. Where you specify the file path depends on the component type.
|
||||
|
||||
The following example runs the **Basic Prompting** template flow, passing the image file and the query `describe this image` as input for the **Chat Input** component.
|
||||
In this case, the file path is specified in `tweaks`.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesUploadImageFilesV12} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesUploadImageFilesV12} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesUploadImageFilesV12} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::tip
|
||||
For help with tweaks, use the **Input Schema** in a flow's [**API access** pane](/concepts-publish#api-access).
|
||||
Setting tweaks with **Input Schema** also automatically populates the required component IDs.
|
||||
:::
|
||||
|
||||
### List files (v1)
|
||||
|
||||
List all files associated with a specific flow.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesListFilesV1} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesListFilesV1} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesListFilesV1} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFilesResultListFilesV1} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
### Download file (v1)
|
||||
|
||||
Download a specific file from a flow.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesDownloadFileV1} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesDownloadFileV1} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesDownloadFileV1} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFilesResultDownloadFileV1} language="text" />
|
||||
|
||||
</details>
|
||||
|
||||
### Delete file (v1)
|
||||
|
||||
Delete a specific file from a flow.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesDeleteFileV1} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesDeleteFileV1} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesDeleteFileV1} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFilesResultDeleteFileV1} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## Files/V2 endpoints
|
||||
|
||||
Use the `/files` endpoints to move files between your local machine and Langflow.
|
||||
|
||||
The `/v2/files` endpoints can be authenticated by an API key or JWT.
|
||||
To create a Langflow API key and export it as an environment variable, see [Get started with the Langflow API](/api-reference-api-examples).
|
||||
|
||||
### Upload file (v2)
|
||||
|
||||
Upload a file to your user account. The file can be used across multiple flows.
|
||||
|
||||
The file is uploaded in the format `USER_ID/FILE_ID.FILE_EXTENSION`, such as `07e5b864-e367-4f52-b647-a48035ae7e5e/d44dc2e1-9ae9-4cf6-9114-8d34a6126c94.pdf`.
|
||||
|
||||
1. To retrieve your current `user_id`, call the `/whoami` endpoint:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesUploadFileV2} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesUploadFileV2} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesUploadFileV2} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFilesResultUploadFileV2} language="text" />
|
||||
|
||||
</details>
|
||||
|
||||
2. In the POST request to `v2/files`, replace **@FILE_NAME.EXTENSION** with the uploaded file name and its extension.
|
||||
You must include the ampersand (`@`) in the request to instruct curl to upload the contents of the file, not the string `FILE_NAME.EXTENSION`.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesUploadFileV22} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesUploadFileV22} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesUploadFileV22} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The file is uploaded in the format `USER_ID/FILE_ID.FILE_EXTENSION`, and the API returns metadata about the uploaded file:
|
||||
|
||||
```json
|
||||
{
|
||||
"id":"d44dc2e1-9ae9-4cf6-9114-8d34a6126c94",
|
||||
"name":"engine_manual",
|
||||
"path":"07e5b864-e367-4f52-b647-a48035ae7e5e/d44dc2e1-9ae9-4cf6-9114-8d34a6126c94.pdf",
|
||||
"size":851160,
|
||||
"provider":null
|
||||
}
|
||||
```
|
||||
|
||||
### Send files to your flows (v2)
|
||||
|
||||
:::info
|
||||
The `/v2/files` endpoint can't send image files to flows.
|
||||
To send image files to your flows through the API, see [Upload image files (v1)](#upload-image-files-v1).
|
||||
:::
|
||||
|
||||
This endpoint uploads files to your Langflow server's file management system.
|
||||
To use an uploaded file in a flow, send the file path to a flow with a [**Read File** component](/read-file).
|
||||
|
||||
The default file limit is 1024 MB. To configure this value, change the `LANGFLOW_MAX_FILE_SIZE_UPLOAD` [environment variable](/environment-variables).
|
||||
|
||||
1. To send a file to your flow with the API, POST the file to the `/api/v2/files` endpoint.
|
||||
|
||||
Replace **FILE_NAME.EXTENSION** with the name and extension of the file you want to upload.
|
||||
This is the same step described in [Upload file (v2)](#upload-file-v2), but since you need the filename to upload to your flow, it is included here.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesSendFilesToYourFlowsV2} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesSendFilesToYourFlowsV2} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesSendFilesToYourFlowsV2} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The file is uploaded in the format `USER_ID/FILE_ID.FILE_EXTENSION`, and the API returns metadata about the uploaded file:
|
||||
|
||||
```json
|
||||
{
|
||||
"id":"d44dc2e1-9ae9-4cf6-9114-8d34a6126c94",
|
||||
"name":"engine_manual",
|
||||
"path":"07e5b864-e367-4f52-b647-a48035ae7e5e/d44dc2e1-9ae9-4cf6-9114-8d34a6126c94.pdf",
|
||||
"size":851160,
|
||||
"provider": null
|
||||
}
|
||||
```
|
||||
|
||||
2. To use this file in your flow, add a **Read File** component to your flow.
|
||||
This component loads files into flows from your local machine or Langflow file management.
|
||||
|
||||
3. Run the flow, passing the `path` to the `Read-File` component in the `tweaks` object:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesSendFilesToYourFlowsV22} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesSendFilesToYourFlowsV22} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesSendFilesToYourFlowsV22} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
To get the `Read-File` component's ID, call the [Read flow](/api-flows#read-flow) endpoint or inspect the component in the visual editor.
|
||||
|
||||
If the file path is valid, the flow runs successfully.
|
||||
|
||||
### List files (v2)
|
||||
|
||||
List all files associated with your user account.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesListFilesV2} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesListFilesV2} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesListFilesV2} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFilesResultListFilesV2} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
### Download file (v2)
|
||||
|
||||
Download a specific file by its ID and file extension.
|
||||
|
||||
You must specify the file type you expect in the `--output` value.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesDownloadFileV2} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesDownloadFileV2} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesDownloadFileV2} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFilesResultDownloadFileV2} language="text" />
|
||||
|
||||
</details>
|
||||
|
||||
### Edit file name (v2)
|
||||
|
||||
Change a file name.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesEditFileNameV2} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesEditFileNameV2} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesEditFileNameV2} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFilesResultEditFileNameV2} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
### Delete file (v2)
|
||||
|
||||
Delete a specific file by its ID.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesDeleteFileV2} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesDeleteFileV2} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesDeleteFileV2} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFilesResultDeleteFileV2} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
### Delete all files (v2)
|
||||
|
||||
Delete all files associated with your user account.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFilesDeleteAllFilesV2} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFilesDeleteAllFilesV2} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFilesDeleteAllFilesV2} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFilesResultDeleteAllFilesV2} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## Create upload file (Deprecated)
|
||||
|
||||
This endpoint is deprecated. Use the `/files` endpoints instead.
|
||||
|
||||
## See also
|
||||
|
||||
* [Manage files](/concepts-file-management)
|
||||
@ -0,0 +1,274 @@
|
||||
---
|
||||
title: Flow trigger endpoints
|
||||
slug: /api-flows-run
|
||||
---
|
||||
import CodeSnippet from '@site/src/components/CodeSnippet';
|
||||
import exampleApiFlowsRunRunFlow from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows-run/run-flow.sh';
|
||||
import exampleApiFlowsRunStreamLlmTokenResponses from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows-run/stream-llm-token-responses.sh';
|
||||
import exampleApiFlowsRunRequestExampleWithAllHeadersAndParameters from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows-run/request-example-with-all-headers-and-parameters.sh';
|
||||
import exampleApiFlowsRunPassGlobalVariablesInRequestHeaders from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows-run/pass-global-variables-in-request-headers.sh';
|
||||
import exampleApiFlowsRunWebhookRunFlow from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows-run/webhook-run-flow.sh';
|
||||
import resultApiFlowsRunResultWebhookRunFlow from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows-run/result-webhook-run-flow.json';
|
||||
import examplePythonApiFlowsRunRunFlow from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows-run/run-flow.py';
|
||||
import exampleJavascriptApiFlowsRunRunFlow from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows-run/run-flow.js';
|
||||
import examplePythonApiFlowsRunStreamLlmTokenResponses from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows-run/stream-llm-token-responses.py';
|
||||
import exampleJavascriptApiFlowsRunStreamLlmTokenResponses from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows-run/stream-llm-token-responses.js';
|
||||
import examplePythonApiFlowsRunRequestExampleWithAllHeadersAndParameters from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows-run/request-example-with-all-headers-and-parameters.py';
|
||||
import exampleJavascriptApiFlowsRunRequestExampleWithAllHeadersAndParameters from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows-run/request-example-with-all-headers-and-parameters.js';
|
||||
import examplePythonApiFlowsRunPassGlobalVariablesInRequestHeaders from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows-run/pass-global-variables-in-request-headers.py';
|
||||
import exampleJavascriptApiFlowsRunPassGlobalVariablesInRequestHeaders from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows-run/pass-global-variables-in-request-headers.js';
|
||||
import examplePythonApiFlowsRunWebhookRunFlow from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows-run/webhook-run-flow.py';
|
||||
import exampleJavascriptApiFlowsRunWebhookRunFlow from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows-run/webhook-run-flow.js';
|
||||
|
||||
|
||||
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
Use the `/run` and `/webhook` endpoints to run flows.
|
||||
|
||||
To create, read, update, and delete flows, see [Flow management endpoints](/api-flows).
|
||||
|
||||
## Run flow
|
||||
|
||||
:::tip
|
||||
Langflow automatically generates Python, JavaScript, and curl code snippets for the `/v1/run/$FLOW_ID` endpoint for all flows.
|
||||
For more information, see [Generate API code snippets](/concepts-publish#generate-api-code-snippets).
|
||||
:::
|
||||
|
||||
Execute a specified flow by ID or name.
|
||||
Flow IDs can be found on the code snippets on the [**API access** pane](/concepts-publish#api-access) or in a flow's URL.
|
||||
|
||||
The following example runs the **Basic Prompting** template flow with flow parameters passed in the request body.
|
||||
This flow requires a chat input string (`input_value`), and uses default values for all other parameters.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsRunRunFlow} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsRunRunFlow} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsRunRunFlow} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The response from `/v1/run/$FLOW_ID` includes metadata, inputs, and outputs for the run.
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
The following example illustrates a response from a Basic Prompting flow:
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "chat-123",
|
||||
"outputs": [{
|
||||
"inputs": {
|
||||
"input_value": "Tell me about something interesting!"
|
||||
},
|
||||
"outputs": [{
|
||||
"results": {
|
||||
"message": {
|
||||
"text": "Sure! Have you ever heard of the phenomenon known as \"bioluminescence\"? It's a fascinating natural occurrence where living organisms produce and emit light. This ability is found in various species, including certain types of jellyfish, fireflies, and deep-sea creatures like anglerfish.\n\nBioluminescence occurs through a chemical reaction in which a light-emitting molecule called luciferin reacts with oxygen, catalyzed by an enzyme called luciferase. The result is a beautiful glow that can serve various purposes, such as attracting mates, deterring predators, or luring prey.\n\nOne of the most stunning displays of bioluminescence can be seen in the ocean, where certain plankton emit light when disturbed, creating a mesmerizing blue glow in the water. This phenomenon is often referred to as \"sea sparkle\" and can be seen in coastal areas around the world.\n\nBioluminescence not only captivates our imagination but also has practical applications in science and medicine, including the development of biosensors and imaging techniques. It's a remarkable example of nature's creativity and complexity!",
|
||||
"sender": "Machine",
|
||||
"sender_name": "AI",
|
||||
"session_id": "chat-123",
|
||||
"timestamp": "2025-03-03T17:17:37+00:00",
|
||||
"flow_id": "d2bbd92b-187e-4c84-b2d4-5df365704201",
|
||||
"properties": {
|
||||
"source": {
|
||||
"id": "OpenAIModel-d1wOZ",
|
||||
"display_name": "OpenAI",
|
||||
"source": "gpt-4o-mini"
|
||||
},
|
||||
"icon": "OpenAI"
|
||||
},
|
||||
"component_id": "ChatOutput-ylMzN"
|
||||
}
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
If you are parsing the response in an application, you most likely need to extract the relevant content from the response, rather than pass the entire response back to the user.
|
||||
For an example of a script that extracts data from a Langflow API response, see the [Quickstart](/get-started-quickstart).
|
||||
|
||||
### Stream LLM token responses
|
||||
|
||||
With `/v1/run/$FLOW_ID`, the flow is executed as a batch with optional LLM token response streaming.
|
||||
|
||||
To stream LLM token responses, append the `?stream=true` query parameter to the request:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsRunStreamLlmTokenResponses} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsRunStreamLlmTokenResponses} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsRunStreamLlmTokenResponses} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
LLM chat responses are streamed back as `token` events, culminating in a final `end` event that closes the connection.
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
The following example is truncated to illustrate a series of `token` events as well as the final `end` event that closes the LLM's token streaming response:
|
||||
|
||||
```text
|
||||
{"event": "add_message", "data": {"timestamp": "2025-03-03T17:20:18", "sender": "User", "sender_name": "User", "session_id": "chat-123", "text": "Tell me about something interesting!", "files": [], "error": false, "edit": false, "properties": {"text_color": "", "background_color": "", "edited": false, "source": {"id": null, "display_name": null, "source": null}, "icon": "", "allow_markdown": false, "positive_feedback": null, "state": "complete", "targets": []}, "category": "message", "content_blocks": [], "id": "0103a21b-ebf7-4c02-9d72-017fb297f812", "flow_id": "d2bbd92b-187e-4c84-b2d4-5df365704201"}}
|
||||
|
||||
{"event": "add_message", "data": {"timestamp": "2025-03-03T17:20:18", "sender": "Machine", "sender_name": "AI", "session_id": "chat-123", "text": "", "files": [], "error": false, "edit": false, "properties": {"text_color": "", "background_color": "", "edited": false, "source": {"id": "OpenAIModel-d1wOZ", "display_name": "OpenAI", "source": "gpt-4o-mini"}, "icon": "OpenAI", "allow_markdown": false, "positive_feedback": null, "state": "complete", "targets": []}, "category": "message", "content_blocks": [], "id": "27b66789-e673-4c65-9e81-021752925161", "flow_id": "d2bbd92b-187e-4c84-b2d4-5df365704201"}}
|
||||
|
||||
{"event": "token", "data": {"chunk": " Have", "id": "27b66789-e673-4c65-9e81-021752925161", "timestamp": "2025-03-03 17:20:18 UTC"}}
|
||||
|
||||
{"event": "token", "data": {"chunk": " you", "id": "27b66789-e673-4c65-9e81-021752925161", "timestamp": "2025-03-03 17:20:18 UTC"}}
|
||||
|
||||
{"event": "token", "data": {"chunk": " ever", "id": "27b66789-e673-4c65-9e81-021752925161", "timestamp": "2025-03-03 17:20:18 UTC"}}
|
||||
|
||||
{"event": "token", "data": {"chunk": " heard", "id": "27b66789-e673-4c65-9e81-021752925161", "timestamp": "2025-03-03 17:20:18 UTC"}}
|
||||
|
||||
{"event": "token", "data": {"chunk": " of", "id": "27b66789-e673-4c65-9e81-021752925161", "timestamp": "2025-03-03 17:20:18 UTC"}}
|
||||
|
||||
{"event": "token", "data": {"chunk": " the", "id": "27b66789-e673-4c65-9e81-021752925161", "timestamp": "2025-03-03 17:20:18 UTC"}}
|
||||
|
||||
{"event": "token", "data": {"chunk": " phenomenon", "id": "27b66789-e673-4c65-9e81-021752925161", "timestamp": "2025-03-03 17:20:18 UTC"}}
|
||||
|
||||
{"event": "end", "data": {"result": {"session_id": "chat-123", "message": "Sure! Have you ever heard of the phenomenon known as \"bioluminescence\"?..."}}}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Run endpoint headers
|
||||
|
||||
| Header | Info | Example |
|
||||
|--------|------|---------|
|
||||
| Content-Type | Required. Specifies the JSON format. | "application/json" |
|
||||
| accept | Optional. Specifies the response format. Defaults to JSON if not specified. | "application/json" |
|
||||
| x-api-key | Required. Your Langflow API key for authentication. Can be passed as a header or query parameter. | "sk-..." |
|
||||
| `X-LANGFLOW-GLOBAL-VAR-*` | Optional. Pass global variables to the flow. Variable names are automatically converted to uppercase. These variables take precedence over OS environment variables and are only available during this specific request execution. | `"X-LANGFLOW-GLOBAL-VAR-API_KEY: sk-..."` |
|
||||
|
||||
### Run endpoint parameters
|
||||
|
||||
| Parameter | Type | Info |
|
||||
|-----------|------|------|
|
||||
| flow_id | UUID/string | Required. Part of URL: `/run/$FLOW_ID` |
|
||||
| stream | Boolean | Optional. Query parameter: `/run/$FLOW_ID?stream=true` |
|
||||
| input_value | string | Optional. JSON body field. Main input text/prompt. Default: `null` |
|
||||
| input_type | string | Optional. JSON body field. Input type ("chat" or "text"). Default: `"chat"` |
|
||||
| output_type | string | Optional. JSON body field. Output type ("chat", "any", "debug"). Default: `"chat"` |
|
||||
| output_component | string | Optional. JSON body field. Target component for output. Default: `""` |
|
||||
| tweaks | object | Optional. JSON body field. Component adjustments. Default: `null` |
|
||||
| session_id | string | Optional. JSON body field. Conversation context ID. See [Session ID](/session-id). Default: `null` |
|
||||
|
||||
### Request example with all headers and parameters
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsRunRequestExampleWithAllHeadersAndParameters} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsRunRequestExampleWithAllHeadersAndParameters} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsRunRequestExampleWithAllHeadersAndParameters} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Pass global variables in request headers {#pass-global-variables-in-headers}
|
||||
|
||||
You can pass global variables to your flow using HTTP headers with the format `X-LANGFLOW-GLOBAL-VAR-{VARIABLE_NAME}`.
|
||||
|
||||
Variables passed in headers take precedence over OS environment variables. If a variable is provided in both a header and an environment variable, the header value is used. Variables are only available during this specific request execution and aren't persisted.
|
||||
|
||||
Variable names are automatically converted to uppercase. For example, `X-LANGFLOW-GLOBAL-VAR-api-key` becomes `API_KEY` in your flow.
|
||||
|
||||
You don't need to create these variables in Langflow's Global Variables section first. Pass any variable name using this header format.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsRunPassGlobalVariablesInRequestHeaders} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsRunPassGlobalVariablesInRequestHeaders} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsRunPassGlobalVariablesInRequestHeaders} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
If your flow components reference variables that aren't provided in headers or your Langflow database, the flow fails by default. To avoid this, you can set `LANGFLOW_FALLBACK_TO_ENV_VAR=True` in your `.env` file, which allows the flow to use values from OS environment variables if they aren't otherwise specified.
|
||||
|
||||
|
||||
## Webhook run flow
|
||||
|
||||
Use the `/webhook` endpoint to start a flow by sending an HTTP `POST` request.
|
||||
|
||||
:::tip
|
||||
After you add a [**Webhook** component](/webhook) to a flow, open the [**API access** pane](/concepts-publish), and then click the **Webhook curl** tab to get an automatically generated `POST /webhook` request for your flow.
|
||||
For more information, see [Trigger flows with webhooks](/webhook).
|
||||
:::
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsRunWebhookRunFlow} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsRunWebhookRunFlow} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsRunWebhookRunFlow} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFlowsRunResultWebhookRunFlow} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## Deprecated flow trigger endpoints
|
||||
|
||||
The following endpoints are deprecated and replaced by the `/run` endpoint:
|
||||
|
||||
* `/process`
|
||||
* `/predict`
|
||||
330
docs/versioned_docs/version-1.10.0/API-Reference/api-flows.mdx
Normal file
330
docs/versioned_docs/version-1.10.0/API-Reference/api-flows.mdx
Normal file
@ -0,0 +1,330 @@
|
||||
---
|
||||
title: Flow management endpoints
|
||||
slug: /api-flows
|
||||
---
|
||||
import CodeSnippet from '@site/src/components/CodeSnippet';
|
||||
import exampleApiFlowsCreateFlow from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/create-flow.sh';
|
||||
import resultApiFlowsResultCreateFlow from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/result-create-flow.json';
|
||||
import exampleApiFlowsCreateFlows from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/create-flows.sh';
|
||||
import exampleApiFlowsReadFlow from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/read-flow.sh';
|
||||
import resultApiFlowsResultReadFlow from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/result-read-flow.json';
|
||||
import exampleApiFlowsReadFlows from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/read-flows.sh';
|
||||
import exampleApiFlowsReadFlows2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/read-flows-2.sh';
|
||||
import exampleApiFlowsReadSampleFlows from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/read-sample-flows.sh';
|
||||
import exampleApiFlowsUpdateFlow from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/update-flow.sh';
|
||||
import resultApiFlowsResultUpdateFlow from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/result-update-flow.json';
|
||||
import exampleApiFlowsDeleteFlow from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/delete-flow.sh';
|
||||
import resultApiFlowsResultDeleteFlow from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/result-delete-flow.json';
|
||||
import exampleApiFlowsExportFlows from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/export-flows.sh';
|
||||
import resultApiFlowsResultExportFlows from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/result-export-flows.txt';
|
||||
import exampleApiFlowsImportFlows from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-flows/import-flows.sh';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import examplePythonApiFlowsCreateFlow from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows/create-flow.py';
|
||||
import exampleJavascriptApiFlowsCreateFlow from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows/create-flow.js';
|
||||
import examplePythonApiFlowsCreateFlows from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows/create-flows.py';
|
||||
import exampleJavascriptApiFlowsCreateFlows from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows/create-flows.js';
|
||||
import examplePythonApiFlowsReadFlow from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows/read-flow.py';
|
||||
import exampleJavascriptApiFlowsReadFlow from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows/read-flow.js';
|
||||
import examplePythonApiFlowsReadFlows from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows/read-flows.py';
|
||||
import exampleJavascriptApiFlowsReadFlows from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows/read-flows.js';
|
||||
import examplePythonApiFlowsReadFlows2 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows/read-flows-2.py';
|
||||
import exampleJavascriptApiFlowsReadFlows2 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows/read-flows-2.js';
|
||||
import examplePythonApiFlowsReadSampleFlows from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows/read-sample-flows.py';
|
||||
import exampleJavascriptApiFlowsReadSampleFlows from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows/read-sample-flows.js';
|
||||
import examplePythonApiFlowsUpdateFlow from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows/update-flow.py';
|
||||
import exampleJavascriptApiFlowsUpdateFlow from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows/update-flow.js';
|
||||
import examplePythonApiFlowsDeleteFlow from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows/delete-flow.py';
|
||||
import exampleJavascriptApiFlowsDeleteFlow from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows/delete-flow.js';
|
||||
import examplePythonApiFlowsExportFlows from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows/export-flows.py';
|
||||
import exampleJavascriptApiFlowsExportFlows from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows/export-flows.js';
|
||||
import examplePythonApiFlowsImportFlows from '!!raw-loader!@site/docs/API-Reference/python-examples/api-flows/import-flows.py';
|
||||
import exampleJavascriptApiFlowsImportFlows from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-flows/import-flows.js';
|
||||
|
||||
|
||||
|
||||
Use the `/flows` endpoint to create, read, update, and delete flows.
|
||||
|
||||
If you want to use the Langflow API to run a flow, see [Flow trigger endpoints](/api-flows-run).
|
||||
|
||||
## Create flow
|
||||
|
||||
Creates a new flow.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsCreateFlow} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsCreateFlow} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsCreateFlow} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFlowsResultCreateFlow} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## Create flows
|
||||
|
||||
Creates multiple new flows, returning an array of flow objects.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsCreateFlows} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsCreateFlows} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsCreateFlows} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Read flow
|
||||
|
||||
Retrieves a specific flow by its ID.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsReadFlow} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsReadFlow} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsReadFlow} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFlowsResultReadFlow} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## Read flows
|
||||
|
||||
Returns a JSON object containing a list of flows.
|
||||
|
||||
Retrieve all flows with pagination:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsReadFlows} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsReadFlows} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsReadFlows} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
To retrieve flows from a specific project, use the `project_id` query parameter:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsReadFlows2} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsReadFlows2} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsReadFlows2} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Read sample flows
|
||||
|
||||
Retrieves a list of sample flows:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsReadSampleFlows} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsReadSampleFlows} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsReadSampleFlows} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Update flow
|
||||
|
||||
Updates an existing flow by its ID.
|
||||
|
||||
This example changes the value for `endpoint_name` from a random UUID to `my_new_endpoint_name`.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsUpdateFlow} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsUpdateFlow} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsUpdateFlow} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFlowsResultUpdateFlow} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## Delete flow
|
||||
|
||||
Deletes a specific flow by its ID.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsDeleteFlow} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsDeleteFlow} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsDeleteFlow} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFlowsResultDeleteFlow} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## Export flows
|
||||
|
||||
Exports specified flows to a ZIP file.
|
||||
|
||||
This endpoint downloads a ZIP file containing [Langflow JSON files](/concepts-flows-import#langflow-json-file-contents) for each flow ID listed in the request body.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsExportFlows} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsExportFlows} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsExportFlows} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiFlowsResultExportFlows} language="text" />
|
||||
|
||||
</details>
|
||||
|
||||
## Import flows
|
||||
|
||||
Imports flows by uploading a [Langflow-compatible JSON file](/concepts-flows-import#langflow-json-file-contents).
|
||||
|
||||
To specify a target project for the flow, include the query parameter `folder_id`.
|
||||
The target `folder_id` must already exist before uploading a flow. Call the [/api/v1/projects/](/api-projects#read-projects) endpoint for a list of available folders and projects.
|
||||
|
||||
This example uploads a local file named `agent-with-astra-db-tool.json` to a folder specified by a `FOLDER_ID` variable:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiFlowsImportFlows} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiFlowsImportFlows} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiFlowsImportFlows} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "agent-with-astra-db-tool",
|
||||
"description": "",
|
||||
"icon": null,
|
||||
"icon_bg_color": null,
|
||||
"gradient": null,
|
||||
"data": {}
|
||||
...
|
||||
}
|
||||
]
|
||||
```
|
||||
</details>
|
||||
@ -0,0 +1,96 @@
|
||||
---
|
||||
title: Logs endpoints
|
||||
slug: /api-logs
|
||||
---
|
||||
import CodeSnippet from '@site/src/components/CodeSnippet';
|
||||
import exampleApiLogsStreamLogs from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-logs/stream-logs.sh';
|
||||
import resultApiLogsResultStreamLogs from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-logs/result-stream-logs.txt';
|
||||
import exampleApiLogsRetrieveLogsWithOptionalParameters from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-logs/retrieve-logs-with-optional-parameters.sh';
|
||||
import resultApiLogsResultRetrieveLogsWithOptionalParameters from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-logs/result-retrieve-logs-with-optional-parameters.txt';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import examplePythonApiLogsStreamLogs from '!!raw-loader!@site/docs/API-Reference/python-examples/api-logs/stream-logs.py';
|
||||
import exampleJavascriptApiLogsStreamLogs from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-logs/stream-logs.js';
|
||||
import examplePythonApiLogsRetrieveLogsWithOptionalParameters from '!!raw-loader!@site/docs/API-Reference/python-examples/api-logs/retrieve-logs-with-optional-parameters.py';
|
||||
import exampleJavascriptApiLogsRetrieveLogsWithOptionalParameters from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-logs/retrieve-logs-with-optional-parameters.js';
|
||||
|
||||
|
||||
|
||||
Retrieve logs for your Langflow flows and server.
|
||||
|
||||
## Enable log retrieval
|
||||
|
||||
The `/logs` endpoint requires log retrieval to be enabled in your Langflow instance.
|
||||
|
||||
To enable log retrieval, set the following [environment variables](/environment-variables) in your Langflow `.env` file, and then start Langflow with `uv run langflow run --env-file .env`:
|
||||
|
||||
```text
|
||||
LANGFLOW_ENABLE_LOG_RETRIEVAL=True
|
||||
LANGFLOW_LOG_RETRIEVER_BUFFER_SIZE=10000 // Must be greater than 0
|
||||
LANGFLOW_LOG_LEVEL=DEBUG // Can be DEBUG, ERROR, INFO, WARNING, or CRITICAL
|
||||
```
|
||||
|
||||
## Stream logs
|
||||
|
||||
Stream logs in real-time using Server Sent Events (SSE):
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiLogsStreamLogs} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiLogsStreamLogs} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiLogsStreamLogs} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiLogsResultStreamLogs} language="text" />
|
||||
|
||||
</details>
|
||||
|
||||
## Retrieve logs with optional parameters
|
||||
|
||||
Retrieve logs with optional query parameters:
|
||||
|
||||
- `lines_before`: The number of logs before the timestamp or the last log.
|
||||
- `lines_after`: The number of logs after the timestamp.
|
||||
- `timestamp`: The timestamp to start getting logs from.
|
||||
|
||||
The default values for all three parameters is `0`.
|
||||
With default values, the endpoint returns the last 10 lines of logs.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiLogsRetrieveLogsWithOptionalParameters} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiLogsRetrieveLogsWithOptionalParameters} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiLogsRetrieveLogsWithOptionalParameters} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiLogsResultRetrieveLogsWithOptionalParameters} language="text" />
|
||||
|
||||
</details>
|
||||
429
docs/versioned_docs/version-1.10.0/API-Reference/api-monitor.mdx
Normal file
429
docs/versioned_docs/version-1.10.0/API-Reference/api-monitor.mdx
Normal file
@ -0,0 +1,429 @@
|
||||
---
|
||||
title: Monitor endpoints
|
||||
slug: /api-monitor
|
||||
---
|
||||
import CodeSnippet from '@site/src/components/CodeSnippet';
|
||||
import exampleApiMonitorGetVertexBuilds from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/get-vertex-builds.sh';
|
||||
import resultApiMonitorResultGetVertexBuilds from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/result-get-vertex-builds.json';
|
||||
import exampleApiMonitorDeleteVertexBuilds from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/delete-vertex-builds.sh';
|
||||
import resultApiMonitorResultDeleteVertexBuilds from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/result-delete-vertex-builds.txt';
|
||||
import exampleApiMonitorGetMessages from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/get-messages.sh';
|
||||
import exampleApiMonitorGetMessages2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/get-messages-2.sh';
|
||||
import resultApiMonitorResultGetMessages2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/result-get-messages-2.json';
|
||||
import exampleApiMonitorDeleteMessages from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/delete-messages.sh';
|
||||
import resultApiMonitorResultDeleteMessages from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/result-delete-messages.txt';
|
||||
import exampleApiMonitorUpdateMessage from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/update-message.sh';
|
||||
import resultApiMonitorResultUpdateMessage from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/result-update-message.json';
|
||||
import exampleApiMonitorUpdateSessionId from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/update-session-id.sh';
|
||||
import resultApiMonitorResultUpdateSessionId from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/result-update-session-id.json';
|
||||
import exampleApiMonitorDeleteMessagesBySession from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/delete-messages-by-session.sh';
|
||||
import resultApiMonitorResultDeleteMessagesBySession from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/result-delete-messages-by-session.txt';
|
||||
import exampleApiMonitorExampleRequest from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/example-request.sh';
|
||||
import exampleApiMonitorGetTransactions from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/get-transactions.sh';
|
||||
import resultApiMonitorResultGetTransactions from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-monitor/result-get-transactions.json';
|
||||
import examplePythonApiMonitorExampleRequest from '!!raw-loader!@site/docs/API-Reference/python-examples/api-monitor/example-request.py';
|
||||
import examplePythonApiMonitorGetVertexBuilds from '!!raw-loader!@site/docs/API-Reference/python-examples/api-monitor/get-vertex-builds.py';
|
||||
import exampleJavascriptApiMonitorGetVertexBuilds from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-monitor/get-vertex-builds.js';
|
||||
import examplePythonApiMonitorDeleteVertexBuilds from '!!raw-loader!@site/docs/API-Reference/python-examples/api-monitor/delete-vertex-builds.py';
|
||||
import exampleJavascriptApiMonitorDeleteVertexBuilds from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-monitor/delete-vertex-builds.js';
|
||||
import examplePythonApiMonitorGetMessages from '!!raw-loader!@site/docs/API-Reference/python-examples/api-monitor/get-messages.py';
|
||||
import exampleJavascriptApiMonitorGetMessages from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-monitor/get-messages.js';
|
||||
import examplePythonApiMonitorGetMessages2 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-monitor/get-messages-2.py';
|
||||
import exampleJavascriptApiMonitorGetMessages2 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-monitor/get-messages-2.js';
|
||||
import examplePythonApiMonitorDeleteMessages from '!!raw-loader!@site/docs/API-Reference/python-examples/api-monitor/delete-messages.py';
|
||||
import exampleJavascriptApiMonitorDeleteMessages from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-monitor/delete-messages.js';
|
||||
import examplePythonApiMonitorUpdateMessage from '!!raw-loader!@site/docs/API-Reference/python-examples/api-monitor/update-message.py';
|
||||
import exampleJavascriptApiMonitorUpdateMessage from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-monitor/update-message.js';
|
||||
import examplePythonApiMonitorUpdateSessionId from '!!raw-loader!@site/docs/API-Reference/python-examples/api-monitor/update-session-id.py';
|
||||
import exampleJavascriptApiMonitorUpdateSessionId from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-monitor/update-session-id.js';
|
||||
import examplePythonApiMonitorDeleteMessagesBySession from '!!raw-loader!@site/docs/API-Reference/python-examples/api-monitor/delete-messages-by-session.py';
|
||||
import exampleJavascriptApiMonitorDeleteMessagesBySession from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-monitor/delete-messages-by-session.js';
|
||||
import examplePythonApiMonitorGetTransactions from '!!raw-loader!@site/docs/API-Reference/python-examples/api-monitor/get-transactions.py';
|
||||
import exampleJavascriptApiMonitorGetTransactions from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-monitor/get-transactions.js';
|
||||
|
||||
|
||||
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
The `/monitor` endpoints are for internal Langflow functionality, primarily related to running flows in the **Playground**, storing chat history, and generating flow logs.
|
||||
|
||||
This information is primarily for those who are building custom components or contributing to the Langflow codebase in a way that requires calling or understanding these endpoints.
|
||||
|
||||
For typical application development with Langflow, there are more appropriate options for monitoring, debugging, and memory management.
|
||||
For more information, see the following:
|
||||
|
||||
* [Logs](/logging): Langflow log storage locations, customization options, and where to view logs in the visual editor
|
||||
* [Test flows in the Playground](/concepts-playground): Run flows and inspect message history
|
||||
* [Memory management options](/memory): Langflow storage locations and options, including the database, cache, and chat history
|
||||
|
||||
## Vertex builds
|
||||
|
||||
The Vertex build endpoints (`/monitor/builds`) are exclusively for **Playground** functionality.
|
||||
|
||||
When you run a flow in the **Playground**, Langflow calls the `/build/$FLOW_ID/flow` endpoint in [chat.py](https://github.com/langflow-ai/langflow/blob/main/src/backend/base/langflow/api/v1/chat.py#L130). This call retrieves the flow data, builds a graph, and executes the graph. As each component (or node) is executed, the `build_vertex` function calls `build_and_run`, which may call the individual components' `def_build` method, if it exists. If a component doesn't have a `def_build` function, the build still returns a component.
|
||||
|
||||
The `build` function allows components to execute logic at runtime. For example, the [**Recursive Character Text Splitter** component](https://github.com/langflow-ai/langflow/blob/main/src/lfx/src/lfx/components/langchain_utilities/recursive_character.py) is a child of the `LCTextSplitterComponent` class. When text needs to be processed, the parent class's `build` method is called, which creates a `RecursiveCharacterTextSplitter` object and uses it to split the text according to the defined parameters. The split text is then passed on to the next component. This all occurs when the component is built.
|
||||
|
||||
### Get Vertex builds
|
||||
|
||||
Retrieve Vertex builds for a specific flow.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiMonitorGetVertexBuilds} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiMonitorGetVertexBuilds} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiMonitorGetVertexBuilds} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiMonitorResultGetVertexBuilds} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
### Delete Vertex builds
|
||||
|
||||
Delete Vertex builds for a specific flow.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiMonitorDeleteVertexBuilds} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiMonitorDeleteVertexBuilds} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiMonitorDeleteVertexBuilds} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiMonitorResultDeleteVertexBuilds} language="text" />
|
||||
|
||||
</details>
|
||||
|
||||
## Messages endpoints
|
||||
|
||||
The `/monitor/messages` endpoints store, retrieve, edit, and delete records in the message table in [`langflow.db`](/memory)
|
||||
Typically, these are called implicitly when running flows that produce message history, or when inspecting and modifying **Playground** memories.
|
||||
|
||||
### Get messages
|
||||
|
||||
Retrieve a list of all messages:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiMonitorGetMessages} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiMonitorGetMessages} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiMonitorGetMessages} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
To filter messages, use the `flow_id`, `session_id`, `sender`, and `sender_name` query parameters.
|
||||
|
||||
To sort the results, use the `order_by` query parameter.
|
||||
|
||||
This example retrieves messages sent by `Machine` and `AI` in a given chat session (`session_id`) and orders the messages by timestamp.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiMonitorGetMessages2} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiMonitorGetMessages2} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiMonitorGetMessages2} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiMonitorResultGetMessages2} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
### Delete messages
|
||||
|
||||
Delete specific messages by their IDs.
|
||||
|
||||
This example deletes the message retrieved in the previous `GET /messages` example.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiMonitorDeleteMessages} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiMonitorDeleteMessages} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiMonitorDeleteMessages} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiMonitorResultDeleteMessages} language="text" />
|
||||
|
||||
</details>
|
||||
|
||||
### Update message
|
||||
|
||||
Update a specific message by its ID.
|
||||
|
||||
This example updates the `text` value of message `3ab66cc6-c048-48f8-ab07-570f5af7b160`.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiMonitorUpdateMessage} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiMonitorUpdateMessage} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiMonitorUpdateMessage} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiMonitorResultUpdateMessage} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
### Update session ID
|
||||
|
||||
Update the session ID for messages.
|
||||
|
||||
This example updates the `session_ID` value `01ce083d-748b-4b8d-97b6-33adbb6a528a` to `different_session_id`.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiMonitorUpdateSessionId} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiMonitorUpdateSessionId} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiMonitorUpdateSessionId} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiMonitorResultUpdateSessionId} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
### Delete messages by session
|
||||
|
||||
Delete all messages for a specific session.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiMonitorDeleteMessagesBySession} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiMonitorDeleteMessagesBySession} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiMonitorDeleteMessagesBySession} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiMonitorResultDeleteMessagesBySession} language="text" />
|
||||
|
||||
</details>
|
||||
|
||||
## Get traces
|
||||
|
||||
Retrieve trace metadata and span trees for a specific flow.
|
||||
|
||||
### Example request
|
||||
|
||||
Use `GET /monitor/traces` and filter by `flow_id`:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="python" label="Python">
|
||||
|
||||
<CodeSnippet source={examplePythonApiMonitorExampleRequest} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="typescript" label="TypeScript">
|
||||
|
||||
```ts
|
||||
const baseUrl = process.env.LANGFLOW_SERVER_URL ?? "http://localhost:7860";
|
||||
const apiKey = process.env.LANGFLOW_API_KEY!;
|
||||
const flowId = "YOUR_FLOW_ID";
|
||||
|
||||
async function listTraces() {
|
||||
const url = new URL("/api/v1/monitor/traces", baseUrl);
|
||||
url.searchParams.set("flow_id", flowId);
|
||||
url.searchParams.set("page", "1");
|
||||
url.searchParams.set("size", "50");
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"x-api-key": apiKey,
|
||||
},
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`Request failed with status ${res.status}`);
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
listTraces().catch(console.error);
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiMonitorExampleRequest} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Example response
|
||||
|
||||
```json
|
||||
{
|
||||
"traces": [
|
||||
{
|
||||
"id": "426656db-fc3c-4a3a-acf8-c60acf099543",
|
||||
"name": "Simple Agent - 9e774f60-857b-44b4-bbcd-87bd23848ee8",
|
||||
"status": "ok",
|
||||
"startTime": "2026-03-03T19:13:30.692628Z",
|
||||
"totalLatencyMs": 18693,
|
||||
"totalTokens": 2050,
|
||||
"flowId": "9e774f60-857b-44b4-bbcd-87bd23848ee8",
|
||||
"sessionId": "9e774f60-857b-44b4-bbcd-87bd23848ee8",
|
||||
"input": {
|
||||
"input_value": "Use tools to teach me about vertex graphs"
|
||||
},
|
||||
"output": {
|
||||
"message": {
|
||||
"text_key": "text",
|
||||
"data": {
|
||||
"timestamp": "2026-03-03 19:13:30 UTC",
|
||||
"sender": "Machine",
|
||||
"sender_name": "AI",
|
||||
"session_id": "9e774f60-857b-44b4-bbcd-87bd23848ee8",
|
||||
"text": "I can teach you the concept, but I couldn’t pull the Wikipedia pages with the tool ... (truncated)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"pages": 1
|
||||
}
|
||||
```
|
||||
|
||||
## Get transactions
|
||||
|
||||
Retrieve all transactions, which are interactions between components, for a specific flow.
|
||||
This information is also available in [flow logs](/logging).
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiMonitorGetTransactions} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiMonitorGetTransactions} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiMonitorGetTransactions} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiMonitorResultGetTransactions} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## See also
|
||||
|
||||
- [Use voice mode](/concepts-voice-mode)
|
||||
- [Session ID](/session-id)
|
||||
@ -0,0 +1,529 @@
|
||||
---
|
||||
title: OpenAI Responses API
|
||||
slug: /api-openai-responses
|
||||
---
|
||||
import CodeSnippet from '@site/src/components/CodeSnippet';
|
||||
import exampleApiOpenaiResponsesExampleRequest from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-openai-responses/example-request.sh';
|
||||
import exampleApiOpenaiResponsesExampleStreamingRequest from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-openai-responses/example-streaming-request.sh';
|
||||
import resultApiOpenaiResponsesResultExampleStreamingRequest from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-openai-responses/result-example-streaming-request.json';
|
||||
import exampleApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-openai-responses/continue-conversations-with-response-and-session-ids.sh';
|
||||
import resultApiOpenaiResponsesResultContinueConversationsWithResponseAndSessionIds from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-openai-responses/result-continue-conversations-with-response-and-session-ids.json';
|
||||
import exampleApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-openai-responses/continue-conversations-with-response-and-session-ids-2.sh';
|
||||
import resultApiOpenaiResponsesResultContinueConversationsWithResponseAndSessionIds2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-openai-responses/result-continue-conversations-with-response-and-session-ids-2.json';
|
||||
import exampleApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds3 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-openai-responses/continue-conversations-with-response-and-session-ids-3.sh';
|
||||
import exampleApiOpenaiResponsesRetrieveToolCallResults from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-openai-responses/retrieve-tool-call-results.sh';
|
||||
import resultApiOpenaiResponsesResultRetrieveToolCallResults from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-openai-responses/result-retrieve-tool-call-results.json';
|
||||
import exampleApiOpenaiResponsesPassGlobalVariablesToYourFlowsInHeaders from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-openai-responses/pass-global-variables-to-your-flows-in-headers.sh';
|
||||
import exampleApiOpenaiResponsesTokenUsageTracking from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-openai-responses/token-usage-tracking.sh';
|
||||
import examplePythonApiOpenaiResponsesAdditionalConfigurationForOpenaiClientLibraries from '!!raw-loader!@site/docs/API-Reference/python-examples/api-openai-responses/additional-configuration-for-openai-client-libraries.py';
|
||||
import exampleJavascriptApiOpenaiResponsesAdditionalConfigurationForOpenaiClientLibraries from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-openai-responses/additional-configuration-for-openai-client-libraries.ts';
|
||||
import examplePythonApiOpenaiResponsesTokenUsageTracking from '!!raw-loader!@site/docs/API-Reference/python-examples/api-openai-responses/token-usage-tracking.py';
|
||||
import examplePythonApiOpenaiResponsesExampleRequest from '!!raw-loader!@site/docs/API-Reference/python-examples/api-openai-responses/example-request.py';
|
||||
import exampleJavascriptApiOpenaiResponsesExampleRequest from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-openai-responses/example-request.js';
|
||||
import examplePythonApiOpenaiResponsesExampleStreamingRequest from '!!raw-loader!@site/docs/API-Reference/python-examples/api-openai-responses/example-streaming-request.py';
|
||||
import exampleJavascriptApiOpenaiResponsesExampleStreamingRequest from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-openai-responses/example-streaming-request.js';
|
||||
import examplePythonApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds from '!!raw-loader!@site/docs/API-Reference/python-examples/api-openai-responses/continue-conversations-with-response-and-session-ids.py';
|
||||
import exampleJavascriptApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-openai-responses/continue-conversations-with-response-and-session-ids.js';
|
||||
import examplePythonApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds2 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-openai-responses/continue-conversations-with-response-and-session-ids-2.py';
|
||||
import exampleJavascriptApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds2 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-openai-responses/continue-conversations-with-response-and-session-ids-2.js';
|
||||
import examplePythonApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds3 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-openai-responses/continue-conversations-with-response-and-session-ids-3.py';
|
||||
import exampleJavascriptApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds3 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-openai-responses/continue-conversations-with-response-and-session-ids-3.js';
|
||||
import examplePythonApiOpenaiResponsesRetrieveToolCallResults from '!!raw-loader!@site/docs/API-Reference/python-examples/api-openai-responses/retrieve-tool-call-results.py';
|
||||
import exampleJavascriptApiOpenaiResponsesRetrieveToolCallResults from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-openai-responses/retrieve-tool-call-results.js';
|
||||
import examplePythonApiOpenaiResponsesPassGlobalVariablesToYourFlowsInHeaders from '!!raw-loader!@site/docs/API-Reference/python-examples/api-openai-responses/pass-global-variables-to-your-flows-in-headers.py';
|
||||
import exampleJavascriptApiOpenaiResponsesPassGlobalVariablesToYourFlowsInHeaders from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-openai-responses/pass-global-variables-to-your-flows-in-headers.js';
|
||||
import exampleJavascriptApiOpenaiResponsesTokenUsageTracking from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-openai-responses/token-usage-tracking.js';
|
||||
|
||||
|
||||
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
Langflow includes an endpoint that is compatible with the [OpenAI Responses API](https://platform.openai.com/docs/api-reference/responses).
|
||||
It is available at `POST /api/v1/responses`.
|
||||
|
||||
This endpoint allows you to use existing OpenAI client libraries with minimal code changes.
|
||||
You only need to replace the `model` name, such as `gpt-4`, with your `flow_id`.
|
||||
You can find Flow IDs in the code snippets on the [**API access** pane](/concepts-publish#api-access) or in a flow's URL.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To be compatible with Langflow's OpenAI Responses API endpoint, your flow and request must adhere to the following requirements:
|
||||
|
||||
- **Chat Input**: Your flow must contain a **Chat Input** component.
|
||||
Flows without this component return an error when passed to this endpoint.
|
||||
The component types `ChatInput` and `Chat Input` are recognized as chat inputs.
|
||||
- **Tools**: The `tools` parameter isn't supported, and returns an error if provided.
|
||||
- **Model Names**: In your request, the `model` field must contain a valid flow ID or endpoint name.
|
||||
- **Authentication**: All requests require an API key passed in the `x-api-key` header.
|
||||
For more information, see [API keys and authentication](/api-keys-and-authentication).
|
||||
|
||||
### Additional configuration for OpenAI client libraries
|
||||
|
||||
This endpoint is compatible with OpenAI's API, but requires special configuration when using OpenAI client libraries.
|
||||
Langflow uses `x-api-key` headers for authentication, while OpenAI uses `Authorization: Bearer` headers.
|
||||
When sending requests to Langflow with OpenAI client libraries, you must configure custom headers and include an `api_key` configuration.
|
||||
The `api_key` parameter can have any value, such as `"dummy-api-key"` in the client examples, as the actual authentication is handled through the `default_headers` configuration.
|
||||
|
||||
In the following examples, replace the values for `LANGFLOW_SERVER_URL`, `LANGFLOW_API_KEY`, and `FLOW_ID` with values from your deployment.
|
||||
<Tabs groupId="client">
|
||||
<TabItem value="Python" label="OpenAI Python Client" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiOpenaiResponsesAdditionalConfigurationForOpenaiClientLibraries} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="OpenAI TypeScript Client">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiOpenaiResponsesAdditionalConfigurationForOpenaiClientLibraries} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details closed>
|
||||
<summary>Response</summary>
|
||||
```text
|
||||
Here are the event dates for the second Wednesday of each month in 2026:
|
||||
- January 14, 2026
|
||||
- February 11, 2026
|
||||
- March 11, 2026
|
||||
- April 8, 2026
|
||||
- May 13, 2026
|
||||
- June 10, 2026
|
||||
- July 8, 2026
|
||||
- August 12, 2026
|
||||
- September 9, 2026
|
||||
- October 14, 2026
|
||||
- November 11, 2026
|
||||
- December 9, 2026
|
||||
If you need these in a different format or want a downloadable calendar, let me know!
|
||||
```
|
||||
</details>
|
||||
|
||||
## Example request
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiOpenaiResponsesExampleRequest} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiOpenaiResponsesExampleRequest} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiOpenaiResponsesExampleRequest} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Headers
|
||||
|
||||
| Header | Required | Description | Example |
|
||||
|--------|----------|-------------|---------|
|
||||
| `x-api-key` | Yes | Your Langflow API key for authentication | `"sk-..."` |
|
||||
| `Content-Type` | Yes | Specifies the JSON format | `"application/json"` |
|
||||
| `X-LANGFLOW-GLOBAL-VAR-*` | No | Global variables for the flow | `"X-LANGFLOW-GLOBAL-VAR-API_KEY: sk-..."` For more, see [Pass global variables to your flows in headers](#global-var). |
|
||||
|
||||
### Request body
|
||||
|
||||
| Field | Type | Required | Default | Description |
|
||||
|-------|------|----------|---------|-------------|
|
||||
| `model` | `string` | Yes | - | The flow ID or endpoint name to execute. |
|
||||
| `input` | `string` | Yes | - | The input text to process. |
|
||||
| `stream` | `boolean` | No | `false` | Whether to stream the response. |
|
||||
| `background` | `boolean` | No | `false` | Whether to process in background. |
|
||||
| `tools` | `list[Any]` | No | `null` | Tools are not supported yet. |
|
||||
| `previous_response_id` | `string` | No | `null` | ID of previous response to continue conversation. For more, see [Continue conversations with response and session IDs](#response-id). |
|
||||
| `include` | `list[string]` | No | `null` | Additional response data to include, such as `['tool_call.results']`. For more, see [Retrieve tool call results](#tool-call-results). |
|
||||
|
||||
## Example response
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "e5e8ef8a-7efd-4090-a110-6aca082bceb7",
|
||||
"object": "response",
|
||||
"created_at": 1756837941,
|
||||
"status": "completed",
|
||||
"model": "ced2ec91-f325-4bf0-8754-f3198c2b1563",
|
||||
"output": [
|
||||
{
|
||||
"type": "message",
|
||||
"id": "msg_e5e8ef8a-7efd-4090-a110-6aca082bceb7",
|
||||
"status": "completed",
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"text": "Hello! I'm here and ready to help. How can I assist you today?",
|
||||
"annotations": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"parallel_tool_calls": true,
|
||||
"previous_response_id": null,
|
||||
"reasoning": {"effort": null, "summary": null},
|
||||
"store": true,
|
||||
"temperature": 1.0,
|
||||
"text": {"format": {"type": "text"}},
|
||||
"tool_choice": "auto",
|
||||
"tools": [],
|
||||
"top_p": 1.0,
|
||||
"truncation": "disabled",
|
||||
"usage": null,
|
||||
"user": null,
|
||||
"metadata": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Response body
|
||||
|
||||
The response contains fields that Langflow sets dynamically and fields that use OpenAI-compatible defaults.
|
||||
|
||||
The OpenAI-compatible default values shown above are currently fixed and cannot be modified via the request.
|
||||
They are included to maintain API compatibility and provide a consistent response format.
|
||||
|
||||
For your requests, you will only be setting the dynamic fields.
|
||||
The default values are documented here for completeness and to show the full response structure.
|
||||
|
||||
Fields set dynamically by Langflow:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | `string` | Unique response identifier. |
|
||||
| `created_at` | `int` | Unix timestamp of response creation. |
|
||||
| `model` | `string` | The flow ID that was executed. |
|
||||
| `output` | `list[dict]` | Array of output items (messages, tool calls, etc.). |
|
||||
| `previous_response_id` | `string` | ID of previous response if continuing conversation. |
|
||||
| `usage` | `dict` | Token usage statistics if the `usage` field is available. Contains `prompt_tokens`, `completion_tokens`, and `total_tokens`. |
|
||||
|
||||
<details>
|
||||
<summary>Fields with OpenAI-compatible default values</summary>
|
||||
|
||||
| Field | Type | Default Value | Description |
|
||||
|-------|------|---------------|-------------|
|
||||
| `object` | `string` | `"response"` | Always `"response"`. |
|
||||
| `status` | `string` | `"completed"` | Response status: `"completed"`, `"in_progress"`, or `"failed"`. |
|
||||
| `error` | `dict` | `null` | Error details (if any). |
|
||||
| `incomplete_details` | `dict` | `null` | Incomplete response details (if any). |
|
||||
| `instructions` | `string` | `null` | Response instructions (if any). |
|
||||
| `max_output_tokens` | `int` | `null` | Maximum output tokens (if any). |
|
||||
| `parallel_tool_calls` | `boolean` | `true` | Whether parallel tool calls are enabled. |
|
||||
| `reasoning` | `dict` | `{"effort": null, "summary": null}` | Reasoning information with effort and summary. |
|
||||
| `store` | `boolean` | `true` | Whether response is stored. |
|
||||
| `temperature` | `float` | `1.0` | Temperature setting. |
|
||||
| `text` | `dict` | `{"format": {"type": "text"}}` | Text format configuration. |
|
||||
| `tool_choice` | `string` | `"auto"` | Tool choice setting. |
|
||||
| `tools` | `list[dict]` | `[]` | Available tools. |
|
||||
| `top_p` | `float` | `1.0` | Top-p setting. |
|
||||
| `truncation` | `string` | `"disabled"` | Truncation setting. |
|
||||
| `usage` | `dict` | `null` | Token usage statistics. Set dynamically when available from flow components, otherwise `null`. See [Token usage tracking](#token-usage-tracking). |
|
||||
| `user` | `string` | `null` | User identifier (if any). |
|
||||
| `metadata` | `dict` | `{}` | Additional metadata. |
|
||||
|
||||
</details>
|
||||
|
||||
## Example streaming request
|
||||
|
||||
When you set `"stream": true` with your request, the API returns a stream where each chunk contains a small piece of the response as it's generated. This provides a real-time experience where users can see the AI's output appear word by word, similar to ChatGPT's typing effect.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiOpenaiResponsesExampleStreamingRequest} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiOpenaiResponsesExampleStreamingRequest} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiOpenaiResponsesExampleStreamingRequest} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiOpenaiResponsesResultExampleStreamingRequest} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
### Streaming response body
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | `string` | Unique response identifier. |
|
||||
| `object` | `string` | Always `"response.chunk"`. |
|
||||
| `created` | `int` | Unix timestamp of chunk creation. |
|
||||
| `model` | `string` | The flow ID that was executed. |
|
||||
| `delta` | `dict` | The new content chunk. |
|
||||
| `status` | `string` | Response status: `"completed"`, `"in_progress"`, or `"failed"` (optional). |
|
||||
|
||||
The stream continues until a final chunk with `"status": "completed"` indicates the response is finished.
|
||||
|
||||
<details>
|
||||
<summary>Final completion chunk</summary>
|
||||
|
||||
```
|
||||
{
|
||||
"id": "f7fcea36-f128-41c4-9ac1-e683137375d5",
|
||||
"object": "response.chunk",
|
||||
"created": 1756838094,
|
||||
"model": "ced2ec91-f325-4bf0-8754-f3198c2b1563",
|
||||
"delta": {},
|
||||
"status": "completed"
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
## Continue conversations with response and session IDs {#response-id}
|
||||
|
||||
Conversation continuity allows you to maintain context across multiple API calls, enabling multi-turn conversations with your flows. This is essential for building chat applications where users can have ongoing conversations.
|
||||
|
||||
When you make a request, the API returns a response with an `id` field. You can use this `id` as the `previous_response_id` in your next request to continue the conversation from where it left off.
|
||||
|
||||
First Message:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiOpenaiResponsesResultContinueConversationsWithResponseAndSessionIds} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
Follow-up message:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds2} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds2} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds2} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiOpenaiResponsesResultContinueConversationsWithResponseAndSessionIds2} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
Optionally, you can use your own session ID values for the `previous_response_id`:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds3} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds3} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiOpenaiResponsesContinueConversationsWithResponseAndSessionIds3} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
This example uses the same flow as the other `previous_response_id` examples, but the LLM had not yet been introduced to Alice in the specified session:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "session-alice-1756839048",
|
||||
"object": "response",
|
||||
"created_at": 1756839048,
|
||||
"status": "completed",
|
||||
"model": "ced2ec91-f325-4bf0-8754-f3198c2b1563",
|
||||
"output": [
|
||||
{
|
||||
"type": "message",
|
||||
"id": "msg_session-alice-1756839048",
|
||||
"status": "completed",
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"text": "I don't have access to your name unless you tell me. If you'd like, you can share your name, and I'll remember it for this conversation!",
|
||||
"annotations": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"previous_response_id": "session-alice-1756839048"
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Retrieve tool call results {#tool-call-results}
|
||||
|
||||
When you send a request to the `/api/v1/responses` endpoint to run a flow that includes tools or function calls, you can retrieve the raw tool execution details by adding `"include": ["tool_call.results"]` to the request payload.
|
||||
|
||||
Without the `include` parameter, tool calls return basic function call information, but not the raw tool results.
|
||||
For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "fc_1",
|
||||
"type": "function_call",
|
||||
"status": "completed",
|
||||
"name": "evaluate_expression",
|
||||
"arguments": "{\"expression\": \"15*23\"}"
|
||||
},
|
||||
```
|
||||
|
||||
To get the raw `results` of each tool execution, add `include: ["tool_call.results"]` to the request payload:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiOpenaiResponsesRetrieveToolCallResults} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiOpenaiResponsesRetrieveToolCallResults} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiOpenaiResponsesRetrieveToolCallResults} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The response now includes the tool call's results.
|
||||
For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "evaluate_expression_1",
|
||||
"type": "tool_call",
|
||||
"tool_name": "evaluate_expression",
|
||||
"queries": ["15*23"],
|
||||
"results": {"result": "345"}
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiOpenaiResponsesResultRetrieveToolCallResults} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
Variables passed with `X-LANGFLOW-GLOBAL-VAR-{VARIABLE_NAME}` are always available to your flow, regardless of whether they exist in the database.
|
||||
|
||||
If your flow components reference variables that aren't provided in headers or your Langflow database, the flow fails by default.
|
||||
|
||||
To avoid this, you can set the `FALLBACK_TO_ENV_VARS` environment variable is `true`, which allows the flow to use values from the `.env` file if they aren't otherwise specified.
|
||||
|
||||
In the above example, `OPENAI_API_KEY` will fall back to the database variable if not provided in the header.
|
||||
`USER_ID` and `ENVIRONMENT` will fall back to environment variables if `FALLBACK_TO_ENV_VARS` is enabled.
|
||||
Otherwise, the flow fails.
|
||||
|
||||
## Token usage tracking {#token-usage-tracking}
|
||||
|
||||
The OpenAI Responses API endpoint tracks token usage when your flow uses language model components that provide token usage information. The `usage` field in the response contains statistics about the number of tokens used for the request and response.
|
||||
|
||||
Token usage is automatically extracted from the flow execution results when the `usage` field is available.
|
||||
The `usage` field follows OpenAI's format with `prompt_tokens`, `completion_tokens`, and `total_tokens` fields.
|
||||
If token usage information is not available from the flow components, the `usage` field is `null`.
|
||||
|
||||
The `usage` field is always present in the response, either with token counts or as `null`. The conditional checks shown in the examples below are optional defensive programming to handle cases where usage might not be available.
|
||||
|
||||
<Tabs groupId="token-usage">
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiOpenaiResponsesTokenUsageTracking} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiOpenaiResponsesTokenUsageTracking} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiOpenaiResponsesTokenUsageTracking} language="bash" />
|
||||
|
||||
<details>
|
||||
<summary>Response with token usage</summary>
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||||
"object": "response",
|
||||
"created_at": 1756837941,
|
||||
"status": "completed",
|
||||
"model": "ced2ec91-f325-4bf0-8754-f3198c2b1563",
|
||||
"output": [
|
||||
{
|
||||
"type": "message",
|
||||
"id": "msg_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||||
"status": "completed",
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"type": "output_text",
|
||||
"text": "Quantum computing is a type of computing that uses quantum mechanical phenomena...",
|
||||
"annotations": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": 12,
|
||||
"completion_tokens": 145,
|
||||
"total_tokens": 157
|
||||
},
|
||||
"previous_response_id": null
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
@ -0,0 +1,259 @@
|
||||
---
|
||||
title: Projects endpoints
|
||||
slug: /api-projects
|
||||
---
|
||||
import CodeSnippet from '@site/src/components/CodeSnippet';
|
||||
import exampleApiProjectsReadProjects from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-projects/read-projects.sh';
|
||||
import resultApiProjectsResultReadProjects from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-projects/result-read-projects.json';
|
||||
import exampleApiProjectsCreateProject from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-projects/create-project.sh';
|
||||
import resultApiProjectsResultCreateProject from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-projects/result-create-project.json';
|
||||
import exampleApiProjectsCreateProject2 from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-projects/create-project-2.sh';
|
||||
import exampleApiProjectsReadProject from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-projects/read-project.sh';
|
||||
import resultApiProjectsResultReadProject from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-projects/result-read-project.json';
|
||||
import exampleApiProjectsUpdateProject from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-projects/update-project.sh';
|
||||
import resultApiProjectsResultUpdateProject from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-projects/result-update-project.json';
|
||||
import exampleApiProjectsDeleteProject from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-projects/delete-project.sh';
|
||||
import resultApiProjectsResultDeleteProject from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-projects/result-delete-project.txt';
|
||||
import exampleApiProjectsExportAProject from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-projects/export-a-project.sh';
|
||||
import exampleApiProjectsImportAProject from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-projects/import-a-project.sh';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import examplePythonApiProjectsReadProjects from '!!raw-loader!@site/docs/API-Reference/python-examples/api-projects/read-projects.py';
|
||||
import exampleJavascriptApiProjectsReadProjects from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-projects/read-projects.js';
|
||||
import examplePythonApiProjectsCreateProject from '!!raw-loader!@site/docs/API-Reference/python-examples/api-projects/create-project.py';
|
||||
import exampleJavascriptApiProjectsCreateProject from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-projects/create-project.js';
|
||||
import examplePythonApiProjectsCreateProject2 from '!!raw-loader!@site/docs/API-Reference/python-examples/api-projects/create-project-2.py';
|
||||
import exampleJavascriptApiProjectsCreateProject2 from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-projects/create-project-2.js';
|
||||
import examplePythonApiProjectsReadProject from '!!raw-loader!@site/docs/API-Reference/python-examples/api-projects/read-project.py';
|
||||
import exampleJavascriptApiProjectsReadProject from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-projects/read-project.js';
|
||||
import examplePythonApiProjectsUpdateProject from '!!raw-loader!@site/docs/API-Reference/python-examples/api-projects/update-project.py';
|
||||
import exampleJavascriptApiProjectsUpdateProject from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-projects/update-project.js';
|
||||
import examplePythonApiProjectsDeleteProject from '!!raw-loader!@site/docs/API-Reference/python-examples/api-projects/delete-project.py';
|
||||
import exampleJavascriptApiProjectsDeleteProject from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-projects/delete-project.js';
|
||||
import examplePythonApiProjectsExportAProject from '!!raw-loader!@site/docs/API-Reference/python-examples/api-projects/export-a-project.py';
|
||||
import exampleJavascriptApiProjectsExportAProject from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-projects/export-a-project.js';
|
||||
import examplePythonApiProjectsImportAProject from '!!raw-loader!@site/docs/API-Reference/python-examples/api-projects/import-a-project.py';
|
||||
import exampleJavascriptApiProjectsImportAProject from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-projects/import-a-project.js';
|
||||
|
||||
|
||||
|
||||
Use the `/projects` endpoint to create, read, update, and delete [Langflow projects](/concepts-flows#projects).
|
||||
|
||||
## Read projects
|
||||
|
||||
Get a list of Langflow projects, including project IDs, names, and descriptions.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiProjectsReadProjects} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiProjectsReadProjects} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiProjectsReadProjects} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiProjectsResultReadProjects} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## Create project
|
||||
|
||||
Create a new project.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiProjectsCreateProject} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiProjectsCreateProject} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiProjectsCreateProject} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiProjectsResultCreateProject} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
To add flows and components at project creation, retrieve the `components_list` and `flows_list` values from the [`/all`](/api-reference-api-examples#get-all-components) and [/flows/read](/api-flows#read-flows) endpoints and add them to the request body.
|
||||
|
||||
Adding a flow to a project moves the flow from its previous location. The flow isn't copied.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiProjectsCreateProject2} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiProjectsCreateProject2} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiProjectsCreateProject2} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Read project
|
||||
|
||||
Retrieve details of a specific project.
|
||||
|
||||
To find the UUID of your project, call the [read projects](#read-projects) endpoint.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiProjectsReadProject} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiProjectsReadProject} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiProjectsReadProject} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiProjectsResultReadProject} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## Update project
|
||||
|
||||
Update the information of a specific project with a `PATCH` request.
|
||||
|
||||
Each PATCH request updates the project with the values you send.
|
||||
Only the fields you include in your request are updated.
|
||||
If you send the same values multiple times, the update is still processed, even if the values are unchanged.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiProjectsUpdateProject} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiProjectsUpdateProject} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiProjectsUpdateProject} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiProjectsResultUpdateProject} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## Delete project
|
||||
|
||||
Delete a specific project.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiProjectsDeleteProject} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiProjectsDeleteProject} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiProjectsDeleteProject} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiProjectsResultDeleteProject} language="text" />
|
||||
|
||||
</details>
|
||||
|
||||
## Export a project
|
||||
|
||||
Download all flows from a project as a zip file.
|
||||
|
||||
The `--output` flag is optional.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiProjectsExportAProject} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiProjectsExportAProject} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiProjectsExportAProject} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Import a project
|
||||
|
||||
Import a project and its flows by uploading a Langflow project zip file:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiProjectsImportAProject} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiProjectsImportAProject} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiProjectsImportAProject} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
@ -0,0 +1,438 @@
|
||||
---
|
||||
title: Get started with the Langflow API
|
||||
slug: /api-reference-api-examples
|
||||
---
|
||||
import CodeSnippet from '@site/src/components/CodeSnippet';
|
||||
import exampleApiReferenceApiExamplesFormLangflowApiRequests from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-reference-api-examples/form-langflow-api-requests.sh';
|
||||
import exampleApiReferenceApiExamplesSetEnvironmentVariables from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-reference-api-examples/set-environment-variables.sh';
|
||||
import exampleApiReferenceApiExamplesHealthCheck from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-reference-api-examples/health-check.sh';
|
||||
import resultApiReferenceApiExamplesResultHealthCheck from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-reference-api-examples/result-health-check.json';
|
||||
import exampleApiReferenceApiExamplesGetVersion from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-reference-api-examples/get-version.sh';
|
||||
import resultApiReferenceApiExamplesResultGetVersion from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-reference-api-examples/result-get-version.txt';
|
||||
import exampleApiReferenceApiExamplesGetConfiguration from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-reference-api-examples/get-configuration.sh';
|
||||
import resultApiReferenceApiExamplesResultGetConfiguration from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-reference-api-examples/result-get-configuration.json';
|
||||
import exampleApiReferenceApiExamplesGetAllComponents from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-reference-api-examples/get-all-components.sh';
|
||||
import examplePythonApiReferenceApiExamplesFormLangflowApiRequests from '!!raw-loader!@site/docs/API-Reference/python-examples/api-reference-api-examples/form-langflow-api-requests.py';
|
||||
import exampleJavascriptApiReferenceApiExamplesFormLangflowApiRequests from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-reference-api-examples/form-langflow-api-requests.js';
|
||||
import examplePythonApiReferenceApiExamplesHealthCheck from '!!raw-loader!@site/docs/API-Reference/python-examples/api-reference-api-examples/health-check.py';
|
||||
import exampleJavascriptApiReferenceApiExamplesHealthCheck from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-reference-api-examples/health-check.js';
|
||||
import examplePythonApiReferenceApiExamplesGetVersion from '!!raw-loader!@site/docs/API-Reference/python-examples/api-reference-api-examples/get-version.py';
|
||||
import exampleJavascriptApiReferenceApiExamplesGetVersion from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-reference-api-examples/get-version.js';
|
||||
import examplePythonApiReferenceApiExamplesGetConfiguration from '!!raw-loader!@site/docs/API-Reference/python-examples/api-reference-api-examples/get-configuration.py';
|
||||
import exampleJavascriptApiReferenceApiExamplesGetConfiguration from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-reference-api-examples/get-configuration.js';
|
||||
import examplePythonApiReferenceApiExamplesGetAllComponents from '!!raw-loader!@site/docs/API-Reference/python-examples/api-reference-api-examples/get-all-components.py';
|
||||
import exampleJavascriptApiReferenceApiExamplesGetAllComponents from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-reference-api-examples/get-all-components.js';
|
||||
|
||||
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
You can use the Langflow API for programmatic interactions with Langflow, such as the following:
|
||||
|
||||
* Create and edit flows, including file management for flows.
|
||||
* Develop applications that use your flows.
|
||||
* Develop custom components.
|
||||
* Build Langflow as a dependency of a larger application, codebase, or service.
|
||||
* Contribute to the overall Langflow codebase.
|
||||
|
||||
To view and test all available endpoints, you can access the Langflow API's OpenAPI specification at your Langflow deployment's `/docs` endpoint, such as `http://localhost:7860/docs`.
|
||||
|
||||
:::tip Try it
|
||||
For an example of the Langflow API in a script, see the [Langflow quickstart](/get-started-quickstart).
|
||||
|
||||
The quickstart demonstrates how to get automatically generated code snippets for your flows, use a script to run a flow, and extract data from the Langfow API response.
|
||||
:::
|
||||
|
||||
## Form Langflow API requests
|
||||
|
||||
While individual options vary by endpoint, all Langflow API requests share some commonalities, like a URL, method, parameters, and authentication.
|
||||
|
||||
As an example of a Langflow API request, the following curl command calls the `/v1/run` endpoint, and it passes a runtime override (`tweaks`) to the flow's **Chat Output** component:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiReferenceApiExamplesFormLangflowApiRequests} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiReferenceApiExamplesFormLangflowApiRequests} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiReferenceApiExamplesFormLangflowApiRequests} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Base URL
|
||||
|
||||
By default, local deployments serve the Langflow API at `http://localhost:7860/api`.
|
||||
|
||||
Remotely hosted Langflow deployments are available at the domain set by the hosting service, such as `http://IP_OR_DNS/api` or `http://IP_OR_DNS:LANGFLOW_PORT/api`.
|
||||
|
||||
You can configure the Langflow port number in the `LANGFLOW_PORT` [environment variable](/environment-variables).
|
||||
|
||||
* `https://UUID.ngrok.app/api`
|
||||
* `http://IP_OR_DNS/api`
|
||||
* `http://IP_OR_DNS:LANGFLOW_PORT/api`
|
||||
|
||||
### Authentication
|
||||
|
||||
In Langflow versions 1.5 and later, most API endpoints require authentication with a Langflow API key in either an `x-api-key` header or query parameter.
|
||||
For more information, see [API keys and authentication](/api-keys-and-authentication).
|
||||
|
||||
As with any API, follow industry best practices for storing and referencing sensitive credentials.
|
||||
For example, you can [set environment variables](#set-environment-variables) for your API keys, and then reference those environment variables in your API requests.
|
||||
|
||||
### Methods, paths, and parameters
|
||||
|
||||
Langflow API requests use various methods, paths, path parameters, query parameters, and body parameters.
|
||||
The specific requirements and options depend on the endpoint that you want to call.
|
||||
|
||||
For example, to create a flow, you pass a JSON-formatted flow definition to `POST /v1/flows`.
|
||||
Then, to run your flow, you call `POST /v1/run/$FLOW_ID` with optional run parameters in the request body.
|
||||
|
||||
### API versions
|
||||
|
||||
The Langflow API serves `/v1` and `/v2` endpoints.
|
||||
|
||||
Some endpoints only exist under a single version and some exist under both the `/v1` and `/v2` versions.
|
||||
|
||||
If a request fails or has an unexpected result, make sure your endpoint path has the correct version.
|
||||
|
||||
## Set environment variables
|
||||
|
||||
You can store commonly used values in environment variables to facilitate reuse, simplify token rotation, and securely reference sensitive values.
|
||||
|
||||
You can use any method you prefer to set environment variables, such as `export`, `.env`, `zshrc`, or `.curlrc`.
|
||||
Then, reference those environment variables in your API requests.
|
||||
For example:
|
||||
|
||||
<CodeSnippet source={exampleApiReferenceApiExamplesSetEnvironmentVariables} language="bash" />
|
||||
|
||||
Commonly used values in Langflow API requests include your [Langflow server URL](#base-url), [Langflow API keys](#authentication), flow IDs, and [project IDs](/api-projects#read-projects).
|
||||
|
||||
You can retrieve flow IDs from the [**API access** pane](/concepts-publish#api-access), in a flow's URL, and with [`GET /flows`](/api-flows#read-flows).
|
||||
|
||||
## Try some Langflow API requests
|
||||
|
||||
Once you have your Langflow server URL, try calling these endpoints that return Langflow metadata.
|
||||
|
||||
### Health check
|
||||
|
||||
Returns the health status of the Langflow database and chat services:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiReferenceApiExamplesHealthCheck} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiReferenceApiExamplesHealthCheck} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiReferenceApiExamplesHealthCheck} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiReferenceApiExamplesResultHealthCheck} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
Langflow provides an additional `GET /health` endpoint.
|
||||
This endpoint is served by uvicorn before Langflow is fully initialized, so it's not reliable for checking Langflow service health.
|
||||
|
||||
### Get version
|
||||
|
||||
Returns the current Langflow API version:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiReferenceApiExamplesGetVersion} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiReferenceApiExamplesGetVersion} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiReferenceApiExamplesGetVersion} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiReferenceApiExamplesResultGetVersion} language="text" />
|
||||
|
||||
</details>
|
||||
|
||||
### Get configuration
|
||||
|
||||
Returns configuration details for your Langflow deployment.
|
||||
Requires a [Langflow API key](/api-keys-and-authentication).
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiReferenceApiExamplesGetConfiguration} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiReferenceApiExamplesGetConfiguration} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiReferenceApiExamplesGetConfiguration} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiReferenceApiExamplesResultGetConfiguration} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
### Get all components
|
||||
|
||||
Returns a dictionary of all Langflow components.
|
||||
Requires a [Langflow API key](/api-keys-and-authentication).
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiReferenceApiExamplesGetAllComponents} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiReferenceApiExamplesGetAllComponents} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiReferenceApiExamplesGetAllComponents} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Available endpoints
|
||||
|
||||
Because you can run Langflow as either an IDE (frontend and backend) or a runtime (headless, backend-only), it serves endpoints that support frontend and backend operations.
|
||||
Many endpoints are for orchestration between the frontend and backend, reading and writing to the Langflow database, or enabling frontend functionality, like the **Playground**.
|
||||
Unless you are contributing to the Langflow codebase, you won't directly call most of the Langflow endpoints.
|
||||
|
||||
For application development, the most commonly used endpoints are the `/run` and `/webhook` [flow trigger endpoints](/api-flows-run).
|
||||
For some use cases, you might use some other endpoints, such as the `/files` endpoints to use files in flows.
|
||||
|
||||
To help you explore the available endpoints, the following lists are sorted by primary use case, although some endpoints might support multiple use cases.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Application development" label="Application development" default>
|
||||
|
||||
The following endpoints are useful for developing applications with Langflow and administering Langflow deployments with one or more users.
|
||||
You will most often use the flow trigger endpoints.
|
||||
Other endpoints are helpful for specific use cases, such as administration and flow management in runtime deployments that don't have a visual editor.
|
||||
|
||||
* [Flow trigger endpoints](/api-flows-run):
|
||||
* POST `/v1/run/{flow_id_or_name}`: Run a flow.
|
||||
* POST `/v1/run/advanced/{flow_id}`: Advanced run with explicit `inputs`, `outputs`, `tweaks`, and optional `session_id`.
|
||||
* POST `/v1/webhook/{flow_id_or_name}`: Trigger a flow via webhook payload.
|
||||
|
||||
* [OpenAI Responses API](/api-openai-responses):
|
||||
* POST `/v1/responses`: Execute flows using an OpenAI-compatible request format.
|
||||
|
||||
* Deployment details:
|
||||
* GET `/v1/version`: Return Langflow version. See [Get version](/api-reference-api-examples#get-version).
|
||||
* GET `/v1/config`: Return deployment configuration. See [Get configuration](/api-reference-api-examples#get-configuration).
|
||||
* GET `/health_check`: Health check endpoint that validates database and chat service connectivity. Returns 500 status if any service is unavailable.
|
||||
|
||||
* [Projects endpoints](/api-projects):
|
||||
* POST `/v1/projects/`: Create a project.
|
||||
* GET `/v1/projects/`: List projects.
|
||||
* GET `/v1/projects/{project_id}`: Read a project (with paginated flows support).
|
||||
* PATCH `/v1/projects/{project_id}`: Update project info and membership.
|
||||
* DELETE `/v1/projects/{project_id}`: Delete a project.
|
||||
* GET `/v1/projects/download/{project_id}`: Export all flows in a project as ZIP.
|
||||
* POST `/v1/projects/upload/`: Import a project ZIP (creates project and flows).
|
||||
* GET `/v1/starter-projects/`: Return a list of templates.
|
||||
|
||||
* [Files endpoints](/api-files):
|
||||
* Files (v1)
|
||||
* POST `/v1/files/upload/{flow_id}`: Upload a file to a specific flow.
|
||||
* GET `/v1/files/download/{flow_id}/{file_name}`: Download a file from a flow.
|
||||
* GET `/v1/files/images/{flow_id}/{file_name}`: Stream an image from a flow.
|
||||
* GET `/v1/files/profile_pictures/{folder_name}/{file_name}`: Get a profile picture asset.
|
||||
* GET `/v1/files/profile_pictures/list`: List available profile picture assets.
|
||||
* GET `/v1/files/list/{flow_id}`: List files for a flow.
|
||||
* DELETE `/v1/files/delete/{flow_id}/{file_name}`: Delete a file from a flow.
|
||||
* Files (v2)
|
||||
* POST `/v2/files` (alias `/v2/files/`): Upload a file owned by the current user.
|
||||
* GET `/v2/files` (alias `/v2/files/`): List files owned by the current user.
|
||||
* DELETE `/v2/files/batch/`: Delete multiple files by IDs.
|
||||
* POST `/v2/files/batch/`: Download multiple files as a ZIP by IDs.
|
||||
* GET `/v2/files/{file_id}`: Download a file by ID (or return raw content internally).
|
||||
* PUT `/v2/files/{file_id}`: Edit a file name by ID.
|
||||
* DELETE `/v2/files/{file_id}`: Delete a file by ID.
|
||||
* DELETE `/v2/files` (alias `/v2/files/`): Delete all files for the current user.
|
||||
|
||||
* [API keys and authentication](/api-keys-and-authentication):
|
||||
* GET `/v1/api_key/`: List API keys for the current user.
|
||||
* POST `/v1/api_key/`: Create a new API key.
|
||||
* DELETE `/v1/api_key/{api_key_id}`: Delete an API key.
|
||||
* POST `/v1/api_key/store`: Save an encrypted Store API key (cookie set).
|
||||
|
||||
* [Flow management endpoints](/api-flows):
|
||||
* POST `/v1/flows/`: Create a flow.
|
||||
* GET `/v1/flows/`: List flows (supports pagination and filters).
|
||||
* GET `/v1/flows/{flow_id}`: Read a flow by ID.
|
||||
* GET `/v1/flows/public_flow/{flow_id}`: Read a public flow by ID.
|
||||
* PATCH `/v1/flows/{flow_id}`: Update a flow.
|
||||
* DELETE `/v1/flows/{flow_id}`: Delete a flow.
|
||||
* POST `/v1/flows/batch/`: Create multiple flows.
|
||||
* POST `/v1/flows/upload/`: Import flows from a JSON file.
|
||||
* DELETE `/v1/flows/`: Delete multiple flows by IDs.
|
||||
* POST `/v1/flows/download/`: Export flows to a ZIP file.
|
||||
* GET `/v1/flows/basic_examples/`: List basic example flows.
|
||||
|
||||
* [Users endpoints](/api-users):
|
||||
* POST `/v1/users/`: Add a user (superuser required when auth enabled).
|
||||
* GET `/v1/users/whoami`: Return the current authenticated user.
|
||||
* GET `/v1/users/`: List all users (superuser required).
|
||||
* PATCH `/v1/users/{user_id}`: Update a user (with role checks).
|
||||
* PATCH `/v1/users/{user_id}/reset-password`: Reset own password.
|
||||
* DELETE `/v1/users/{user_id}`: Delete a user (cannot delete yourself).
|
||||
|
||||
* Custom components: You might use these endpoints when developing custom Langflow components for your own use or to share with the Langflow community:
|
||||
* GET `/v1/all`: Return all available Langflow component types. See [Get all components](/api-reference-api-examples#get-all-components).
|
||||
* POST `/v1/custom_component`: Build a custom component from code and return its node.
|
||||
* POST `/v1/custom_component/update`: Update an existing custom component's build config and outputs.
|
||||
* POST `/v1/validate/code`: Validate a Python code snippet for a custom component.
|
||||
* POST `/v1/validate/prompt`: Validate a prompt payload.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Codebase contribution" label="Codebase development">
|
||||
|
||||
The following endpoints are most often used when contributing to the Langflow codebase, and you need to understand or call endpoints that support frontend-to-backend orchestration or other internal functionality.
|
||||
|
||||
* Base (metadata):
|
||||
* GET `/v1/all`: Return all available Langflow component types. See [Get all components](/api-reference-api-examples#get-all-components).
|
||||
* GET `/v1/version`: Return Langflow version. See [Get version](/api-reference-api-examples#get-version).
|
||||
* GET `/v1/config`: Return deployment configuration. See [Get configuration](/api-reference-api-examples#get-configuration).
|
||||
* GET `/v1/starter-projects/`: Return a list of templates.
|
||||
|
||||
* [Build endpoints](/api-build) (internal editor support):
|
||||
* POST `/v1/build/{flow_id}/flow`: Start a flow build and return a job ID.
|
||||
* GET `/v1/build/{job_id}/events`: Stream or fetch build events.
|
||||
* POST `/v1/build/{job_id}/cancel`: Cancel a build job.
|
||||
* POST `/v1/build_public_tmp/{flow_id}/flow`: Build a public flow without auth.
|
||||
* POST `/v1/validate/prompt`: Validate a prompt payload.
|
||||
|
||||
* [API keys and authentication](/api-keys-and-authentication):
|
||||
* POST `/v1/login`: Login and set tokens as cookies.
|
||||
* GET `/v1/auto_login`: Auto-login (if enabled) and set tokens.
|
||||
* POST `/v1/refresh`: Refresh tokens using refresh cookie.
|
||||
* POST `/v1/logout`: Logout and clear cookies.
|
||||
|
||||
* [Monitor endpoints](/api-monitor):
|
||||
* GET `/v1/monitor/builds`: Get vertex builds for a flow.
|
||||
* DELETE `/v1/monitor/builds`: Delete vertex builds for a flow.
|
||||
* GET `/v1/monitor/messages/sessions`: List message session IDs (auth required).
|
||||
* GET `/v1/monitor/messages`: List messages with optional filters.
|
||||
* DELETE `/v1/monitor/messages`: Delete messages by IDs (auth required).
|
||||
* PUT `/v1/monitor/messages/{message_id}`: Update a message.
|
||||
* PATCH `/v1/monitor/messages/session/{old_session_id}`: Change a session ID for all messages in that session.
|
||||
* DELETE `/v1/monitor/messages/session/{session_id}`: Delete messages by session.
|
||||
* GET `/v1/monitor/transactions`: List transactions for a flow (paginated).
|
||||
|
||||
* Variables:
|
||||
* POST `/v1/variables/`: Create a variable, such as an API key, for the user.
|
||||
* GET `/v1/variables/`: List variables for the user.
|
||||
* PATCH `/v1/variables/{variable_id}`: Update a variable.
|
||||
* DELETE `/v1/variables/{variable_id}`: Delete a variable.
|
||||
|
||||
* [Use voice mode](/concepts-voice-mode):
|
||||
* WS `/v1/voice/ws/flow_tts/{flow_id}`: Speech-to-text session that runs a flow and returns TTS.
|
||||
* WS `/v1/voice/ws/flow_tts/{flow_id}/{session_id}`: Same as above with explicit session ID.
|
||||
* GET `/v1/voice/elevenlabs/voice_ids`: List available ElevenLabs voice IDs for the user.
|
||||
|
||||
* MCP servers: The following endpoints are for managing Langflow MCP servers and MCP server connections.
|
||||
They aren't typically called directly; instead, they are used to drive internal functionality in the Langflow frontend and when running flows that call MCP servers.
|
||||
Langflow MCP servers support both streamable HTTP and SSE transport.
|
||||
* HEAD `/v1/mcp/streamable`: Health check for streamable HTTP MCP.
|
||||
* GET `/v1/mcp/streamable`: Open streamable HTTP connection for MCP server.
|
||||
* POST `/v1/mcp/streamable`: Post messages to the MCP server via streamable HTTP.
|
||||
* DELETE `/v1/mcp/streamable`: Close streamable HTTP connection.
|
||||
* HEAD `/v1/mcp/sse` (LEGACY): Health check for MCP SSE.
|
||||
* GET `/v1/mcp/sse` (LEGACY): Open SSE stream for MCP server events.
|
||||
* POST `/v1/mcp/` (LEGACY): Post messages to the MCP server.
|
||||
* GET `/v1/mcp/project/{project_id}`: List MCP-enabled tools and project auth settings.
|
||||
* HEAD `/v1/mcp/project/{project_id}/streamable`: Health check for project streamable HTTP MCP.
|
||||
* GET `/v1/mcp/project/{project_id}/streamable`: Open project-scoped streamable HTTP connection.
|
||||
* POST `/v1/mcp/project/{project_id}/streamable`: Post messages to project MCP server via streamable HTTP.
|
||||
* DELETE `/v1/mcp/project/{project_id}/streamable`: Close project streamable HTTP connection.
|
||||
* HEAD `/v1/mcp/project/{project_id}/sse` (LEGACY): Health check for project SSE.
|
||||
* GET `/v1/mcp/project/{project_id}/sse` (LEGACY): Open project-scoped MCP SSE.
|
||||
* POST `/v1/mcp/project/{project_id}` (LEGACY): Post messages to project MCP server.
|
||||
* PATCH `/v1/mcp/project/{project_id}`: Update MCP settings for flows and project auth settings.
|
||||
* POST `/v1/mcp/project/{project_id}/install`: Install MCP client config for Cursor/Windsurf/Claude (local only).
|
||||
* GET `/v1/mcp/project/{project_id}/installed`: Check which clients have MCP config installed.
|
||||
|
||||
* Custom components: You might use these endpoints when developing custom Langflow components for your own use or to share with the Langflow community:
|
||||
* GET `/v1/all`: Return all available Langflow component types. See [Get all components](/api-reference-api-examples#get-all-components).
|
||||
* POST `/v1/custom_component`: Build a custom component from code and return its node.
|
||||
* POST `/v1/custom_component/update`: Update an existing custom component's build config and outputs.
|
||||
* POST `/v1/validate/code`: Validate a Python code snippet for a custom component.
|
||||
* POST `/v1/validate/prompt`: Validate a prompt payload.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="Deprecated" label="Deprecated">
|
||||
|
||||
The following endpoints are deprecated:
|
||||
|
||||
* POST `/v1/predict/{flow_id}`: Use [`/v1/run/{flow_id}`](/api-flows-run) instead.
|
||||
* POST `/v1/process/{flow_id}`: Use [`/v1/run/{flow_id}`](/api-flows-run) instead.
|
||||
* GET `/v1/task/{task_id}`: Deprecated functionality.
|
||||
* POST `/v1/upload/{flow_id}`: Use [`/files`](/api-files) instead.
|
||||
* POST `/v1/build/{flow_id}/vertices`: Replaced by [`/monitor/builds`](/api-monitor).
|
||||
* POST `/v1/build/{flow_id}/vertices/{vertex_id}`: Replaced by [`/monitor/builds`](/api-monitor).
|
||||
* GET `/v1/build/{flow_id}/{vertex_id}/stream`: Replaced by [`/monitor/builds`](/api-monitor).
|
||||
* GET `/v1/store/check/`: Return whether the Store feature is enabled.
|
||||
* GET `/v1/store/check/api_key`: Check if a Store API key exists and is valid.
|
||||
* POST `/v1/store/components/`: Share a component to the Store.
|
||||
* PATCH `/v1/store/components/{component_id}`: Update a shared component.
|
||||
* GET `/v1/store/components/`: List available Store components (filters supported).
|
||||
* GET `/v1/store/components/{component_id}`: Download a component from the Store.
|
||||
* GET `/v1/store/tags`: List Store tags.
|
||||
* GET `/v1/store/users/likes`: List components liked by the current user.
|
||||
* POST `/v1/store/users/likes/{component_id}`: Like a component.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Next steps
|
||||
|
||||
* Use the Langflow API to [run a flow](/api-flows-run).
|
||||
* Use the Langflow API to [upload files](/api-files).
|
||||
* Use the Langflow API to [get flow logs](/api-logs).
|
||||
* Explore all endpoints in the [Langflow API specification](/api).
|
||||
225
docs/versioned_docs/version-1.10.0/API-Reference/api-users.mdx
Normal file
225
docs/versioned_docs/version-1.10.0/API-Reference/api-users.mdx
Normal file
@ -0,0 +1,225 @@
|
||||
---
|
||||
title: Users endpoints
|
||||
slug: /api-users
|
||||
---
|
||||
import CodeSnippet from '@site/src/components/CodeSnippet';
|
||||
import exampleApiUsersAddUser from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-users/add-user.sh';
|
||||
import resultApiUsersResultAddUser from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-users/result-add-user.json';
|
||||
import exampleApiUsersGetCurrentUser from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-users/get-current-user.sh';
|
||||
import resultApiUsersResultGetCurrentUser from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-users/result-get-current-user.json';
|
||||
import exampleApiUsersListAllUsers from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-users/list-all-users.sh';
|
||||
import resultApiUsersResultListAllUsers from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-users/result-list-all-users.json';
|
||||
import exampleApiUsersUpdateUser from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-users/update-user.sh';
|
||||
import resultApiUsersResultUpdateUser from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-users/result-update-user.json';
|
||||
import exampleApiUsersResetPassword from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-users/reset-password.sh';
|
||||
import resultApiUsersResultResetPassword from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-users/result-reset-password.json';
|
||||
import exampleApiUsersDeleteUser from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-users/delete-user.sh';
|
||||
import resultApiUsersResultDeleteUser from '!!raw-loader!@site/docs/API-Reference/curl-examples/api-users/result-delete-user.json';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import examplePythonApiUsersAddUser from '!!raw-loader!@site/docs/API-Reference/python-examples/api-users/add-user.py';
|
||||
import exampleJavascriptApiUsersAddUser from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-users/add-user.js';
|
||||
import examplePythonApiUsersGetCurrentUser from '!!raw-loader!@site/docs/API-Reference/python-examples/api-users/get-current-user.py';
|
||||
import exampleJavascriptApiUsersGetCurrentUser from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-users/get-current-user.js';
|
||||
import examplePythonApiUsersListAllUsers from '!!raw-loader!@site/docs/API-Reference/python-examples/api-users/list-all-users.py';
|
||||
import exampleJavascriptApiUsersListAllUsers from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-users/list-all-users.js';
|
||||
import examplePythonApiUsersUpdateUser from '!!raw-loader!@site/docs/API-Reference/python-examples/api-users/update-user.py';
|
||||
import exampleJavascriptApiUsersUpdateUser from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-users/update-user.js';
|
||||
import examplePythonApiUsersResetPassword from '!!raw-loader!@site/docs/API-Reference/python-examples/api-users/reset-password.py';
|
||||
import exampleJavascriptApiUsersResetPassword from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-users/reset-password.js';
|
||||
import examplePythonApiUsersDeleteUser from '!!raw-loader!@site/docs/API-Reference/python-examples/api-users/delete-user.py';
|
||||
import exampleJavascriptApiUsersDeleteUser from '!!raw-loader!@site/docs/API-Reference/javascript-examples/api-users/delete-user.js';
|
||||
|
||||
|
||||
|
||||
Use the `/users` endpoint to manage user accounts in Langflow.
|
||||
|
||||
## Add user
|
||||
|
||||
Create a new user account with a given username and password.
|
||||
|
||||
Requires authentication as a superuser if the Langflow server has authentication enabled.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiUsersAddUser} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiUsersAddUser} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiUsersAddUser} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The request returns an object describing the new user.
|
||||
The user's UUID is stored in `user_id` in the Langflow database, and returned as `id` in the `/users` API response.
|
||||
This `user_id` key is specifically for Langflow user management.
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiUsersResultAddUser} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## Get current user
|
||||
|
||||
Retrieve information about the authenticated user.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiUsersGetCurrentUser} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiUsersGetCurrentUser} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiUsersGetCurrentUser} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiUsersResultGetCurrentUser} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## List all users
|
||||
|
||||
Get a paginated list of all users in the system.
|
||||
|
||||
Requires authentication as a superuser if the Langflow server has authentication enabled.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiUsersListAllUsers} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiUsersListAllUsers} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiUsersListAllUsers} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiUsersResultListAllUsers} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## Update user
|
||||
|
||||
Modify an existing user's information with a PATCH request.
|
||||
|
||||
Requires authentication as a superuser if the Langflow server has authentication enabled.
|
||||
|
||||
This example activates the specified user's account and makes them a superuser:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiUsersUpdateUser} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiUsersUpdateUser} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiUsersUpdateUser} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiUsersResultUpdateUser} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## Reset password
|
||||
|
||||
Change the specified user's password to a new secure value.
|
||||
|
||||
Requires authentication as the target user.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiUsersResetPassword} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiUsersResetPassword} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiUsersResetPassword} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiUsersResultResetPassword} language="json" />
|
||||
|
||||
</details>
|
||||
|
||||
## Delete user
|
||||
|
||||
Remove a user account from the system.
|
||||
|
||||
Requires authentication as a superuser if the Langflow server has authentication enabled.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="Python" label="Python" default>
|
||||
|
||||
<CodeSnippet source={examplePythonApiUsersDeleteUser} language="python" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="JavaScript" label="JavaScript">
|
||||
|
||||
<CodeSnippet source={exampleJavascriptApiUsersDeleteUser} language="javascript" />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="curl">
|
||||
|
||||
<CodeSnippet source={exampleApiUsersDeleteUser} language="bash" />
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Result</summary>
|
||||
|
||||
<CodeSnippet source={resultApiUsersResultDeleteUser} language="json" />
|
||||
|
||||
</details>
|
||||
@ -0,0 +1,4 @@
|
||||
curl -X GET \
|
||||
"$LANGFLOW_URL/api/v1/build/123e4567-e89b-12d3-a456-426614174000/events" \
|
||||
-H "accept: application/json" \
|
||||
-H "x-api-key: $LANGFLOW_API_KEY"
|
||||
@ -0,0 +1,4 @@
|
||||
curl -X GET \
|
||||
"$LANGFLOW_URL/api/v1/build/123e4567-e89b-12d3-a456-426614174000/events?stream=false" \
|
||||
-H "accept: application/json" \
|
||||
-H "x-api-key: $LANGFLOW_API_KEY"
|
||||
@ -0,0 +1,10 @@
|
||||
curl -X POST \
|
||||
"$LANGFLOW_URL/api/v1/build/$FLOW_ID/flow" \
|
||||
-H "accept: application/json" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "x-api-key: $LANGFLOW_API_KEY" \
|
||||
-d '{
|
||||
"inputs": {
|
||||
"input_value": "Tell me a story"
|
||||
}
|
||||
}'
|
||||
@ -0,0 +1,15 @@
|
||||
curl -X POST \
|
||||
"$LANGFLOW_URL/api/v1/build/$FLOW_ID/flow" \
|
||||
-H "accept: application/json" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "x-api-key: $LANGFLOW_API_KEY" \
|
||||
-d '{
|
||||
"data": {
|
||||
"nodes": [],
|
||||
"edges": []
|
||||
},
|
||||
"inputs": {
|
||||
"input_value": "Your custom input here",
|
||||
"session": "session_id"
|
||||
}
|
||||
}'
|
||||
@ -0,0 +1,7 @@
|
||||
{"event": "vertices_sorted", "data": {"ids": ["ChatInput-XtBLx"], "to_run": ["Prompt-x74Ze", "ChatOutput-ylMzN", "ChatInput-XtBLx", "OpenAIModel-d1wOZ"]}}
|
||||
|
||||
{"event": "add_message", "data": {"timestamp": "2025-03-03T17:42:23", "sender": "User", "sender_name": "User", "session_id": "d2bbd92b-187e-4c84-b2d4-5df365704201", "text": "Tell me a story", "files": [], "error": false, "edit": false, "properties": {"text_color": "", "background_color": "", "edited": false, "source": {"id": null, "display_name": null, "source": null}, "icon": "", "allow_markdown": false, "positive_feedback": null, "state": "complete", "targets": []}, "category": "message", "content_blocks": [], "id": "28879bd8-6a68-4dd5-b658-74d643a4dd92", "flow_id": "d2bbd92b-187e-4c84-b2d4-5df365704201"}}
|
||||
|
||||
// ... Additional events as the flow executes ...
|
||||
|
||||
{"event": "end", "data": {}}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"job_id": "123e4567-e89b-12d3-a456-426614174000"
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
{ "job_id": "0bcc7f23-40b4-4bfa-9b8a-a44181fd1175" }
|
||||
@ -0,0 +1,6 @@
|
||||
curl -X POST \
|
||||
"$LANGFLOW_URL/api/v1/build/$FLOW_ID/flow" \
|
||||
-H "accept: application/json" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "x-api-key: $LANGFLOW_API_KEY" \
|
||||
-d '{"stop_component_id": "OpenAIModel-Uksag"}'
|
||||
@ -0,0 +1,4 @@
|
||||
curl -X DELETE \
|
||||
"$LANGFLOW_URL/api/v2/files" \
|
||||
-H "accept: application/json" \
|
||||
-H "x-api-key: $LANGFLOW_API_KEY"
|
||||
@ -0,0 +1,4 @@
|
||||
curl -X DELETE \
|
||||
"$LANGFLOW_URL/api/v1/files/delete/$FLOW_ID/2024-12-30_15-19-43_your_file.txt" \
|
||||
-H "accept: application/json" \
|
||||
-H "x-api-key: $LANGFLOW_API_KEY"
|
||||
@ -0,0 +1,4 @@
|
||||
curl -X DELETE \
|
||||
"$LANGFLOW_URL/api/v2/files/$FILE_ID" \
|
||||
-H "accept: application/json" \
|
||||
-H "x-api-key: $LANGFLOW_API_KEY"
|
||||
@ -0,0 +1,5 @@
|
||||
curl -X GET \
|
||||
"$LANGFLOW_URL/api/v1/files/download/$FLOW_ID/2024-12-30_15-19-43_your_file.txt" \
|
||||
-H "accept: application/json" \
|
||||
-H "x-api-key: $LANGFLOW_API_KEY" \
|
||||
--output downloaded_file.txt
|
||||
@ -0,0 +1,5 @@
|
||||
curl -X GET \
|
||||
"$LANGFLOW_URL/api/v2/files/c7b22c4c-d5e0-4ec9-af97-5d85b7657a34" \
|
||||
-H "accept: application/json" \
|
||||
-H "x-api-key: $LANGFLOW_API_KEY" \
|
||||
--output downloaded_file.txt
|
||||
@ -0,0 +1,4 @@
|
||||
curl -X PUT \
|
||||
"$LANGFLOW_URL/api/v2/files/$FILE_ID?name=new_file_name" \
|
||||
-H "accept: application/json" \
|
||||
-H "x-api-key: $LANGFLOW_API_KEY"
|
||||
@ -0,0 +1,4 @@
|
||||
curl -X GET \
|
||||
"$LANGFLOW_URL/api/v1/files/list/$FLOW_ID" \
|
||||
-H "accept: application/json" \
|
||||
-H "x-api-key: $LANGFLOW_API_KEY"
|
||||
@ -0,0 +1,4 @@
|
||||
curl -X GET \
|
||||
"$LANGFLOW_URL/api/v2/files" \
|
||||
-H "accept: application/json" \
|
||||
-H "x-api-key: $LANGFLOW_API_KEY"
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"message": "All files deleted successfully"
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"message": "File 2024-12-30_15-19-43_your_file.txt deleted successfully"
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"message": "File deleted successfully"
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
File contents downloaded to downloaded_file.txt
|
||||
@ -0,0 +1 @@
|
||||
File contents downloaded to downloaded_file.txt
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user