Merge branch 'feature/store' into feat/store/cris
This commit is contained in:
commit
adf849a6fb
6 changed files with 100 additions and 19 deletions
|
|
@ -72,6 +72,7 @@ def update_settings(
|
||||||
dev: bool = False,
|
dev: bool = False,
|
||||||
remove_api_keys: bool = False,
|
remove_api_keys: bool = False,
|
||||||
components_path: Optional[Path] = None,
|
components_path: Optional[Path] = None,
|
||||||
|
store: bool = True,
|
||||||
):
|
):
|
||||||
"""Update the settings from a config file."""
|
"""Update the settings from a config file."""
|
||||||
|
|
||||||
|
|
@ -90,6 +91,9 @@ def update_settings(
|
||||||
if components_path:
|
if components_path:
|
||||||
logger.debug(f"Adding component path {components_path}")
|
logger.debug(f"Adding component path {components_path}")
|
||||||
settings_service.settings.update_settings(COMPONENTS_PATH=components_path)
|
settings_service.settings.update_settings(COMPONENTS_PATH=components_path)
|
||||||
|
if not store:
|
||||||
|
logger.debug("Setting store to False")
|
||||||
|
settings_service.settings.update_settings(STORE=False)
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
|
|
@ -126,13 +130,6 @@ def run(
|
||||||
default=None,
|
default=None,
|
||||||
),
|
),
|
||||||
dev: bool = typer.Option(False, help="Run in development mode (may contain bugs)"),
|
dev: bool = typer.Option(False, help="Run in development mode (may contain bugs)"),
|
||||||
# This variable does not work but is set by the .env file
|
|
||||||
# and works with Pydantic
|
|
||||||
# database_url: str = typer.Option(
|
|
||||||
# None,
|
|
||||||
# help="Database URL to connect to. If not provided, a local SQLite database will be used.",
|
|
||||||
# envvar="LANGFLOW_DATABASE_URL",
|
|
||||||
# ),
|
|
||||||
path: str = typer.Option(
|
path: str = typer.Option(
|
||||||
None,
|
None,
|
||||||
help="Path to the frontend directory containing build files. This is for development purposes only.",
|
help="Path to the frontend directory containing build files. This is for development purposes only.",
|
||||||
|
|
@ -153,6 +150,11 @@ def run(
|
||||||
help="Run only the backend server without the frontend.",
|
help="Run only the backend server without the frontend.",
|
||||||
envvar="LANGFLOW_BACKEND_ONLY",
|
envvar="LANGFLOW_BACKEND_ONLY",
|
||||||
),
|
),
|
||||||
|
store: bool = typer.Option(
|
||||||
|
True,
|
||||||
|
help="Enables the store features.",
|
||||||
|
envvar="LANGFLOW_STORE",
|
||||||
|
),
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Run the Langflow.
|
Run the Langflow.
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from langflow.services.store.schema import (
|
||||||
DownloadComponentResponse,
|
DownloadComponentResponse,
|
||||||
ListComponentResponse,
|
ListComponentResponse,
|
||||||
StoreComponentCreate,
|
StoreComponentCreate,
|
||||||
|
TagResponse,
|
||||||
)
|
)
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
|
|
@ -44,6 +45,13 @@ def get_optional_user_store_api_key(
|
||||||
return decrypted
|
return decrypted
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/")
|
||||||
|
def check_if_store_is_enabled(
|
||||||
|
settings_service=Depends(get_settings_service),
|
||||||
|
):
|
||||||
|
return {"enabled": settings_service.settings.STORE}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/components/", response_model=ComponentResponse, status_code=201)
|
@router.post("/components/", response_model=ComponentResponse, status_code=201)
|
||||||
def create_component(
|
def create_component(
|
||||||
component: StoreComponentCreate,
|
component: StoreComponentCreate,
|
||||||
|
|
@ -65,15 +73,43 @@ def list_components(
|
||||||
store_api_Key: str = Depends(get_optional_user_store_api_key),
|
store_api_Key: str = Depends(get_optional_user_store_api_key),
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
fields = ["id", "name", "description", "user_created.name", "is_component"]
|
fields = [
|
||||||
result = store_service.list_components(
|
"id",
|
||||||
store_api_Key, page, limit, fields=fields, filter_by_user=filter_by_user
|
"name",
|
||||||
|
"description",
|
||||||
|
"user_created.name",
|
||||||
|
"is_component",
|
||||||
|
"tags.tags_id.name",
|
||||||
|
"tags.tags_id.id",
|
||||||
|
"count(liked_by)",
|
||||||
|
]
|
||||||
|
result = store_service.query_components(
|
||||||
|
store_api_Key,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
fields=fields,
|
||||||
|
filter_by_user=filter_by_user,
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise HTTPException(status_code=400, detail=str(exc))
|
raise HTTPException(status_code=400, detail=str(exc))
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/components/count", response_model=dict)
|
||||||
|
def count_components(
|
||||||
|
filter_by_user: bool = Query(False),
|
||||||
|
store_service: StoreService = Depends(get_store_service),
|
||||||
|
store_api_Key: str = Depends(get_optional_user_store_api_key),
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
result = store_service.count_components(
|
||||||
|
api_key=store_api_Key, filter_by_user=filter_by_user
|
||||||
|
)
|
||||||
|
return {"count": result}
|
||||||
|
except Exception as exc:
|
||||||
|
raise HTTPException(status_code=400, detail=str(exc))
|
||||||
|
|
||||||
|
|
||||||
@router.get("/components/{component_id}", response_model=DownloadComponentResponse)
|
@router.get("/components/{component_id}", response_model=DownloadComponentResponse)
|
||||||
def read_component(
|
def read_component(
|
||||||
component_id: UUID,
|
component_id: UUID,
|
||||||
|
|
@ -124,3 +160,14 @@ async def search_endpoint(
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise HTTPException(status_code=500, detail=str(exc))
|
raise HTTPException(status_code=500, detail=str(exc))
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/tags", response_model=List[TagResponse])
|
||||||
|
def get_tags(
|
||||||
|
store_service: StoreService = Depends(get_store_service),
|
||||||
|
store_api_Key: str = Depends(get_optional_user_store_api_key),
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
return store_service.get_tags(store_api_Key)
|
||||||
|
except Exception as exc:
|
||||||
|
raise HTTPException(status_code=500, detail=str(exc))
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ class Settings(BaseSettings):
|
||||||
LANGFUSE_PUBLIC_KEY: Optional[str] = None
|
LANGFUSE_PUBLIC_KEY: Optional[str] = None
|
||||||
LANGFUSE_HOST: Optional[str] = None
|
LANGFUSE_HOST: Optional[str] = None
|
||||||
|
|
||||||
|
STORE: Optional[bool] = True
|
||||||
STORE_URL: Optional[str] = None
|
STORE_URL: Optional[str] = None
|
||||||
DOWNLOAD_WEBHOOK_URL: Optional[str] = None
|
DOWNLOAD_WEBHOOK_URL: Optional[str] = None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,11 @@ from typing import Optional, List
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
|
|
||||||
|
class TagResponse(BaseModel):
|
||||||
|
id: UUID
|
||||||
|
name: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
class ComponentResponse(BaseModel):
|
class ComponentResponse(BaseModel):
|
||||||
id: UUID
|
id: UUID
|
||||||
status: Optional[str]
|
status: Optional[str]
|
||||||
|
|
@ -17,16 +22,15 @@ class ComponentResponse(BaseModel):
|
||||||
description: Optional[str]
|
description: Optional[str]
|
||||||
data: Optional[dict]
|
data: Optional[dict]
|
||||||
tags: Optional[List[int]]
|
tags: Optional[List[int]]
|
||||||
likes_count: Optional[List[UUID]]
|
liked_by_count: Optional[List[UUID]]
|
||||||
parent: Optional[UUID]
|
parent: Optional[UUID]
|
||||||
|
|
||||||
|
|
||||||
class ListComponentResponse(BaseModel):
|
class ListComponentResponse(BaseModel):
|
||||||
(["id", "name", "description", "count(likes)", "is_component"])
|
|
||||||
id: UUID
|
id: UUID
|
||||||
name: Optional[str]
|
name: Optional[str]
|
||||||
description: Optional[str]
|
description: Optional[str]
|
||||||
likes_count: Optional[int]
|
liked_by_count: Optional[int]
|
||||||
is_component: Optional[bool]
|
is_component: Optional[bool]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from datetime import datetime
|
||||||
import json
|
import json
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
from langflow.services.base import Service
|
from langflow.services.base import Service
|
||||||
from typing import TYPE_CHECKING, List, Dict, Any, Optional
|
from typing import TYPE_CHECKING, List, Dict, Any, Optional, Union
|
||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
from httpx import HTTPError
|
from httpx import HTTPError
|
||||||
|
|
@ -70,7 +70,7 @@ class StoreService(Service):
|
||||||
tags: Optional[List[str]] = None,
|
tags: Optional[List[str]] = None,
|
||||||
date_from: Optional[datetime] = None,
|
date_from: Optional[datetime] = None,
|
||||||
date_to: Optional[datetime] = None,
|
date_to: Optional[datetime] = None,
|
||||||
sort: Optional[List[str]] = ["-likes"],
|
sort: Optional[List[str]] = ["-count(liked_by)"],
|
||||||
fields: Optional[List[str]] = None,
|
fields: Optional[List[str]] = None,
|
||||||
filter_by_user: bool = False,
|
filter_by_user: bool = False,
|
||||||
) -> List[ComponentResponse]:
|
) -> List[ComponentResponse]:
|
||||||
|
|
@ -119,20 +119,41 @@ class StoreService(Service):
|
||||||
results = self._get(self.components_url, api_key, params)
|
results = self._get(self.components_url, api_key, params)
|
||||||
return [ComponentResponse(**component) for component in results]
|
return [ComponentResponse(**component) for component in results]
|
||||||
|
|
||||||
def list_components(
|
def count_components(
|
||||||
|
self,
|
||||||
|
api_key: Optional[str] = None,
|
||||||
|
filter_by_user: bool = False,
|
||||||
|
) -> int:
|
||||||
|
params = {"aggregate": json.dumps({"count": "*"})}
|
||||||
|
if filter_by_user:
|
||||||
|
params["deep"] = json.dumps(
|
||||||
|
{
|
||||||
|
"components": {
|
||||||
|
"_filter": {"user_created": {"token": {"_eq": api_key}}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
params["filter"] = json.dumps({"status": {"_in": ["public", "Public"]}})
|
||||||
|
results = self._get(self.components_url, api_key, params)
|
||||||
|
return results[0].get("count", 0)
|
||||||
|
|
||||||
|
def query_components(
|
||||||
self,
|
self,
|
||||||
api_key: str,
|
api_key: str,
|
||||||
page: int = 1,
|
page: int = 1,
|
||||||
limit: int = 15,
|
limit: int = 15,
|
||||||
fields: Optional[List[str]] = None,
|
fields: Optional[List[str]] = None,
|
||||||
filter_by_user: bool = False,
|
filter_by_user: bool = False,
|
||||||
) -> List[ListComponentResponse]:
|
) -> Union[List[ListComponentResponse], List[Dict[str, int]]]:
|
||||||
params = {"page": page, "limit": limit}
|
params = {"page": page, "limit": limit}
|
||||||
# ?aggregate[count]=likes
|
# ?aggregate[count]=likes
|
||||||
params["fields"] = (
|
params["fields"] = (
|
||||||
",".join(fields)
|
",".join(fields)
|
||||||
if fields
|
if fields
|
||||||
else ",".join(["id", "name", "description", "count(likes)", "is_component"])
|
else ",".join(
|
||||||
|
["id", "name", "description", "count(liked_by)", "is_component"]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
# Only public components or the ones created by the user
|
# Only public components or the ones created by the user
|
||||||
# check for "public" or "Public"
|
# check for "public" or "Public"
|
||||||
|
|
@ -189,3 +210,9 @@ class StoreService(Service):
|
||||||
except UnboundLocalError:
|
except UnboundLocalError:
|
||||||
pass
|
pass
|
||||||
raise ValueError(f"Upload failed: {exc}")
|
raise ValueError(f"Upload failed: {exc}")
|
||||||
|
|
||||||
|
def get_tags(self, api_key: str) -> List[Dict[str, Any]]:
|
||||||
|
url = f"{self.base_url}/items/tags"
|
||||||
|
params = {"fields": ",".join(["id", "name"])}
|
||||||
|
tags = self._get(url, api_key, params)
|
||||||
|
return tags
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ def test_search_components(mock_httpx: Mock, client):
|
||||||
"filter[name][_like]": "test",
|
"filter[name][_like]": "test",
|
||||||
"page": 1,
|
"page": 1,
|
||||||
"limit": 5,
|
"limit": 5,
|
||||||
"sort": "likes",
|
"sort": "count(liked_by)",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue