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:
2026-08-21 21:48:05 +02:00
co-authored by Claude Opus 5
parent 97785ee590
commit 60d7ec81c0
170 changed files with 629 additions and 619 deletions
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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/.*
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
# Python # Python
__pycache__ __pycache__
app.egg-info fluksio.egg-info
*.pyc *.pyc
.mypy_cache .mypy_cache
.coverage .coverage
+1 -1
View File
@@ -1,6 +1,6 @@
*.png *.png
__pycache__ __pycache__
app.egg-info fluksio.egg-info
*.pyc *.pyc
.mypy_cache .mypy_cache
.coverage .coverage
+4 -4
View File
@@ -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
View File
@@ -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
-26
View File
@@ -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",
]
-37
View File
@@ -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,
@@ -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:
+26
View File
@@ -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__)
+37
View File
@@ -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