Mohamed Zaki
08/09/2025, 11:23 PMfrom 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 appearsMarvin
08/09/2025, 11:24 PMMarvin
08/09/2025, 11:27 PMfrom 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 APIMohamed Zaki
08/09/2025, 11:30 PM# 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 locatedMarvin
08/09/2025, 11:31 PMMarvin
08/09/2025, 11:31 PMfrom 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:
from flows.autoupdate import AutoupdateRequest
- Add an empty __init__.py
to flows/
so relative/package imports work:
touch flows/__init__.py
- Ensure your deployment entrypoint in prefect.yaml points to the file correctly (probably already does), e.g.:
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:
autoupdate/
__init__.py
flows/
__init__.py
auto_update.py
autoupdate.py
prefect.yaml
requirements.txt
- Keep your original import:
from autoupdate.flows.autoupdate import AutoupdateRequest
- Update the entrypoint in prefect.yaml accordingly:
entrypoint: autoupdate/flows/auto_update.py:auto_update_flow
- Redeploy.
Sanity checks
- After fixing imports, verify what Prefect “sees” before deploying:
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:
@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.