Rename the import package app to fluksio
A wheel whose top-level module is `app` collides with anything else in a user's venv, so the package that is about to be published takes the name it is published under. Only the Python package moves; the repo, the Docker WORKDIR and the compose project keep theirs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -49,5 +49,5 @@ jobs:
|
|||||||
# The hooks cover ruff and biome but not mypy, which `make lint-backend`
|
# The hooks cover ruff and biome but not mypy, which `make lint-backend`
|
||||||
# runs and which is strict for everything outside the flow prototypes.
|
# runs and which is strict for everything outside the flow prototypes.
|
||||||
- name: Typecheck backend
|
- name: Typecheck backend
|
||||||
run: uv run mypy app
|
run: uv run mypy fluksio
|
||||||
working-directory: backend
|
working-directory: backend
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ repos:
|
|||||||
exclude: |
|
exclude: |
|
||||||
(?x)^(
|
(?x)^(
|
||||||
frontend/src/client/.*|
|
frontend/src/client/.*|
|
||||||
backend/app/email-templates/build/.*
|
backend/fluksio/email-templates/build/.*
|
||||||
)$
|
)$
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
exclude: ^frontend/src/client/.*
|
exclude: ^frontend/src/client/.*
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ install: ## Install all dependencies (backend + frontend)
|
|||||||
cd frontend && bun install
|
cd frontend && bun install
|
||||||
|
|
||||||
dev-backend: ## Start the FastAPI backend with hot-reload (local)
|
dev-backend: ## Start the FastAPI backend with hot-reload (local)
|
||||||
cd backend && uv run fastapi dev app/main.py
|
cd backend && uv run fastapi dev fluksio/main.py
|
||||||
|
|
||||||
dev-frontend: ## Start the Vite dev server (local)
|
dev-frontend: ## Start the Vite dev server (local)
|
||||||
cd frontend && bun dev
|
cd frontend && bun dev
|
||||||
@@ -169,7 +169,7 @@ lint: lint-backend lint-frontend ## Run all linters
|
|||||||
lint-backend: ## Lint backend with ruff + mypy
|
lint-backend: ## Lint backend with ruff + mypy
|
||||||
cd backend && uv run ruff check .
|
cd backend && uv run ruff check .
|
||||||
cd backend && uv run ruff format --check .
|
cd backend && uv run ruff format --check .
|
||||||
cd backend && uv run mypy app
|
cd backend && uv run mypy fluksio
|
||||||
|
|
||||||
lint-frontend: ## Lint frontend with biome
|
lint-frontend: ## Lint frontend with biome
|
||||||
cd frontend && bun run lint
|
cd frontend && bun run lint
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ repo holds the FastAPI backend, the flow engine, and the dashboard SPA. It is se
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
backend/ FastAPI + SQLModel + Alembic + Postgres
|
backend/ FastAPI + SQLModel + Alembic + Postgres
|
||||||
app/flow/ the flow engine (nodes, pipeline, state backends, controller)
|
fluksio/flow/ the flow engine (nodes, pipeline, state backends, controller)
|
||||||
frontend/ React 19 + TanStack Router + Tailwind 4 + shadcn/ui
|
frontend/ React 19 + TanStack Router + Tailwind 4 + shadcn/ui
|
||||||
docker/ compose.yml → compose.dev.yml → compose.local.yml (+ compose.traefik.yml)
|
docker/ compose.yml → compose.dev.yml → compose.local.yml (+ compose.traefik.yml)
|
||||||
scripts/ generate-client.sh, test.sh
|
scripts/ generate-client.sh, test.sh
|
||||||
|
|||||||
+2
-2
@@ -44,14 +44,14 @@ Python, optimised for development speed. Owns the graph structure, persistence a
|
|||||||
external interfaces. See `docs/architecture/structure.canvas` → *Backend – Management*.
|
external interfaces. See `docs/architecture/structure.canvas` → *Backend – Management*.
|
||||||
|
|
||||||
- [x] FastAPI + SQLModel + Alembic + Postgres base with JWT auth and user management
|
- [x] FastAPI + SQLModel + Alembic + Postgres base with JWT auth and user management
|
||||||
- [x] Flow engine in `backend/app/flow/`: `Node` / `Pipeline` / `StateBackend`
|
- [x] Flow engine in `backend/fluksio/flow/`: `Node` / `Pipeline` / `StateBackend`
|
||||||
(memory + Redis) / `FlowController`
|
(memory + Redis) / `FlowController`
|
||||||
- [x] Node types: HTTP, MQTT, InfluxDB, Delay, MLP
|
- [x] Node types: HTTP, MQTT, InfluxDB, Delay, MLP
|
||||||
- [x] Flow-logic vocabulary as node types rather than repeated code: inject (manual,
|
- [x] Flow-logic vocabulary as node types rather than repeated code: inject (manual,
|
||||||
interval, cron or at startup), switch, change, filter-unchanged, join, trigger,
|
interval, cron or at startup), switch, change, filter-unchanged, join, trigger,
|
||||||
command, file and ntfy. Each is configured by filling in a form the editor
|
command, file and ntfy. Each is configured by filling in a form the editor
|
||||||
generates from its parameter schema
|
generates from its parameter schema
|
||||||
- [x] `app/flow` is an importable package with absolute `app.flow.*` imports
|
- [x] `fluksio/flow` is an importable package with absolute `fluksio.flow.*` imports
|
||||||
- [x] Typed, serializable node I/O: every port declares a `DType`, messages are
|
- [x] Typed, serializable node I/O: every port declares a `DType`, messages are
|
||||||
JSON on the wire and in Redis, no pickle anywhere. Binary codecs are still
|
JSON on the wire and in Redis, no pickle anywhere. Binary codecs are still
|
||||||
open — `DType.JSON` carries everything non-scalar for now
|
open — `DType.JSON` carries everything non-scalar for now
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Python
|
# Python
|
||||||
__pycache__
|
__pycache__
|
||||||
app.egg-info
|
fluksio.egg-info
|
||||||
*.pyc
|
*.pyc
|
||||||
.mypy_cache
|
.mypy_cache
|
||||||
.coverage
|
.coverage
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
*.png
|
*.png
|
||||||
__pycache__
|
__pycache__
|
||||||
app.egg-info
|
fluksio.egg-info
|
||||||
*.pyc
|
*.pyc
|
||||||
.mypy_cache
|
.mypy_cache
|
||||||
.coverage
|
.coverage
|
||||||
|
|||||||
+4
-4
@@ -25,20 +25,20 @@ ENV PATH="/app/.venv/bin:$PATH"
|
|||||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
--mount=type=bind,source=uv.lock,target=uv.lock \
|
--mount=type=bind,source=uv.lock,target=uv.lock \
|
||||||
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||||
uv sync --frozen --no-install-workspace --package app
|
uv sync --frozen --no-install-workspace --package fluksio
|
||||||
|
|
||||||
COPY ./backend/scripts /app/backend/scripts
|
COPY ./backend/scripts /app/backend/scripts
|
||||||
|
|
||||||
COPY ./backend/pyproject.toml ./backend/alembic.ini /app/backend/
|
COPY ./backend/pyproject.toml ./backend/alembic.ini /app/backend/
|
||||||
|
|
||||||
COPY ./backend/app /app/backend/app
|
COPY ./backend/fluksio /app/backend/fluksio
|
||||||
|
|
||||||
# Sync the project
|
# Sync the project
|
||||||
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#intermediate-layers
|
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#intermediate-layers
|
||||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
--mount=type=bind,source=uv.lock,target=uv.lock \
|
--mount=type=bind,source=uv.lock,target=uv.lock \
|
||||||
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||||
uv sync --frozen --package app
|
uv sync --frozen --package fluksio
|
||||||
|
|
||||||
# Connectors are ordinary installed packages found through the
|
# Connectors are ordinary installed packages found through the
|
||||||
# `fluksio.node_types` entry point. `make connectors` builds them into here;
|
# `fluksio.node_types` entry point. `make connectors` builds them into here;
|
||||||
@@ -54,4 +54,4 @@ WORKDIR /app/backend/
|
|||||||
# Single worker on purpose: the process hosts the flow engine, and a second
|
# Single worker on purpose: the process hosts the flow engine, and a second
|
||||||
# worker would be a second engine — duplicated subscriptions, cron ticks and
|
# worker would be a second engine — duplicated subscriptions, cron ticks and
|
||||||
# webhooks. Scaling out is the M5 worker split, not more uvicorn processes.
|
# webhooks. Scaling out is the M5 worker split, not more uvicorn processes.
|
||||||
CMD ["fastapi", "run", "app/main.py"]
|
CMD ["fastapi", "run", "fluksio/main.py"]
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[alembic]
|
[alembic]
|
||||||
# path to migration scripts
|
# path to migration scripts
|
||||||
script_location = app/alembic
|
script_location = fluksio/alembic
|
||||||
|
|
||||||
# template used to generate migration files
|
# template used to generate migration files
|
||||||
# file_template = %%(rev)s_%%(slug)s
|
# file_template = %%(rev)s_%%(slug)s
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
"""The flow engine: nodes, the pipeline that runs them, and their storage."""
|
|
||||||
|
|
||||||
from app.flow.controller import FlowController, NodeStatus
|
|
||||||
from app.flow.events import EventBus, event_bus
|
|
||||||
from app.flow.messages import DType, MessageSpec, qualify
|
|
||||||
from app.flow.nodes import Node
|
|
||||||
from app.flow.pipeline import Pipeline, ValidationIssue
|
|
||||||
from app.flow.state import MemoryState, RedisState, StateBackend
|
|
||||||
from app.flow.store import FlowStore
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"DType",
|
|
||||||
"EventBus",
|
|
||||||
"FlowController",
|
|
||||||
"FlowStore",
|
|
||||||
"MemoryState",
|
|
||||||
"MessageSpec",
|
|
||||||
"Node",
|
|
||||||
"NodeStatus",
|
|
||||||
"Pipeline",
|
|
||||||
"RedisState",
|
|
||||||
"StateBackend",
|
|
||||||
"ValidationIssue",
|
|
||||||
"event_bus",
|
|
||||||
"qualify",
|
|
||||||
]
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
"""Built-in node types.
|
|
||||||
|
|
||||||
Split by the outside world each one talks to. Importing from
|
|
||||||
``app.flow.nodes`` keeps working, which is what every caller does.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from app.flow.nodes.base import RESERVED_SETTINGS, Node
|
|
||||||
from app.flow.nodes.delay import DelayNode
|
|
||||||
from app.flow.nodes.exec import ExecNode
|
|
||||||
from app.flow.nodes.file import FileNode
|
|
||||||
from app.flow.nodes.http import HttpNode
|
|
||||||
from app.flow.nodes.influx import InfluxDbNode
|
|
||||||
from app.flow.nodes.inject import InjectNode
|
|
||||||
from app.flow.nodes.logic import ChangeNode, JoinNode, RbeNode, SwitchNode
|
|
||||||
from app.flow.nodes.mlp import MLPNode
|
|
||||||
from app.flow.nodes.mqtt import MqttNode
|
|
||||||
from app.flow.nodes.ntfy import NtfyNode
|
|
||||||
from app.flow.nodes.trigger import TriggerNode
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"ChangeNode",
|
|
||||||
"DelayNode",
|
|
||||||
"ExecNode",
|
|
||||||
"FileNode",
|
|
||||||
"HttpNode",
|
|
||||||
"InfluxDbNode",
|
|
||||||
"InjectNode",
|
|
||||||
"JoinNode",
|
|
||||||
"MLPNode",
|
|
||||||
"MqttNode",
|
|
||||||
"Node",
|
|
||||||
"NtfyNode",
|
|
||||||
"RESERVED_SETTINGS",
|
|
||||||
"RbeNode",
|
|
||||||
"SwitchNode",
|
|
||||||
"TriggerNode",
|
|
||||||
]
|
|
||||||
@@ -18,8 +18,8 @@ fileConfig(config.config_file_name)
|
|||||||
# target_metadata = mymodel.Base.metadata
|
# target_metadata = mymodel.Base.metadata
|
||||||
# target_metadata = None
|
# target_metadata = None
|
||||||
|
|
||||||
from app.models import SQLModel # noqa
|
from fluksio.models import SQLModel # noqa
|
||||||
from app.core.config import settings # noqa
|
from fluksio.core.config import settings # noqa
|
||||||
|
|
||||||
target_metadata = SQLModel.metadata
|
target_metadata = SQLModel.metadata
|
||||||
|
|
||||||
@@ -8,15 +8,15 @@ from jwt.exceptions import InvalidTokenError
|
|||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
|
|
||||||
from app.cloud import config as cloud_config
|
from fluksio.cloud import config as cloud_config
|
||||||
from app.core import security
|
from fluksio.core import security
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
from app.core.db import engine
|
from fluksio.core.db import engine
|
||||||
from app.flow import panels
|
from fluksio.flow import panels
|
||||||
from app.flow.controller import FlowController
|
from fluksio.flow.controller import FlowController
|
||||||
from app.flow.dashboards import DashboardStore
|
from fluksio.flow.dashboards import DashboardStore
|
||||||
from app.flow.workers import PythonWorkerPool
|
from fluksio.flow.workers import PythonWorkerPool
|
||||||
from app.models import TokenPayload, User
|
from fluksio.models import TokenPayload, User
|
||||||
|
|
||||||
reusable_oauth2 = OAuth2PasswordBearer(
|
reusable_oauth2 = OAuth2PasswordBearer(
|
||||||
tokenUrl=f"{settings.API_V1_STR}/login/access-token"
|
tokenUrl=f"{settings.API_V1_STR}/login/access-token"
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from app.api.routes import (
|
from fluksio.api.routes import (
|
||||||
alerts,
|
alerts,
|
||||||
artifacts,
|
artifacts,
|
||||||
cloud,
|
cloud,
|
||||||
@@ -6,10 +6,10 @@ from typing import Any
|
|||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
from fastapi.concurrency import run_in_threadpool
|
from fastapi.concurrency import run_in_threadpool
|
||||||
|
|
||||||
from app.api.deps import FlowControllerDep, get_current_user
|
from fluksio.api.deps import FlowControllerDep, get_current_user
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
from app.flow.alerts import Alert, AlertsConfig
|
from fluksio.flow.alerts import Alert, AlertsConfig
|
||||||
from app.models import Message
|
from fluksio.models import Message
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/alerts", tags=["alerts"], dependencies=[Depends(get_current_user)]
|
prefix="/alerts", tags=["alerts"], dependencies=[Depends(get_current_user)]
|
||||||
@@ -13,10 +13,10 @@ from jwt.exceptions import InvalidTokenError
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from sqlmodel import Session
|
from sqlmodel import Session
|
||||||
|
|
||||||
from app.api.deps import user_from_token
|
from fluksio.api.deps import user_from_token
|
||||||
from app.core import security
|
from fluksio.core import security
|
||||||
from app.core.db import engine
|
from fluksio.core.db import engine
|
||||||
from app.flow.artifacts import ArtifactStore
|
from fluksio.flow.artifacts import ArtifactStore
|
||||||
|
|
||||||
|
|
||||||
def artifact_caller(request: Request) -> str:
|
def artifact_caller(request: Request) -> str:
|
||||||
@@ -23,16 +23,16 @@ from fastapi import APIRouter, Depends, HTTPException, Request
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from sqlmodel import select
|
from sqlmodel import select
|
||||||
|
|
||||||
from app import crud
|
from fluksio import crud
|
||||||
from app.api.deps import (
|
from fluksio.api.deps import (
|
||||||
CurrentUser,
|
CurrentUser,
|
||||||
SessionDep,
|
SessionDep,
|
||||||
get_current_active_superuser,
|
get_current_active_superuser,
|
||||||
get_current_user,
|
get_current_user,
|
||||||
)
|
)
|
||||||
from app.cloud import config as cloud_config
|
from fluksio.cloud import config as cloud_config
|
||||||
from app.core.security import get_password_hash
|
from fluksio.core.security import get_password_hash
|
||||||
from app.models import Message, User, UserPublic
|
from fluksio.models import Message, User, UserPublic
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -279,7 +279,7 @@ def disconnect(request: Request) -> Message:
|
|||||||
|
|
||||||
|
|
||||||
def _start_connector(app: Any) -> None:
|
def _start_connector(app: Any) -> None:
|
||||||
from app.cloud.connector import CloudConnector
|
from fluksio.cloud.connector import CloudConnector
|
||||||
|
|
||||||
existing = getattr(app.state, "cloud_task", None)
|
existing = getattr(app.state, "cloud_task", None)
|
||||||
if existing is not None:
|
if existing is not None:
|
||||||
@@ -7,17 +7,17 @@ from fastapi import APIRouter, Depends, HTTPException
|
|||||||
from fastapi.concurrency import run_in_threadpool
|
from fastapi.concurrency import run_in_threadpool
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from app.api.deps import DashboardStoreDep, FlowControllerDep, get_current_user
|
from fluksio.api.deps import DashboardStoreDep, FlowControllerDep, get_current_user
|
||||||
from app.flow.dashboards import (
|
from fluksio.flow.dashboards import (
|
||||||
DashboardDef,
|
DashboardDef,
|
||||||
DashboardExists,
|
DashboardExists,
|
||||||
DashboardNotFound,
|
DashboardNotFound,
|
||||||
DashboardsPublic,
|
DashboardsPublic,
|
||||||
default_dashboard,
|
default_dashboard,
|
||||||
)
|
)
|
||||||
from app.flow.events import event_bus
|
from fluksio.flow.events import event_bus
|
||||||
from app.flow.store import StaleVersion
|
from fluksio.flow.store import StaleVersion
|
||||||
from app.models import Message
|
from fluksio.models import Message
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/dashboards", tags=["dashboards"], dependencies=[Depends(get_current_user)]
|
prefix="/dashboards", tags=["dashboards"], dependencies=[Depends(get_current_user)]
|
||||||
@@ -18,7 +18,7 @@ from pydantic import BaseModel
|
|||||||
from sqlalchemy import delete
|
from sqlalchemy import delete
|
||||||
from sqlmodel import Session, col, select
|
from sqlmodel import Session, col, select
|
||||||
|
|
||||||
from app.api.deps import (
|
from fluksio.api.deps import (
|
||||||
CurrentUser,
|
CurrentUser,
|
||||||
FlowControllerDep,
|
FlowControllerDep,
|
||||||
SessionDep,
|
SessionDep,
|
||||||
@@ -26,15 +26,15 @@ from app.api.deps import (
|
|||||||
get_current_user,
|
get_current_user,
|
||||||
user_from_token,
|
user_from_token,
|
||||||
)
|
)
|
||||||
from app.core.db import engine
|
from fluksio.core.db import engine
|
||||||
from app.flow.controller import FlowController
|
from fluksio.flow.controller import FlowController
|
||||||
from app.flow.dashboards import DashboardStore
|
from fluksio.flow.dashboards import DashboardStore
|
||||||
from app.flow.events import event_bus
|
from fluksio.flow.events import event_bus
|
||||||
from app.flow.messages import qualify
|
from fluksio.flow.messages import qualify
|
||||||
from app.flow.panels import messages_for
|
from fluksio.flow.panels import messages_for
|
||||||
from app.flow.pipeline import ValidationIssue
|
from fluksio.flow.pipeline import ValidationIssue
|
||||||
from app.flow.runs import RunRejected
|
from fluksio.flow.runs import RunRejected
|
||||||
from app.flow.schemas import (
|
from fluksio.flow.schemas import (
|
||||||
NAME_PATTERN,
|
NAME_PATTERN,
|
||||||
BrainGraph,
|
BrainGraph,
|
||||||
FlowDef,
|
FlowDef,
|
||||||
@@ -49,15 +49,15 @@ from app.flow.schemas import (
|
|||||||
NodeStatusPublic,
|
NodeStatusPublic,
|
||||||
NodeTypeInfo,
|
NodeTypeInfo,
|
||||||
)
|
)
|
||||||
from app.flow.state import as_number
|
from fluksio.flow.state import as_number
|
||||||
from app.flow.store import (
|
from fluksio.flow.store import (
|
||||||
FlowExists,
|
FlowExists,
|
||||||
FlowNotFound,
|
FlowNotFound,
|
||||||
LibExists,
|
LibExists,
|
||||||
LibNotFound,
|
LibNotFound,
|
||||||
StaleVersion,
|
StaleVersion,
|
||||||
)
|
)
|
||||||
from app.models import Message, Run, RunArtifact, RunMetric, RunNode
|
from fluksio.models import Message, Run, RunArtifact, RunMetric, RunNode
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/flows", tags=["flows"], dependencies=[Depends(get_current_user)]
|
prefix="/flows", tags=["flows"], dependencies=[Depends(get_current_user)]
|
||||||
@@ -287,7 +287,7 @@ def read_flows(controller: FlowControllerDep) -> Any:
|
|||||||
@router.get("/node-types", response_model=list[NodeTypeInfo])
|
@router.get("/node-types", response_model=list[NodeTypeInfo])
|
||||||
def read_node_types() -> Any:
|
def read_node_types() -> Any:
|
||||||
"""The node types that can be placed on a canvas."""
|
"""The node types that can be placed on a canvas."""
|
||||||
from app.flow.controller import node_type_info
|
from fluksio.flow.controller import node_type_info
|
||||||
|
|
||||||
return node_type_info()
|
return node_type_info()
|
||||||
|
|
||||||
@@ -5,12 +5,12 @@ from fastapi import APIRouter, Depends, HTTPException
|
|||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from fastapi.security import OAuth2PasswordRequestForm
|
from fastapi.security import OAuth2PasswordRequestForm
|
||||||
|
|
||||||
from app import crud
|
from fluksio import crud
|
||||||
from app.api.deps import CurrentUser, SessionDep, get_current_active_superuser
|
from fluksio.api.deps import CurrentUser, SessionDep, get_current_active_superuser
|
||||||
from app.core import security
|
from fluksio.core import security
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
from app.models import Message, NewPassword, Token, UserPublic, UserUpdate
|
from fluksio.models import Message, NewPassword, Token, UserPublic, UserUpdate
|
||||||
from app.utils import (
|
from fluksio.utils import (
|
||||||
generate_password_reset_token,
|
generate_password_reset_token,
|
||||||
generate_reset_password_email,
|
generate_reset_password_email,
|
||||||
send_email,
|
send_email,
|
||||||
@@ -12,10 +12,10 @@ from fastapi import APIRouter, Depends, HTTPException
|
|||||||
from fastapi.concurrency import run_in_threadpool
|
from fastapi.concurrency import run_in_threadpool
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from app.api.deps import FlowControllerDep, get_current_user
|
from fluksio.api.deps import FlowControllerDep, get_current_user
|
||||||
from app.flow.messages import flow_of
|
from fluksio.flow.messages import flow_of
|
||||||
from app.flow.pipeline import ValueSource
|
from fluksio.flow.pipeline import ValueSource
|
||||||
from app.flow.state import as_number
|
from fluksio.flow.state import as_number
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/messages", tags=["messages"], dependencies=[Depends(get_current_user)]
|
prefix="/messages", tags=["messages"], dependencies=[Depends(get_current_user)]
|
||||||
@@ -12,15 +12,15 @@ from typing import Any
|
|||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
from fastapi.concurrency import run_in_threadpool
|
from fastapi.concurrency import run_in_threadpool
|
||||||
|
|
||||||
from app.api.deps import (
|
from fluksio.api.deps import (
|
||||||
CurrentUser,
|
CurrentUser,
|
||||||
FlowControllerDep,
|
FlowControllerDep,
|
||||||
WorkerPoolDep,
|
WorkerPoolDep,
|
||||||
get_current_user,
|
get_current_user,
|
||||||
)
|
)
|
||||||
from app.flow import modules
|
from fluksio.flow import modules
|
||||||
from app.flow.events import event_bus
|
from fluksio.flow.events import event_bus
|
||||||
from app.flow.schemas import ApplyRequest, ApplyResult, ModulesInfo
|
from fluksio.flow.schemas import ApplyRequest, ApplyResult, ModulesInfo
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/modules", tags=["modules"], dependencies=[Depends(get_current_user)]
|
prefix="/modules", tags=["modules"], dependencies=[Depends(get_current_user)]
|
||||||
@@ -36,15 +36,15 @@ from fastapi.responses import JSONResponse
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from sqlmodel import col, select
|
from sqlmodel import col, select
|
||||||
|
|
||||||
from app.api.deps import (
|
from fluksio.api.deps import (
|
||||||
CurrentUser,
|
CurrentUser,
|
||||||
SessionDep,
|
SessionDep,
|
||||||
get_current_active_superuser,
|
get_current_active_superuser,
|
||||||
get_current_user,
|
get_current_user,
|
||||||
)
|
)
|
||||||
from app.core import security
|
from fluksio.core import security
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
from app.models import (
|
from fluksio.models import (
|
||||||
Message,
|
Message,
|
||||||
OAuthAuthorizationCode,
|
OAuthAuthorizationCode,
|
||||||
OAuthAuthorizeInfo,
|
OAuthAuthorizeInfo,
|
||||||
+4
-4
@@ -17,10 +17,10 @@ from sqlalchemy import ColumnElement, DateTime, Interval, cast, func, literal
|
|||||||
from sqlalchemy import select as sa_select
|
from sqlalchemy import select as sa_select
|
||||||
from sqlmodel import col, select
|
from sqlmodel import col, select
|
||||||
|
|
||||||
from app.api.deps import FlowControllerDep, SessionDep, get_current_user
|
from fluksio.api.deps import FlowControllerDep, SessionDep, get_current_user
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
from app.flow.controller import ADVISORY_ISSUES, NodeStatus
|
from fluksio.flow.controller import ADVISORY_ISSUES, NodeStatus
|
||||||
from app.models import EngineEvent, FlowRun, MetricBucket
|
from fluksio.models import EngineEvent, FlowRun, MetricBucket
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/observability",
|
prefix="/observability",
|
||||||
@@ -11,7 +11,7 @@ without a session — a device with no credential is the whole point of them —
|
|||||||
and mints the credential itself when the approval comes. Which side minted it
|
and mints the credential itself when the approval comes. Which side minted it
|
||||||
changes nothing about what it may do: the scope check is here either way.
|
changes nothing about what it may do: the scope check is here either way.
|
||||||
|
|
||||||
The credential is scoped: ``app.api.deps`` lets it reach the dashboards that
|
The credential is scoped: ``fluksio.api.deps`` lets it reach the dashboards that
|
||||||
panel was assigned and nothing else. Removing the panel revokes it.
|
panel was assigned and nothing else. Removing the panel revokes it.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -27,13 +27,13 @@ from fastapi import APIRouter, Depends, HTTPException, Request
|
|||||||
from fastapi.concurrency import run_in_threadpool
|
from fastapi.concurrency import run_in_threadpool
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from app.api.deps import CurrentUser, get_current_active_superuser, get_current_user
|
from fluksio.api.deps import CurrentUser, get_current_active_superuser, get_current_user
|
||||||
from app.cloud import config as cloud_config
|
from fluksio.cloud import config as cloud_config
|
||||||
from app.core import security
|
from fluksio.core import security
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
from app.flow.events import event_bus
|
from fluksio.flow.events import event_bus
|
||||||
from app.flow.panels import PanelDef, PanelsConfig, find, read_config, write_config
|
from fluksio.flow.panels import PanelDef, PanelsConfig, find, read_config, write_config
|
||||||
from app.models import Message
|
from fluksio.models import Message
|
||||||
|
|
||||||
#: Gated per route rather than on the router: the two pairing endpoints are the
|
#: Gated per route rather than on the router: the two pairing endpoints are the
|
||||||
#: only unauthenticated ones in the app, because a device with no credential is
|
#: only unauthenticated ones in the app, because a device with no credential is
|
||||||
@@ -3,10 +3,10 @@ from typing import Any
|
|||||||
from fastapi import APIRouter, HTTPException
|
from fastapi import APIRouter, HTTPException
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from app.api.deps import SessionDep
|
from fluksio.api.deps import SessionDep
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
from app.core.security import get_password_hash
|
from fluksio.core.security import get_password_hash
|
||||||
from app.models import (
|
from fluksio.models import (
|
||||||
User,
|
User,
|
||||||
UserPublic,
|
UserPublic,
|
||||||
)
|
)
|
||||||
@@ -12,10 +12,10 @@ from fastapi.concurrency import run_in_threadpool
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from sqlmodel import col, select
|
from sqlmodel import col, select
|
||||||
|
|
||||||
from app.api.deps import CurrentUser, SessionDep, get_current_user
|
from fluksio.api.deps import CurrentUser, SessionDep, get_current_user
|
||||||
from app.flow.runs import RunRejected, RunService, new_run_id
|
from fluksio.flow.runs import RunRejected, RunService, new_run_id
|
||||||
from app.flow.store import FlowNotFound
|
from fluksio.flow.store import FlowNotFound
|
||||||
from app.models import Run, RunArtifact, RunMetric, RunNode
|
from fluksio.models import Run, RunArtifact, RunMetric, RunNode
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/runs", tags=["runs"], dependencies=[Depends(get_current_user)]
|
prefix="/runs", tags=["runs"], dependencies=[Depends(get_current_user)]
|
||||||
@@ -9,9 +9,9 @@ from fastapi import APIRouter, Depends, HTTPException
|
|||||||
from fastapi.concurrency import run_in_threadpool
|
from fastapi.concurrency import run_in_threadpool
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from app.api.deps import get_current_user
|
from fluksio.api.deps import get_current_user
|
||||||
from app.flow.secrets import SecretNotFound, get_secrets
|
from fluksio.flow.secrets import SecretNotFound, get_secrets
|
||||||
from app.models import Message
|
from fluksio.models import Message
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/secrets", tags=["secrets"], dependencies=[Depends(get_current_user)]
|
prefix="/secrets", tags=["secrets"], dependencies=[Depends(get_current_user)]
|
||||||
@@ -4,16 +4,16 @@ from typing import Any
|
|||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
from sqlmodel import col, func, select
|
from sqlmodel import col, func, select
|
||||||
|
|
||||||
from app import crud
|
from fluksio import crud
|
||||||
from app.api.deps import (
|
from fluksio.api.deps import (
|
||||||
CurrentUser,
|
CurrentUser,
|
||||||
SessionDep,
|
SessionDep,
|
||||||
get_current_active_superuser,
|
get_current_active_superuser,
|
||||||
)
|
)
|
||||||
from app.api.routes.cloud import forget_remote_user
|
from fluksio.api.routes.cloud import forget_remote_user
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
from app.core.security import get_password_hash, verify_password
|
from fluksio.core.security import get_password_hash, verify_password
|
||||||
from app.models import (
|
from fluksio.models import (
|
||||||
Message,
|
Message,
|
||||||
UpdatePassword,
|
UpdatePassword,
|
||||||
User,
|
User,
|
||||||
@@ -24,7 +24,7 @@ from app.models import (
|
|||||||
UserUpdate,
|
UserUpdate,
|
||||||
UserUpdateMe,
|
UserUpdateMe,
|
||||||
)
|
)
|
||||||
from app.utils import generate_new_account_email, send_email
|
from fluksio.utils import generate_new_account_email, send_email
|
||||||
|
|
||||||
router = APIRouter(prefix="/users", tags=["users"])
|
router = APIRouter(prefix="/users", tags=["users"])
|
||||||
|
|
||||||
@@ -5,10 +5,10 @@ from fastapi import APIRouter, Depends, Request, Response
|
|||||||
from fastapi.concurrency import run_in_threadpool
|
from fastapi.concurrency import run_in_threadpool
|
||||||
from pydantic.networks import EmailStr
|
from pydantic.networks import EmailStr
|
||||||
|
|
||||||
from app.api.deps import get_current_active_superuser
|
from fluksio.api.deps import get_current_active_superuser
|
||||||
from app.flow.state import RedisState
|
from fluksio.flow.state import RedisState
|
||||||
from app.models import Message
|
from fluksio.models import Message
|
||||||
from app.utils import generate_test_email, send_email
|
from fluksio.utils import generate_test_email, send_email
|
||||||
|
|
||||||
router = APIRouter(prefix="/utils", tags=["utils"])
|
router = APIRouter(prefix="/utils", tags=["utils"])
|
||||||
|
|
||||||
@@ -18,10 +18,10 @@ from fastapi.responses import PlainTextResponse
|
|||||||
from jwt.exceptions import InvalidTokenError
|
from jwt.exceptions import InvalidTokenError
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from app.api.deps import get_current_active_superuser, get_current_user
|
from fluksio.api.deps import get_current_active_superuser, get_current_user
|
||||||
from app.core import security
|
from fluksio.core import security
|
||||||
from app.flow import worker_main
|
from fluksio.flow import worker_main
|
||||||
from app.flow.remote import PROTOCOL, RemoteWorker, RemoteWorkerHub
|
from fluksio.flow.remote import PROTOCOL, RemoteWorker, RemoteWorkerHub
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ from sqlalchemy import Engine
|
|||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from tenacity import after_log, before_log, retry, stop_after_attempt, wait_fixed
|
from tenacity import after_log, before_log, retry, stop_after_attempt, wait_fixed
|
||||||
|
|
||||||
from app.core.db import engine
|
from fluksio.core.db import engine
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -24,7 +24,7 @@ from typing import Any
|
|||||||
import jwt
|
import jwt
|
||||||
from jwt.exceptions import InvalidTokenError
|
from jwt.exceptions import InvalidTokenError
|
||||||
|
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -26,8 +26,8 @@ from typing import Any
|
|||||||
import httpx
|
import httpx
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
|
||||||
from app.cloud import config as cloud_config
|
from fluksio.cloud import config as cloud_config
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -158,8 +158,8 @@ class CloudConnector:
|
|||||||
"""
|
"""
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
|
|
||||||
from app.core.db import engine
|
from fluksio.core.db import engine
|
||||||
from app.models import User
|
from fluksio.models import User
|
||||||
|
|
||||||
if not owner:
|
if not owner:
|
||||||
return
|
return
|
||||||
@@ -208,7 +208,7 @@ class CloudConnector:
|
|||||||
a short-lived local token: the observability API is authenticated, and
|
a short-lived local token: the observability API is authenticated, and
|
||||||
this process is entitled to mint one for the enrolling user.
|
this process is entitled to mint one for the enrolling user.
|
||||||
"""
|
"""
|
||||||
from app.core import security
|
from fluksio.core import security
|
||||||
|
|
||||||
token = security.create_access_token(config.local_user_id, timedelta(minutes=5))
|
token = security.create_access_token(config.local_user_id, timedelta(minutes=5))
|
||||||
try:
|
try:
|
||||||
@@ -339,14 +339,14 @@ class CloudConnector:
|
|||||||
"""
|
"""
|
||||||
from sqlmodel import Session
|
from sqlmodel import Session
|
||||||
|
|
||||||
from app.api.deps import user_from_token
|
from fluksio.api.deps import user_from_token
|
||||||
from app.api.routes.flows import (
|
from fluksio.api.routes.flows import (
|
||||||
event_for_panel,
|
event_for_panel,
|
||||||
panel_scope,
|
panel_scope,
|
||||||
snapshot_payload,
|
snapshot_payload,
|
||||||
)
|
)
|
||||||
from app.core.db import engine
|
from fluksio.core.db import engine
|
||||||
from app.flow.events import event_bus
|
from fluksio.flow.events import event_bus
|
||||||
|
|
||||||
stream_id = str(frame["id"])
|
stream_id = str(frame["id"])
|
||||||
path = str(frame.get("path") or "")
|
path = str(frame.get("path") or "")
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
from sqlmodel import Session, create_engine, select
|
from sqlmodel import Session, create_engine, select
|
||||||
|
|
||||||
from app import crud
|
from fluksio import crud
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
from app.models import User, UserCreate
|
from fluksio.models import User, UserCreate
|
||||||
|
|
||||||
# A connection idle across a Postgres restart is dead but still pooled; the
|
# A connection idle across a Postgres restart is dead but still pooled; the
|
||||||
# pre-ping spends a round trip to find out instead of failing the request.
|
# pre-ping spends a round trip to find out instead of failing the request.
|
||||||
@@ -11,7 +11,7 @@ from pwdlib import PasswordHash
|
|||||||
from pwdlib.hashers.argon2 import Argon2Hasher
|
from pwdlib.hashers.argon2 import Argon2Hasher
|
||||||
from pwdlib.hashers.bcrypt import BcryptHasher
|
from pwdlib.hashers.bcrypt import BcryptHasher
|
||||||
|
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
|
|
||||||
password_hash = PasswordHash(
|
password_hash = PasswordHash(
|
||||||
(
|
(
|
||||||
@@ -182,7 +182,7 @@ def create_panel_token(
|
|||||||
person in exactly the same way: ``sub`` is the account that approved the
|
person in exactly the same way: ``sub`` is the account that approved the
|
||||||
pairing, so everything the panel does is attributable to them. What keeps
|
pairing, so everything the panel does is attributable to them. What keeps
|
||||||
it from being a full session is the ``panel`` claim — the request filter in
|
it from being a full session is the ``panel`` claim — the request filter in
|
||||||
``app.api.deps`` lets it reach only that panel's dashboards and the message
|
``fluksio.api.deps`` lets it reach only that panel's dashboards and the message
|
||||||
endpoints its widgets need.
|
endpoints its widgets need.
|
||||||
|
|
||||||
Long-lived on purpose: a wall tablet is set up once and left running, and
|
Long-lived on purpose: a wall tablet is set up once and left running, and
|
||||||
@@ -2,8 +2,8 @@ from typing import Any
|
|||||||
|
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
|
|
||||||
from app.core.security import get_password_hash, verify_password
|
from fluksio.core.security import get_password_hash, verify_password
|
||||||
from app.models import User, UserCreate, UserUpdate
|
from fluksio.models import User, UserCreate, UserUpdate
|
||||||
|
|
||||||
|
|
||||||
def create_user(*, session: Session, user_create: UserCreate) -> User:
|
def create_user(*, session: Session, user_create: UserCreate) -> User:
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
"""The flow engine: nodes, the pipeline that runs them, and their storage."""
|
||||||
|
|
||||||
|
from fluksio.flow.controller import FlowController, NodeStatus
|
||||||
|
from fluksio.flow.events import EventBus, event_bus
|
||||||
|
from fluksio.flow.messages import DType, MessageSpec, qualify
|
||||||
|
from fluksio.flow.nodes import Node
|
||||||
|
from fluksio.flow.pipeline import Pipeline, ValidationIssue
|
||||||
|
from fluksio.flow.state import MemoryState, RedisState, StateBackend
|
||||||
|
from fluksio.flow.store import FlowStore
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"DType",
|
||||||
|
"EventBus",
|
||||||
|
"FlowController",
|
||||||
|
"FlowStore",
|
||||||
|
"MemoryState",
|
||||||
|
"MessageSpec",
|
||||||
|
"Node",
|
||||||
|
"NodeStatus",
|
||||||
|
"Pipeline",
|
||||||
|
"RedisState",
|
||||||
|
"StateBackend",
|
||||||
|
"ValidationIssue",
|
||||||
|
"event_bus",
|
||||||
|
"qualify",
|
||||||
|
]
|
||||||
@@ -22,8 +22,8 @@ from typing import Any, Literal
|
|||||||
import httpx
|
import httpx
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from app.flow.events import EventBus
|
from fluksio.flow.events import EventBus
|
||||||
from app.flow.secrets import resolve_params
|
from fluksio.flow.secrets import resolve_params
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -351,8 +351,8 @@ class AlertManager:
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
|
||||||
async def _send_email(self, config: dict[str, Any], alert: Alert) -> None:
|
async def _send_email(self, config: dict[str, Any], alert: Alert) -> None:
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
from app.utils import send_email
|
from fluksio.utils import send_email
|
||||||
|
|
||||||
recipient = config.get("to")
|
recipient = config.get("to")
|
||||||
if not recipient:
|
if not recipient:
|
||||||
@@ -32,7 +32,7 @@ from typing import TYPE_CHECKING, Any, ClassVar
|
|||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from app.flow.nodes import Node
|
from fluksio.flow.nodes import Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
@@ -21,12 +21,12 @@ from typing import Any, cast
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.concurrency import run_in_threadpool
|
from fastapi.concurrency import run_in_threadpool
|
||||||
|
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
from app.flow.alerts import AlertManager
|
from fluksio.flow.alerts import AlertManager
|
||||||
from app.flow.events import EventBus
|
from fluksio.flow.events import EventBus
|
||||||
from app.flow.executor import ExecutionService
|
from fluksio.flow.executor import ExecutionService
|
||||||
from app.flow.messages import MessageSpec, flow_of, qualify
|
from fluksio.flow.messages import MessageSpec, flow_of, qualify
|
||||||
from app.flow.nodes import (
|
from fluksio.flow.nodes import (
|
||||||
RESERVED_SETTINGS,
|
RESERVED_SETTINGS,
|
||||||
ChangeNode,
|
ChangeNode,
|
||||||
DelayNode,
|
DelayNode,
|
||||||
@@ -44,9 +44,9 @@ from app.flow.nodes import (
|
|||||||
SwitchNode,
|
SwitchNode,
|
||||||
TriggerNode,
|
TriggerNode,
|
||||||
)
|
)
|
||||||
from app.flow.pipeline import NodeOutcome, Pipeline, ValidationIssue, ValueSource
|
from fluksio.flow.pipeline import NodeOutcome, Pipeline, ValidationIssue, ValueSource
|
||||||
from app.flow.remote import RemoteWorkerHub
|
from fluksio.flow.remote import RemoteWorkerHub
|
||||||
from app.flow.schemas import (
|
from fluksio.flow.schemas import (
|
||||||
BrainEdge,
|
BrainEdge,
|
||||||
BrainGraph,
|
BrainGraph,
|
||||||
BrainNode,
|
BrainNode,
|
||||||
@@ -56,12 +56,12 @@ from app.flow.schemas import (
|
|||||||
NodeStatusPublic,
|
NodeStatusPublic,
|
||||||
NodeTypeInfo,
|
NodeTypeInfo,
|
||||||
)
|
)
|
||||||
from app.flow.secrets import SecretNotFound, resolve_params
|
from fluksio.flow.secrets import SecretNotFound, resolve_params
|
||||||
from app.flow.state import MemoryState, StateBackend
|
from fluksio.flow.state import MemoryState, StateBackend
|
||||||
from app.flow.store import LIB_DIR, FlowNotFound, FlowStore, LibNotFound
|
from fluksio.flow.store import LIB_DIR, FlowNotFound, FlowStore, LibNotFound
|
||||||
from app.flow.supervision import Supervisor
|
from fluksio.flow.supervision import Supervisor
|
||||||
from app.flow.worker_main import load_function
|
from fluksio.flow.worker_main import load_function
|
||||||
from app.flow.workers import PythonWorkerPool
|
from fluksio.flow.workers import PythonWorkerPool
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -807,7 +807,7 @@ class FlowController:
|
|||||||
What a dashboard picks from, so it spans flows rather than sitting
|
What a dashboard picks from, so it spans flows rather than sitting
|
||||||
inside one.
|
inside one.
|
||||||
"""
|
"""
|
||||||
from app.api.routes.messages import MessageInfo
|
from fluksio.api.routes.messages import MessageInfo
|
||||||
|
|
||||||
specs: dict[str, MessageSpec] = {}
|
specs: dict[str, MessageSpec] = {}
|
||||||
providers: dict[str, list[str]] = {}
|
providers: dict[str, list[str]] = {}
|
||||||
@@ -23,8 +23,8 @@ from typing import Any, Literal
|
|||||||
|
|
||||||
from pydantic import BaseModel, Field, field_validator, model_validator
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
||||||
|
|
||||||
from app.flow.schemas import _validate_name
|
from fluksio.flow.schemas import _validate_name
|
||||||
from app.flow.store import FlowStore, StaleVersion
|
from fluksio.flow.store import FlowStore, StaleVersion
|
||||||
|
|
||||||
#: Sibling of the shared-node library, and likewise not a flow.
|
#: Sibling of the shared-node library, and likewise not a flow.
|
||||||
DASHBOARD_DIR = "_dashboards"
|
DASHBOARD_DIR = "_dashboards"
|
||||||
@@ -18,11 +18,11 @@ import time
|
|||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
from app.flow.queue import MAX_DELIVERIES, WorkItem, WorkQueue
|
from fluksio.flow.queue import MAX_DELIVERIES, WorkItem, WorkQueue
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from app.flow.events import EventBus
|
from fluksio.flow.events import EventBus
|
||||||
from app.flow.pipeline import Pipeline
|
from fluksio.flow.pipeline import Pipeline
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -22,10 +22,10 @@ from sqlalchemy import delete, func, update
|
|||||||
from sqlalchemy.dialects.postgresql import insert
|
from sqlalchemy.dialects.postgresql import insert
|
||||||
from sqlmodel import Session, col
|
from sqlmodel import Session, col
|
||||||
|
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
from app.core.db import engine
|
from fluksio.core.db import engine
|
||||||
from app.flow.events import EventBus
|
from fluksio.flow.events import EventBus
|
||||||
from app.models import EngineEvent, FlowRun, MetricBucket
|
from fluksio.models import EngineEvent, FlowRun, MetricBucket
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -22,11 +22,11 @@ import tempfile
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
from app.flow.schemas import ModulePackage, ModulesInfo
|
from fluksio.flow.schemas import ModulePackage, ModulesInfo
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from app.flow.store import FlowStore
|
from fluksio.flow.store import FlowStore
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
"""Built-in node types.
|
||||||
|
|
||||||
|
Split by the outside world each one talks to. Importing from
|
||||||
|
``fluksio.flow.nodes`` keeps working, which is what every caller does.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from fluksio.flow.nodes.base import RESERVED_SETTINGS, Node
|
||||||
|
from fluksio.flow.nodes.delay import DelayNode
|
||||||
|
from fluksio.flow.nodes.exec import ExecNode
|
||||||
|
from fluksio.flow.nodes.file import FileNode
|
||||||
|
from fluksio.flow.nodes.http import HttpNode
|
||||||
|
from fluksio.flow.nodes.influx import InfluxDbNode
|
||||||
|
from fluksio.flow.nodes.inject import InjectNode
|
||||||
|
from fluksio.flow.nodes.logic import ChangeNode, JoinNode, RbeNode, SwitchNode
|
||||||
|
from fluksio.flow.nodes.mlp import MLPNode
|
||||||
|
from fluksio.flow.nodes.mqtt import MqttNode
|
||||||
|
from fluksio.flow.nodes.ntfy import NtfyNode
|
||||||
|
from fluksio.flow.nodes.trigger import TriggerNode
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ChangeNode",
|
||||||
|
"DelayNode",
|
||||||
|
"ExecNode",
|
||||||
|
"FileNode",
|
||||||
|
"HttpNode",
|
||||||
|
"InfluxDbNode",
|
||||||
|
"InjectNode",
|
||||||
|
"JoinNode",
|
||||||
|
"MLPNode",
|
||||||
|
"MqttNode",
|
||||||
|
"Node",
|
||||||
|
"NtfyNode",
|
||||||
|
"RESERVED_SETTINGS",
|
||||||
|
"RbeNode",
|
||||||
|
"SwitchNode",
|
||||||
|
"TriggerNode",
|
||||||
|
]
|
||||||
@@ -12,15 +12,15 @@ import logging
|
|||||||
from collections.abc import Callable, Coroutine, Iterable, Iterator
|
from collections.abc import Callable, Coroutine, Iterable, Iterator
|
||||||
from typing import TYPE_CHECKING, Any, TypeAlias
|
from typing import TYPE_CHECKING, Any, TypeAlias
|
||||||
|
|
||||||
from app.flow import logs
|
from fluksio.flow import logs
|
||||||
from app.flow.messages import MessageSpec, qualify
|
from fluksio.flow.messages import MessageSpec, qualify
|
||||||
from app.flow.supervision import Supervisor
|
from fluksio.flow.supervision import Supervisor
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
|
||||||
from app.flow.pipeline import Pipeline
|
from fluksio.flow.pipeline import Pipeline
|
||||||
from app.flow.state import StateBackend
|
from fluksio.flow.state import StateBackend
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, Any
|
|||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict
|
from pydantic import BaseModel, ConfigDict
|
||||||
|
|
||||||
from app.flow.nodes.base import Node
|
from fluksio.flow.nodes.base import Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
@@ -15,8 +15,8 @@ from typing import Any
|
|||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
from app.flow.messages import MessageSpec
|
from fluksio.flow.messages import MessageSpec
|
||||||
from app.flow.nodes.base import Node
|
from fluksio.flow.nodes.base import Node
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -14,15 +14,15 @@ from typing import Any, Literal
|
|||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
from app.flow.messages import MessageSpec
|
from fluksio.flow.messages import MessageSpec
|
||||||
from app.flow.nodes.base import Node
|
from fluksio.flow.nodes.base import Node
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def files_root() -> Path:
|
def files_root() -> Path:
|
||||||
"""Where flow-readable files live: beside the flow store, not inside it."""
|
"""Where flow-readable files live: beside the flow store, not inside it."""
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
|
|
||||||
return settings.FLOWS_DIR.parent / "files"
|
return settings.FLOWS_DIR.parent / "files"
|
||||||
|
|
||||||
@@ -13,8 +13,8 @@ from urllib.parse import urlsplit, urlunsplit
|
|||||||
import httpx
|
import httpx
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
from app.flow.messages import MessageSpec
|
from fluksio.flow.messages import MessageSpec
|
||||||
from app.flow.nodes.base import Node
|
from fluksio.flow.nodes.base import Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
@@ -8,8 +8,8 @@ from typing import Any
|
|||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
from app.flow.messages import MessageSpec
|
from fluksio.flow.messages import MessageSpec
|
||||||
from app.flow.nodes.base import Node, NodeResult
|
from fluksio.flow.nodes.base import Node, NodeResult
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -15,8 +15,8 @@ from typing import TYPE_CHECKING, Any
|
|||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
from app.flow.messages import MessageSpec
|
from fluksio.flow.messages import MessageSpec
|
||||||
from app.flow.nodes.base import Node
|
from fluksio.flow.nodes.base import Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
@@ -14,8 +14,8 @@ from typing import Any, Literal
|
|||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
from app.flow.messages import MessageSpec
|
from fluksio.flow.messages import MessageSpec
|
||||||
from app.flow.nodes.base import Node
|
from fluksio.flow.nodes.base import Node
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -10,8 +10,8 @@ from typing import Any
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from pydantic import BaseModel, ConfigDict
|
from pydantic import BaseModel, ConfigDict
|
||||||
|
|
||||||
from app.flow.messages import MessageSpec
|
from fluksio.flow.messages import MessageSpec
|
||||||
from app.flow.nodes.base import Node
|
from fluksio.flow.nodes.base import Node
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -11,8 +11,8 @@ from typing import TYPE_CHECKING, Any
|
|||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
from app.flow.messages import MessageSpec
|
from fluksio.flow.messages import MessageSpec
|
||||||
from app.flow.nodes.base import Node
|
from fluksio.flow.nodes.base import Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
@@ -13,9 +13,9 @@ from typing import Any
|
|||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
from app.flow.messages import MessageSpec
|
from fluksio.flow.messages import MessageSpec
|
||||||
from app.flow.nodes.base import Node
|
from fluksio.flow.nodes.base import Node
|
||||||
from app.flow.nodes.http import shared_client
|
from fluksio.flow.nodes.http import shared_client
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -13,8 +13,8 @@ from typing import Any
|
|||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
from app.flow.messages import MessageSpec
|
from fluksio.flow.messages import MessageSpec
|
||||||
from app.flow.nodes.base import Node
|
from fluksio.flow.nodes.base import Node
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -16,9 +16,9 @@ from pathlib import Path
|
|||||||
|
|
||||||
from pydantic import BaseModel, Field, field_validator
|
from pydantic import BaseModel, Field, field_validator
|
||||||
|
|
||||||
from app.core.config import settings
|
from fluksio.core.config import settings
|
||||||
from app.flow.dashboards import DashboardNotFound, DashboardStore
|
from fluksio.flow.dashboards import DashboardNotFound, DashboardStore
|
||||||
from app.flow.schemas import _validate_name
|
from fluksio.flow.schemas import _validate_name
|
||||||
|
|
||||||
|
|
||||||
class PanelDef(BaseModel):
|
class PanelDef(BaseModel):
|
||||||
@@ -20,13 +20,13 @@ from typing import Any, Literal
|
|||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from app.flow import logs
|
from fluksio.flow import logs
|
||||||
from app.flow.artifacts import is_reference
|
from fluksio.flow.artifacts import is_reference
|
||||||
from app.flow.events import EventBus
|
from fluksio.flow.events import EventBus
|
||||||
from app.flow.messages import flow_of
|
from fluksio.flow.messages import flow_of
|
||||||
from app.flow.nodes import Node
|
from fluksio.flow.nodes import Node
|
||||||
from app.flow.queue import WorkQueue
|
from fluksio.flow.queue import WorkQueue
|
||||||
from app.flow.state import MemoryState, StateBackend
|
from fluksio.flow.state import MemoryState, StateBackend
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -1136,7 +1136,7 @@ class Pipeline:
|
|||||||
Returns False when there is no queue to hold the item, in which case
|
Returns False when there is no queue to hold the item, in which case
|
||||||
the caller has to wait however it waited before.
|
the caller has to wait however it waited before.
|
||||||
"""
|
"""
|
||||||
from app.flow.queue import WorkItem
|
from fluksio.flow.queue import WorkItem
|
||||||
|
|
||||||
if self._queue is None or seconds <= 0:
|
if self._queue is None or seconds <= 0:
|
||||||
return False
|
return False
|
||||||
@@ -1158,7 +1158,7 @@ class Pipeline:
|
|||||||
|
|
||||||
def _enqueue_cascade(self, node: Node, outputs: dict[str, Any] | None) -> None:
|
def _enqueue_cascade(self, node: Node, outputs: dict[str, Any] | None) -> None:
|
||||||
"""Journal a trigger, or fall back to running it here if that fails."""
|
"""Journal a trigger, or fall back to running it here if that fails."""
|
||||||
from app.flow.queue import WorkItem
|
from fluksio.flow.queue import WorkItem
|
||||||
|
|
||||||
item = WorkItem(
|
item = WorkItem(
|
||||||
kind="cascade",
|
kind="cascade",
|
||||||
@@ -15,7 +15,7 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
from importlib.metadata import entry_points
|
from importlib.metadata import entry_points
|
||||||
|
|
||||||
from app.flow.connector import CONTRACT_VERSION, ConnectorNode
|
from fluksio.flow.connector import CONTRACT_VERSION, ConnectorNode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ ENTRY_POINT_GROUP = "fluksio.node_types"
|
|||||||
|
|
||||||
def load_plugins() -> list[str]:
|
def load_plugins() -> list[str]:
|
||||||
"""Register every installed connector. Returns the type names it added."""
|
"""Register every installed connector. Returns the type names it added."""
|
||||||
from app.flow.controller import NODE_TYPES, NodeType, _schema_of
|
from fluksio.flow.controller import NODE_TYPES, NodeType, _schema_of
|
||||||
|
|
||||||
added: list[str] = []
|
added: list[str] = []
|
||||||
for entry in entry_points(group=ENTRY_POINT_GROUP):
|
for entry in entry_points(group=ENTRY_POINT_GROUP):
|
||||||
@@ -30,7 +30,7 @@ import time
|
|||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from app.flow.workers import NodeTimeout, RemoteError, _remote_class
|
from fluksio.flow.workers import NodeTimeout, RemoteError, _remote_class
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -42,14 +42,14 @@ from sqlalchemy import update
|
|||||||
from sqlalchemy.dialects.postgresql import insert as pg_insert
|
from sqlalchemy.dialects.postgresql import insert as pg_insert
|
||||||
from sqlmodel import Session, col, select
|
from sqlmodel import Session, col, select
|
||||||
|
|
||||||
from app.core.db import engine as db_engine
|
from fluksio.core.db import engine as db_engine
|
||||||
from app.flow.controller import FlowController, RunContext
|
from fluksio.flow.controller import FlowController, RunContext
|
||||||
from app.flow.messages import qualify
|
from fluksio.flow.messages import qualify
|
||||||
from app.flow.pipeline import NodeOutcome, Pipeline
|
from fluksio.flow.pipeline import NodeOutcome, Pipeline
|
||||||
from app.flow.queue import WorkItem, WorkQueue
|
from fluksio.flow.queue import WorkItem, WorkQueue
|
||||||
from app.flow.schemas import FlowDef
|
from fluksio.flow.schemas import FlowDef
|
||||||
from app.flow.state import MemoryState, StateBackend
|
from fluksio.flow.state import MemoryState, StateBackend
|
||||||
from app.models import Run, RunArtifact, RunMetric, RunNode
|
from fluksio.models import Run, RunArtifact, RunMetric, RunNode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ from typing import Any, Literal
|
|||||||
|
|
||||||
from pydantic import BaseModel, Field, field_validator
|
from pydantic import BaseModel, Field, field_validator
|
||||||
|
|
||||||
from app.flow.messages import MessageSpec
|
from fluksio.flow.messages import MessageSpec
|
||||||
|
|
||||||
NAME_PATTERN = re.compile(r"^[a-z][a-z0-9_]*$")
|
NAME_PATTERN = re.compile(r"^[a-z][a-z0-9_]*$")
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ import subprocess
|
|||||||
import threading
|
import threading
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from app.flow.schemas import FlowDef
|
from fluksio.flow.schemas import FlowDef
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ from collections import deque
|
|||||||
from collections.abc import Callable, Coroutine
|
from collections.abc import Callable, Coroutine
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from app.flow.events import EventBus
|
from fluksio.flow.events import EventBus
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ import os
|
|||||||
import time
|
import time
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
from app.flow.events import EventBus
|
from fluksio.flow.events import EventBus
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ def _store_bytes(data: bytes, name: str, media_type: str) -> dict[str, Any]:
|
|||||||
A worker in the engine's own container writes the file; one on another host
|
A worker in the engine's own container writes the file; one on another host
|
||||||
puts it over HTTP. Node code cannot tell the difference, which is the point
|
puts it over HTTP. Node code cannot tell the difference, which is the point
|
||||||
— the same flow runs either place. The hashing is repeated from
|
— the same flow runs either place. The hashing is repeated from
|
||||||
``app.flow.artifacts`` rather than imported, because nothing of the app is
|
``fluksio.flow.artifacts`` rather than imported, because nothing of the app is
|
||||||
importable here.
|
importable here.
|
||||||
"""
|
"""
|
||||||
digest = "sha256:" + hashlib.sha256(data).hexdigest()
|
digest = "sha256:" + hashlib.sha256(data).hexdigest()
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user