<@ULVA73B9P> I created this flow: ``` from autou...
# marvin-ai
m
@Marvin I created this flow:
Copy code
from autoupdate.flows.autoupdate import AutoupdateRequest


@task
async def spider_task(source_name: str = "", links: list[str] = []):
    logger = get_run_logger()
    host = DockerHost(base_url=os.getenv("DOCKER_HOST"))

    <http://logger.info|logger.info>(f"Docker host: {os.getenv('DOCKER_HOST')}")

    content = {
        "source_name": source_name,
        "links": links,
    }
    in_memory_file = io.BytesIO()
    with tarfile.open(fileobj=in_memory_file, mode="w") as tar:
        info = tarfile.TarInfo("content.json")
        info.size = len(json.dumps(content).encode("utf-8"))
        tar.addfile(info, io.BytesIO(json.dumps(content).encode("utf-8")))
    in_memory_file.seek(0)

    container = await create_docker_container(
        image="<http://715841351541.dkr.ecr.us-east-1.amazonaws.com/spiders-zocdoc:latest|715841351541.dkr.ecr.us-east-1.amazonaws.com/spiders-zocdoc:latest>", command="tail -f /dev/null", environment={
            "PREFECT_API_URL": "<http://localhost:4200/api>",
        }, restart_policy={
            "name": "on-failure",
            "max_attempts": 3,
        },
         detach=True
    )
    container = await start_docker_container(container_id=container.id)
    client = host.get_client()
    client.api.put_archive(container=container.id, path="/", data=in_memory_file)
    logs = await get_docker_container_logs(container_id=container.id)
    <http://logger.info|logger.info>(logs)
    await stop_docker_container(container_id=container.id)
    # await remove_docker_container(container_id=container.id)
    return container


@flow
def auto_update_flow(request: AutoupdateRequest):
    logger = get_run_logger()
    container = asyncio.run(spider_task("", []))
    <http://logger.info|logger.info>(f"Container: {container} exited")
    return container
The imported model doesn't appear as a form in prefect UI, however, when moving the model code to the same file the form appears
m
thought for 178.3 seconds
Short answer: the UI form is generated from the deployment’s parameter schema. Prefect builds that schema by importing your type hints at deploy time. When the model lives in another module and that module isn’t importable in the environment where you run prefect deploy (or flow.from_source(...).deploy(...)), Prefect can’t resolve the annotation and falls back to “any/object” — the UI has nothing to render. When you move the model into the same file, it’s trivially importable, schema generation succeeds, and the form appears. Joy. What to check/fix - Make sure the module containing AutoupdateRequest is importable in the environment where you create the deployment: - If it’s a local package, pip install it into that environment (pip install -e . or add it to your pyproject/requirements). - If you’re using source-based deployments (flow.from_source(...) or prefect deploy), ensure the repository path includes the module and that the entrypoint is correct. - Avoid circular imports; they can prevent annotation resolution at import time. - Confirm AutoupdateRequest is a Pydantic v2 BaseModel. Prefect 3.x uses Pydantic v2 for schema generation. A v1 model or a non-Pydantic class won’t yield a rich form. - Redeploy after changes. The UI shows the schema captured at deployment time; editing code without redeploying won’t update the form. - Verify what Prefect “sees” before deploying:
Copy code
from prefect.utilities.callables import generate_parameter_schema
  from pprint import pprint

  # Import your flow
  from your_module import auto_update_flow

  schema = generate_parameter_schema(auto_update_flow)
  pprint(schema.model_dump())  # or print(schema.json())
If the
request
parameter shows up as an untyped object with no properties, Prefect could not import/resolve your model. Common gotchas - PYTHONPATH/importability: running prefect deploy from a shell where your package isn’t on the path is the usual culprit. - Forward references: if you use from future import annotations but don’t import the model in the module where the flow is defined, type resolution can fail. - Dynamic models (e.g., pydantic.create_model) can trip up JSON schema generation. - Dataclasses/TypedDicts don’t provide the same rich schema as Pydantic models. Side note while I’m here: inside a flow, don’t use asyncio.run on a Prefect task. Either: - Make the flow async and
await spider_task(...)
, or - Use
spider_task.submit(...)
in a sync flow. If you share: - Your Prefect version (assuming 3.x?) - How you create the deployment (prefect deploy vs flow.from_source(...).deploy(...)) - How AutoupdateRequest is defined (the class body), and where it lives relative to your entrypoint …I can point at the exact import/scheme issue instead of waving my arms at the void. Docs for more light reading, should you be so inclined: - Form building in Prefect 3.x - prefect.utilities.pydantic API
m
@Marvin
Copy code
# ls -alR
.:
total 20
drwxr-xr-x 3 1000 1000 4096 Aug  9 19:51 .
drwxr-xr-x 1 root root 4096 Aug  9 20:33 ..
drwxr-xr-x 3 1000 1000 4096 Aug  9 23:08 flows
-rw-r--r-- 1 1000 1000  727 Aug  9 23:08 prefect.yaml
-rw-r--r-- 1 1000 1000   14 Aug  9 20:18 requirements.txt

./flows:
total 20
drwxr-xr-x 3 1000 1000 4096 Aug  9 23:08 .
drwxr-xr-x 3 1000 1000 4096 Aug  9 19:51 ..
drwxr-xr-x 2 1000 1000 4096 Aug  9 23:17 __pycache__
-rw-r--r-- 1 1000 1000 2143 Aug  9 23:18 auto_update.py
-rw-r--r-- 1 1000 1000 1287 Aug  9 23:12 autoupdate.py

./flows/__pycache__:
total 28
drwxr-xr-x 2 1000 1000 4096 Aug  9 23:17 .
drwxr-xr-x 3 1000 1000 4096 Aug  9 23:08 ..
-rw-r--r-- 1 root root 6831 Aug  9 23:17 auto_update.cpython-311.pyc
-rw-r--r-- 1 root root 1842 Aug  9 19:46 auto_update.cpython-312.pyc
-rw-r--r-- 1 root root 3007 Aug  9 23:12 autoupdate.cpython-311.pyc
-rw-r--r-- 1 1000 1000  476 Aug  9 19:43 run_docker_image.cpython-311.pyc
I run
prefect deploy
while iam standing at root dir where prefect.yml is located
m
thought for 27.4 seconds
Thanks — that directory tree explains it. Your import says
from autoupdate.flows.autoupdate import AutoupdateRequest
, but your repo only has
flows/autoupdate.py
. There’s no top-level
autoupdate
package, and there are no
__init__.py
files, so at deploy time Prefect can’t import that model. When the annotation can’t be resolved, the parameter schema degrades and the UI has nothing to render as a form. When you paste the model into the same file, the import isn’t needed, so the schema builds and the form appears. Predictable, if not thrilling. Two clean fixes — pick one: Option A: Keep your current layout; fix the import and make flows a package - Change the import in
flows/auto_update.py
to match your tree:
Copy code
from flows.autoupdate import AutoupdateRequest
- Add an empty
__init__.py
to
flows/
so relative/package imports work:
Copy code
touch flows/__init__.py
- Ensure your deployment entrypoint in prefect.yaml points to the file correctly (probably already does), e.g.:
Copy code
entrypoint: flows/auto_update.py:auto_update_flow
- Redeploy:
prefect deploy
Option B: Make the structure match your original import - Restructure to a real package:
Copy code
autoupdate/
    __init__.py
    flows/
      __init__.py
      auto_update.py
      autoupdate.py
  prefect.yaml
  requirements.txt
- Keep your original import:
Copy code
from autoupdate.flows.autoupdate import AutoupdateRequest
- Update the entrypoint in prefect.yaml accordingly:
Copy code
entrypoint: autoupdate/flows/auto_update.py:auto_update_flow
- Redeploy. Sanity checks - After fixing imports, verify what Prefect “sees” before deploying:
Copy code
from prefect.utilities.callables import generate_parameter_schema
  from pprint import pprint
  from flows.auto_update import auto_update_flow  # or your final path

  schema = generate_parameter_schema(auto_update_flow)
  pprint(schema.model_dump())
You should see your
request
parameter described with properties from
AutoupdateRequest
. If it’s just an untyped object, the model still isn’t importable. - Ensure you’re on Prefect 3.x and your model inherits from Pydantic v2’s BaseModel. Prefect 3’s UI form generation depends on that. Docs: Form building in Prefect 3.x Unrelated but worth fixing while you’re here - Inside a flow, don’t call
asyncio.run(spider_task(...))
. Either make the flow async and
await spider_task(...)
, or in a sync flow use
spider_task.submit(...).result()
. -
links: list[str] = []
is a mutable default. It’ll bite later. Prefer:
Copy code
@task
  async def spider_task(source_name: str = "", links: list[str] | None = None):
      links = links or []
      ...
If you paste your prefect.yaml (especially the entrypoint) and the exact import line you’re using in
auto_update.py
, I’ll sanity-check it so the UI form shows up on the first redeploy instead of the third.