<@ULVA73B9P> when using a modal work pool, how can...
# ask-community
b
@Marvin when using a modal work pool, how can I access secrets i've already configured within modal like secret environment variables?
m
To access secrets configured within a Modal work pool, such as secret environment variables, you typically need to use Prefect's Secret blocks. Unfortunately, I couldn't find specific documentation on accessing Modal secrets directly. However, here are some general steps and resources that might help: 1. Secret Blocks: Prefect recommends using Secret blocks to store and access sensitive information. You can create and manage these secrets through the Prefect UI, API, or CLI. This approach ensures that sensitive data is not exposed in your code. 2. Environment Variables: If your secrets are stored as environment variables, you can access them using Python's built-in
os.environ
module within your Prefect tasks or flows. 3. ControlFlow's Private Flows: If you're using ControlFlow, you can create isolated execution environments that encapsulate sensitive information within private flows. This ensures that sensitive data is only available within the private flow and not exposed to the main workflow. More information can be found in the ControlFlow documentation on private flows. For more details on work pools and secret management, you can refer to the Prefect documentation on work pools and Prefect documentation on secrets.
b
@Marvin the secrets are created in modal, not prefect
m
To access secrets configured in Modal within a Prefect work pool, you would generally need to integrate Prefect with the service where your secrets are stored. Here's a general approach: 1. Environment Variable Access: If Modal makes secrets available as environment variables, you can access them directly in your Prefect tasks using Python's
os.environ
. This is a straightforward method if Modal automatically injects these environment variables into the runtime environment where Prefect is executing. 2. Third-Party Secret Managers: If Modal provides an API or SDK to fetch secrets, you can write a Prefect task that uses this API to retrieve the secrets at runtime. Prefect allows you to connect to third-party services securely using Credential and Secret blocks, but this typically involves storing credentials in Prefect. Since you want to use Modal's secrets directly, ensure your tasks have the necessary permissions and access to Modal's APIs or services. 3. Custom Integration: If Modal has a specific way to expose secrets to applications running in its environment, you might need to implement a custom integration within your Prefect tasks to fetch and use these secrets. For more guidance on handling secrets and integrating with third-party services, you can refer to the Prefect documentation on secrets.
j
hey! you can make sure they're injected into your modal container as env vars by passing them by name to the
secrets
field:
b
oh thanks @Jake Kaplan! but where is that defined? I'm using a
prefect.yaml
deployment file
j
ah understood! It would be under
job_variables
. see below I believe it would look like:
🤯 1
b
didnt know about that!! trying now, will add to my guide once i get it working
j
any of the fields on from modal work pool you can set per deployment through
job_variables
! let me actually just double check I'm not giving you the wrong yaml syntax...
under your deployments section it should look like:
Copy code
- name: my-modal-deployment
  ...
  work_pool:
    name: my-modal-pool
    job_variables: {
        "secrets": ["test-secret-123"]
    }
b
Copy code
job_variables:
    - secrets: ["supabase"]
this didnt work, let me try
Copy code
job_variables: {
        "secrets": ["test-secret-123"]
    }
Hm unfortunately neither of those worked @Jake Kaplan -- to be clear, the secret is inside modal, not inside prefect. That's your expectation for this right?
i think i did it wrong, trying again
j
hm yes. What are you seeing? Just for reference: • my modal secret • my deployment job variables • secret's key:values populated as env var in the container
b
ah yes it worked!!! thank you SO much
do you mind if I pick your brain for another piece? This is all going directly into my guide for all prefect users + prefect docs 🙂
j
sure fire away
b
In the guide, i talk through how to how to get your packages installed, but what i have written doesnt actually work assuming you have a full package you're installing (like your app itself). The only way I was able to get it to work is like this, which feels super hacky.
Copy code
pull:
- prefect.deployments.steps.git_clone:
    id: clone-step
    repository: <https://github.com/Team/my-repo>
    branch: main
    credentials: '{{ prefect.blocks.github-credentials.pat-pull }}'
- prefect.deployments.steps.run_shell_script:
    directory: '{{ clone-step.directory }}/my-app'
    script: |
        pip install --upgrade 'uv>=0.5.6,<0.6'
        /bin/sh -c "uv export --no-dev --no-editable --no-hashes --locked > requirements.txt"
- prefect.deployments.steps.pip_install_requirements:
    directory: '/{{ clone-step.directory }}/my-app'
    requirements_file: requirements.txt
- prefect.deployments.steps.set_working_directory:
    directory: '/{{ clone-step.directory }}/my-app/src' # HERE i have to cd into src so that my project itself is discoverable
i know i could use docker, but the point of the guide is to be really lightweight. This is working, but it's the only way i could figure out how. Do you have an idea for another way to accomplish this? Just getting my requirements and app into the prefect python env namespace
j
It sounds like theres maybe a couple issues here. does your project have a requirements file? Are you running uv export to get your package listed as a dependency because it's not included originally (as like
.
)?
b
my project doesn't use a requirements.txt because it uses a
pyproject.toml
and a
uv.lock
i use
uv export
to get a requirements file because it seems like thats the only option that prefect accepted
but when i run
uv export
i do get a
.
in my requirements file, which is why im so confused
j
So you're not forced to use the pip install pull step it's really just a convenience. It's really just running
python -m pip install -r <file>
. I do think think that should take a pyproject.toml as well though. I really think you should just be able to do:
Copy code
prefect.deployments.steps.run_shell_script
    script: "uv pip install ."
or
Copy code
prefect.deployments.steps.run_shell_script
    script: "uv sync"
which I believe should install all of the dependencies including your package?
b
yes it definitely should
j
(sorry if I'm missing the point 😅)
b
but when prefect runs my entrypoint i get a
ModuleNotFound: no module app
unless i cd into
src
with that last command of mine
i tried just
uv sync
as well as
uv pip install --system .
and neither of those worked unfortunately
j
I think that should at least let you consolidate pull steps 2 and 3
I am unsure of the module not found part... do you have a traceback?
you can also add a pull step to
pip freeze | app
to make sure it's being installed as expected
b
Copy code
02:10:41.892 | ERROR   | prefect.engine - Engine execution of flow run '0c18d59e-3252-4240-8f77-0734b7fd671e' exited with unexpected exception
Traceback (most recent call last):
  File "<frozen importlib._bootstrap_external>", line 995, in exec_module
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "/exploration-main/copilot/src/flows/auto_respond.py", line 6, in <module>
    from app.baml_client.async_client import b
ModuleNotFoundError: No module named 'app'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/prefect/engine.py", line 51, in <module>
    flow: "Flow[..., Any]" = load_flow(flow_run)
                             ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/prefect/flow_engine.py", line 130, in load_flow
    flow = run_coro_as_sync(
           ^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/prefect/utilities/asyncutils.py", line 209, in run_coro_as_sync
    return call.result()
           ^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/prefect/_internal/concurrency/calls.py", line 330, in result
    return self.future.result(timeout=timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/prefect/_internal/concurrency/calls.py", line 193, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/usr/local/lib/python3.12/site-packages/prefect/_internal/concurrency/calls.py", line 403, in _run_async
    result = await coro
             ^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/prefect/utilities/asyncutils.py", line 190, in coroutine_wrapper
    return await task
           ^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/prefect/client/utilities.py", line 69, in wrapper
    return await func(client, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/prefect/flows.py", line 2332, in load_flow_from_flow_run
    flow = await run_sync_in_worker_thread(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/prefect/utilities/asyncutils.py", line 235, in run_sync_in_worker_thread
    result = await anyio.to_thread.run_sync(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/anyio/to_thread.py", line 56, in run_sync
    return await get_async_backend().run_sync_in_worker_thread(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 2461, in run_sync_in_worker_thread
    idle_workers = _threadpool_idle_workers.get()
               ^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 962, in run
    super().__init__(name="AnyIO worker thread")
                             ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/prefect/utilities/asyncutils.py", line 245, in call_with_mark
    return call()
           ^^^^^^
  File "/usr/local/lib/python3.12/site-packages/prefect/flows.py", line 2050, in load_flow_from_entrypoint
    flow: Flow[P, Any] = import_object(entrypoint)  # pyright: ignore[reportRedeclaration]
                         ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/prefect/utilities/importtools.py", line 223, in import_object
    module = load_script_as_module(script_path)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/prefect/utilities/importtools.py", line 188, in load_script_as_module
    raise ScriptError(user_exc=exc, path=path) from exc
prefect.exceptions.ScriptError: Script at 'src/flows/auto_respond.py' encountered an exception: ModuleNotFoundError("No module named 'app'")
j
and what's your entrypoint?
b
src/flows/auto_respond.py:main
j
short answer: I'm not sure 😄 can you try the extra pip freeze pull step or similar just to verify it's getting installed?
b
the problem with
uv sync
im quite sure is that it creates a new virtual environment which is separate from the one thats running in prefect. But
uv pip install --system
should be the same one as the running python
j
I swear
UV_SYSTEM_PYTHON=1
is set in the prefect docker image that gets used as a base >.>
👀 1
b
heres my generated requirements.txt
Copy code
> Running run_shell_script step...
# This file was autogenerated by uv via the following command:
#    uv export --no-dev --no-editable --no-hashes --locked
.
aiohappyeyeballs==2.4.4
aiohttp==3.11.11
aiohttp-retry==2.8.3
aiosignal==1.3.2
annotated-types==0.7.0
anyio==4.7.0
appnope==0.1.4 ; sys_platform == 'darwin'
asttokens==3.0.0
async-timeout==5.0.1 ; python_full_version < '3.11'
...
and on a pip freeze i see
Copy code
jupyterlab_widgets==3.0.13
lancedb==0.17.0
leasing-copilot @ file:///exploration-main/leasing-copilot
that last one is my app
so it's definitely installed 😂
😂 1
j
assuming the importable package is
leasing_copilot
shouldn't your import be
from leasing_copilot.app.baml_client.async_client import b
? or does it actually import as
app
b
its imported as
app.
j
can you run a shell another shell script that is
python -c "import app; print(app.__version__")
b
funny i was just doing that. i ran
Copy code
python -c "from app.baml_client.sync_client import b"
and i did get a
Copy code
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ModuleNotFoundError: No module named 'app.baml_client'
even though th line above it shows that its installed. It's clearly a different python and pip...
but
app
was installed, since it complained about
app.baml_client
and not
app
j
that's so odd, what does
Copy code
from inspect import getfile
getfile(app)
say? does it come back as expected?
b
running now
it returned
/usr/local/lib/python3.12/site-packages/app/__init__.py
im going to name my folder something different and see what happens......
j
i was going to see if you inspect the source too
i'm not sure this is what it is
but I know
App
is a modal thing
b
yea but i'd expect that to be in
site-packages/modal
-- but good call on inspect ill try that after this attempt
this is bonkers 😂
renaming didnt work, ill getsource
j
just to verify it's YOUR file
💯 1
b
thats why i tried changing the name, i thought that it might be a conflict
👍 1
but the name change on the folder from
app
didnt work. i feel like there might be some sneaky caching going on, because i reverted back to
app
and now i can't even import
app
in the way i did a second ago
OK @Jake Kaplan it is defintely the right file on
getsource
i did a listdir as well and got the files in my project...
do you expect
uv
to be installed on the image? I tried just
uv sync
with nothing else, and got
Copy code
/bin/sh: 1: uv: not found
does this imply that this is potentially the wrong base imgae being used?
j
🤔 are you setting the base image to be anything? Or just letting the modal push pool dictate it
sorry yes, I do expect uv to be installed
b
I'm not specifying any image
Copy code
push:
build:
deployments:
- name: flow
  work_pool:
    name: modal-pool
    work_queue_name: default
    job_variables: {
        "secrets": ["supabase"]
    }
  ...
j
can you add a shell script for
python -c "import prefect; print(prefect.__version__)"
I'm quite confused here
b
Haha me too! But when we get this figured out I'll update the guide and no one will have to go through this again lol
🙌 1
I have to step away for the next ~1 hour unfortunately
j
no worries at all, I do as well. I will try and do some of my own testing and setup a package repro with what you're seeing
b
Any interest in pairing this out live together?
You can start here as a template for testing. This doesn't end a package installed so it works but can be easily adapted https://github.com/Ben-Epstein/prefect-modal
j
definitely would be down to pair live. Let me see what my afternoon looks like and will get back to you in a bit
highfive 1
b
I'm free for the next 2 hours @Jake Kaplan -- let me know if any time works well for you!
j
hey! Apologies was in some meetings. I have ~45 minutes until about 3PM ET if you're still interested?
just need 5 min to make some coffee
and that does look promising... 👀
b
yup! free till 3pm also
i haev a log up that is going to make your brain hurt 😂
j
Oh dear... apologies for the delay. What's easiest for you for chatting? I can make a google meet?
Or wait I forgot we can huddle here I think
t
@Ben Epstein I also just ran into a similar issue trying to add slack-sdk as a dependency I get:
Copy code
Unexpected exception encountered when trying to load flow

ModuleNotFoundError: No module named 'slack_sdk'
uv pip list
shows it is installed
b
Hey @Trey Gilliland I've been working hard with Jake on this all day. We think this is a prefect bug, but I figured out a workaround for now.
Copy code
pull:
- prefect.deployments.steps.git_clone:
    id: clone-step
    repository: <https://github.com/Team/repo|https://github.com/Team/repo>
    branch: main
    credentials: '{{ prefect.blocks.github-pat-pull }}'
- prefect.deployments.steps.run_shell_script:
    directory: '{{ clone-step.directory }}/project'
    script: |
        pip install --upgrade 'uv>=0.5.6,<0.6'
        /bin/sh -c "uv export --no-dev --no-editable --no-hashes --locked > requirements.txt"
        uv pip install --system -r requirements.txt
- prefect.deployments.steps.set_working_directory:
    directory: '/{{ clone-step.directory }}/project/src'
So I changed how uv installs, forcing it to the system python, and then I set the working dir to
src
so that your project itself is available in the sys.path. But this also means you need to adjust the entrypoint path in the yaml as well as where you invoke it from. For example I updated my makefile to be
Copy code
prefect-deploy: setup
        (cd src && uv run prefect deploy --all --prefect-file ./flows/prefect.yaml)
🙌 1
t
Yep, I saw that message from earlier. Works for me as well
🚀 1
b
Hopefully we root cause the bug and make it so a uv sync just works
j
Okay I think I have a better understanding of what's happening here. By default prefect is looking to set your entrypoint as a script not a package. If your entrypoint is a script:
src/prefect_modal/flows/flow1.py:main
it will effectively load the file totally independently, as such the package this file is part of won't be in your
PYTHONPATH
. Particularly because it is just making sure the parent directory is in the PYTHONPATH, not the source root. Which I think is fine for most sripts but won't work when your flow is embedded in the same package it's trying to utilize. However I am able to get my repro to work as expected if I specify the entrypoint as a module instead of a script. So it would be
prefect_modal.flows.flow1:main
(working repo here as an example)
b
thanks @Jake Kaplan for digging in! I tried this earlier and it didnt work but i'm ofc going to try again now.
By default prefect is looking to set your entrypoint as a script not a package. If your entrypoint is a script:
src/prefect_modal/flows/flow1.py:main
it will effectively load the file totally independently, as such the package this file is part of won't be in your
PYTHONPATH
.
i dont quite understand why this causes an issue, if your package it installed into the python environment. For example I can call
Copy code
python src/flows/my_flow.py
from anywhere and it will work because
flows
is installed in my python environment's site-packages
hmmm did you run this on modal directly? because i'm still getting a command not found on
uv
j
I was doing it locally so I could debug easier. You may still need to install uv. There is a separate issue I'm investigating with Modal work pools where it seems modal's built-in dependencies forcing prefect to downgrade to 2.x
b
oh hm interesting. but i don't think the example you gave above in modal works locally because your uv environment is the same and "global" -- but in modal I still get this issue
also unfortunately
uv pip install
is not a usable solution because it doesn't leverage the lockfile, so you can get unexpected behavior. So the
uv export --locked
followed by
uv pip install --system -r requirements.txt
is still a stronger solution at least for now
👍 1
j
Let me re-try on modal in a bit and w/o uv pip install. If my understanding is correct, I still think using the module as the entrypoint should fix.
There still may need to be something that changes in the flow loading from prefect, just not so sure now that it's strictly a bug (not working as intended) as opposed to maybe not great design/undocumented expectation.
👍 1
b
@Jake Kaplan so i tried using
UV_PROJECT_ENVIRONMENT
to set the uv target environment to the system python and very interestingly got this error
Copy code
error: Project virtual environment directory `/usr/local` cannot be used because it is not a compatible environment but cannot be recreated because it is not a virtual environment
particularly interesting because in their docs example they use
/usr/local
https://docs.astral.sh/uv/concepts/projects/config/#project-environment-path and I did the same thing
Copy code
/bin/sh -c "UV_PROJECT_ENVIRONMENT=$(python -c \"import sysconfig; print(sysconfig.get_config_var('prefix'))\") uv sync"
j
Okay wait lol
I got this to work on modal:
Copy code
# Welcome to your prefect.yaml file! You can use this file for storing and managing
# configuration for deploying your flows. We recommend committing this file to source
# control along with your flow code.

# Generic metadata about this project
name: test-flows
prefect-version: 3.1.15

# build section allows you to manage and build docker images
build:

# push section allows you to manage if and how this project is uploaded to remote locations
push:

# pull section allows you to provide instructions for cloning this project in remote locations
pull:
- prefect.deployments.steps.git_clone:
    id: clone-step
    repository: <https://github.com/jakekaplan/test-flows.git>
    branch: main
- prefect.deployments.steps.run_shell_script:
    script: pip install uv
- prefect.deployments.steps.run_shell_script:
    directory: '{{ clone-step.directory }}'
    script: uv sync

deployments:
- name: test-flows2-deploy
  version:
  tags: []
  concurrency_limit:
  description:
  entrypoint: test_flows.my_flow:my_flow
  parameters: {}
  work_pool:
    name: my-modal-pool
    work_queue_name:
    job_variables: {"env": {"UV_PROJECT_ENVIRONMENT": "/usr/local"}}
  schedules: []
👀 1
key things here: 1. I needed
UV_PROJECT_ENVIRONMENT
in the env vars like you mentioned set to
/usr/local
2. The entrypoint needs to be a module not a script so
<http://test_flows.my|test_flows.my>_flow:my_flow
I also was shooting myself in the foot with an accidental
__init__.py
file inside of
src
at the same level as my package dir
🫠 1
b
waiting for my job to kick off, very excited!
meow code
same error weirdly
do you have a
.python-version
set? what's yours if so
j
Copy code
[project]
name = "test-flows"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
authors = [
    { name = "<mailto:jake@prefect.io|jake@prefect.io>", email = "<mailto:jake@prefect.io|jake@prefect.io>" }
]
requires-python = ">=3.11"
dependencies = [
    "prefect>=3.1.15",
]

[tool.hatch.build.targets.wheel]
packages = ["src/test_flows"]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
b
so no
.python-version
file?
j
I have one that's 3.11 but it's not committed
b
i bet modal is on 3.12, i have one committed. trying without it
if it works then i bet my other implementation also worked, where i "discover" the version
j
I still am trying to figure out what's up with the prefect/modal compat unfortunately... not sure what is the culprit yet for forcing the downgrade to prefect2
b
yup it's on 3.12 in modal
that's wild re prefect2
noooooo 🫠
🫠 1
j
what's your entrypoint?
b
Copy code
flows.auto_respond:main
but does
uv sync
actually install the package itself?
i'm confused how yours worked in that case
j
Copy code
Installing collected packages: uv
Feb 10  15:28:33.642
Successfully installed uv-0.5.29
Feb 10  15:28:33.839
[notice] A new release of pip is available: 24.2 -> 25.0.1 [notice] To update, run: pip install --upgrade pip
Feb 10  15:28:34.216
Resolved 97 packages in 1ms
Feb 10  15:28:34.246
Building test-flows @ file:///test-flows-main
Feb 10  15:28:34.287
Downloading asyncpg (3.4MiB)
Feb 10  15:28:34.292
Downloading sqlalchemy (3.1MiB) Downloading cryptography (4.0MiB) Downloading pygments (1.2MiB) Downloading apprise (1.3MiB)
Feb 10  15:28:34.297
Downloading prefect (6.0MiB)
Feb 10  15:28:34.304
Downloading pydantic-core (1.9MiB)
Feb 10  15:28:34.870
Downloaded pydantic-core
Feb 10  15:28:35.078
Downloaded asyncpg
Feb 10  15:28:35.142
Downloaded apprise
Feb 10  15:28:35.163
Downloaded cryptography
Feb 10  15:28:35.230
Downloaded sqlalchemy
Feb 10  15:28:35.257
Downloaded pygments
Feb 10  15:28:35.451
Built test-flows @ file:///test-flows-main
Feb 10  15:28:35.524
Downloaded prefect
Feb 10  15:28:35.529
Prepared 62 packages in 1.30s
Feb 10  15:28:36.771
Uninstalled 99 packages in 1.24s
Feb 10  15:28:37.129
Installed 62 packages in 357ms
Feb 10  15:28:39.823
Bytecode compiled 3922 files in 2.69s
Feb 10  15:28:39.828
- aiohttp==3.9.1 - aiosignal==1.3.1 - aiosqlite==0.20.0 + aiosqlite==0.21.0 - aiostream==0.4.4 - alembic==1.13.2 + alembic==1.14.1 - anyio==4.4.0 + anyio==4.8.0 - apprise==1.9.0 + apprise==1.9.2 - asgiref==3.5.2 - asttokens==2.4.1 - asyncpg==0.29.0 + asyncpg==0.30.0 - attrs==24.2.0 + attrs==25.1.0 - cachetools==5.5.0 + cachetools==5.5.1 - certifi==2024.8.30 + certifi==2025.1.31 - charset-normalizer==3.3.2 + charset-normalizer==3.4.1 - click==8.1.7 + click==8.1.8 - cloudpickle==2.2.0 + cloudpickle==3.1.1 - commonmark==0.9.1 - croniter==3.0.3 + croniter==6.0.0 - cryptography==43.0.1 + cryptography==44.0.0 - dateparser==1.2.0 + dateparser==1.2.1 - decorator==5.1.1 + deprecated==1.2.18 - dnspython==2.7.0 - durationpy==0.9 - email-validator==2.2.0 - executing==2.1.0 - fastapi==0.88.0 + fastapi==0.115.8 - fastprogress==1.0.0 - frozenlist==1.4.1 - fsspec==2024.9.0 + fsspec==2025.2.0 - google-auth==2.38.0 - greenlet==3.0.3 + greenlet==3.1.1 - griffe==1.2.0 + griffe==1.5.6 - grpclib==0.4.7 - h2==4.1.0 + h2==4.2.0 - hpack==4.0.0 + hpack==4.1.0 - httpcore==1.0.5 + httpcore==1.0.7 - httpx==0.27.2 + httpx==0.28.1 - humanize==4.10.0 + humanize==4.11.0 - hyperframe==6.0.1 + hyperframe==6.1.0 - idna==3.8 + idna==3.10 - importlib-metadata==4.8.1 + importlib-metadata==8.5.0 - importlib-resources==6.4.5 - ipython==8.27.0 - itsdangerous==2.2.0 - jedi==0.19.1 - jinja2==3.1.4 + jinja2==3.1.5 - jsonschema-specifications==2023.12.1 + jsonschema-specifications==2024.10.1 - kubernetes==31.0.0 - mako==1.3.5 + mako==1.3.9 - markupsafe==2.1.5 + markupsafe==3.0.2 - matplotlib-inline==0.1.7 - multidict==6.0.5 - numpy==2.1.1 + opentelemetry-api==1.30.0 - orjson==3.10.7 + orjson==3.10.15 - packaging==24.1 + packaging==24.2 - parso==0.8.4 - pexpect==4.9.0 - prefect==2.20.15 + prefect==3.2.0 - prometheus-client==0.20.0 + prometheus-client==0.21.1 - prompt-toolkit==3.0.47 - protobuf==5.28.0 - ptyprocess==0.7.0 - pure-eval==0.2.3 - pyasn1==0.6.1 - pyasn1-modules==0.4.1 - pydantic==1.10.18 + pydantic==2.10.6 - pydantic-core==2.23.2 + pydantic-core==2.27.2 - pydantic-extra-types==2.9.0 + pydantic-extra-types==2.10.2 - pydantic-settings==2.4.0 + pydantic-settings==2.7.1 - pygments==2.18.0 + pygments==2.19.1 - python-multipart==0.0.9 + python-socks==2.7.1 - pytz==2024.1 + pytz==2025.1 - readchar==4.2.0 + readchar==4.2.1 - referencing==0.35.1 + referencing==0.36.2 - regex==2024.7.24 + regex==2024.11.6 - rich==12.3.0 + rich==13.9.4 - rpds-py==0.20.0 + rpds-py==0.22.3 - rsa==4.9 - ruamel-yaml==0.18.6 + ruamel-yaml==0.18.10 - ruamel-yaml-clib==0.2.8 + ruamel-yaml-clib==0.2.12 - six==1.16.0 + six==1.17.0 - sqlalchemy==2.0.34 + sqlalchemy==2.0.38 - stack-data==0.6.3 - starlette==0.32.0.post1 + starlette==0.45.3 - tblib==1.7.0 + test-flows==0.1.0 (from file:///test-flows-main) - time-machine==2.15.0 + time-machine==2.16.0 - traitlets==5.14.3 - typeguard==4.3.0 - typer==0.13.1 + typer==0.15.1 - types-certifi==2021.10.8.3 - types-toml==0.10.4 - tzdata==2024.1 + tzdata==2025.1 - urllib3==2.2.2 + urllib3==2.3.0 - uvicorn==0.30.6 + uvicorn==0.34.0 - wcwidth==0.2.13 - websocket-client==1.8.0 - websockets==13.0.1 + websockets==13.1 + wrapt==1.17.2 - yarl==1.10.0 - zipp==3.20.1 + zipp==3.21.0
Feb 10  15:28:39.964
> Running run_shell_script step... > Running run_shell_script step... /usr/local/lib/python3.12/site-packages/test_flows/__init__.py ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'main']
Feb 10  15:28:40.499
> Running run_shell_script step... aiosqlite==0.21.0 alembic==1.14.1 annotated-types==0.7.0 anyio==4.8.0 apprise==1.9.2 asgi-lifespan==2.1.0 asyncpg==0.30.0 attrs==25.1.0 cachetools==5.5.1 certifi==2025.1.31 cffi==1.17.1 charset-normalizer==3.4.1 click==8.1.8 cloudpickle==3.1.1 colorama==0.4.6 coolname==2.2.0 croniter==6.0.0 cryptography==44.0.0 dateparser==1.2.1 Deprecated==1.2.18 docker==7.1.0 exceptiongroup==1.2.2 fastapi==0.115.8 fsspec==2025.2.0 graphviz==0.20.3 greenlet==3.1.1 griffe==1.5.6 h11==0.14.0 h2==4.2.0 hpack==4.1.0 httpcore==1.0.7 httpx==0.28.1 humanize==4.11.0 hyperframe==6.1.0 idna==3.10 importlib_metadata==8.5.0 Jinja2==3.1.5 jinja2-humanize-extension==0.4.0 jsonpatch==1.33 jsonpointer==3.0.0 jsonschema==4.23.0 jsonschema-specifications==2024.10.1 Mako==1.3.9 Markdown==3.7 markdown-it-py==3.0.0 MarkupSafe==3.0.2 mdurl==0.1.2 oauthlib==3.2.2 opentelemetry-api==1.30.0 orjson==3.10.15 packaging==24.2 pathspec==0.12.1 pendulum==3.0.0 prefect==3.2.0 prometheus_client==0.21.1 pycparser==2.22 pydantic==2.10.6 pydantic-extra-types==2.10.2 pydantic-settings==2.7.1 pydantic_core==2.27.2 Pygments==2.19.1 python-dateutil==2.9.0.post0 python-dotenv==1.0.1 python-slugify==8.0.4 python-socks==2.7.1 pytz==2025.1 PyYAML==6.0.2 readchar==4.2.1 referencing==0.36.2 regex==2024.11.6 requests==2.32.3 requests-oauthlib==2.0.0 rfc3339-validator==0.1.4 rich==13.9.4 rpds-py==0.22.3 ruamel.yaml==0.18.10 ruamel.yaml.clib==0.2.12 shellingham==1.5.4 six==1.17.0 sniffio==1.3.1 SQLAlchemy==2.0.38 starlette==0.45.3 test-flows @ file:///test-flows-main text-unidecode==1.3 time-machine==2.16.0 toml==0.10.2 typer==0.15.1 typing_extensions==4.12.2 tzdata==2025.1 tzlocal==5.2 ujson==5.10.0 urllib3==2.3.0 uv==0.5.29 uvicorn==0.34.0 websockets==13.1 wrapt==1.17.2 zipp==3.21.0
Feb 10  15:28:40.630
> Running run_shell_script step... __modal bin boot dev etc home lib lib64 media mnt modal_requirements.txt opt proc root run sbin srv sys test-flows-main tmp usr var
Feb 10  15:28:40.950
20:28:40.945 | INFO | Flow run 'hopeful-squirrel' - 1
Feb 10  15:28:41.169
20:28:41.168 | INFO | Flow run 'hopeful-squirrel' - Finished in state Completed()
Feb 10  15:28:42.106
20:28:42.104 | INFO | prefect.flow_runs.runner - Process for flow run 'hopeful-squirrel' exited cleanly.
test-flows
does seem to be in my lock file fwiw
b
yea i see my package in mine too... no idea why it can't find the module though
j
oh!!
Copy code
prefect_modal.flows.auto_respond:main
b
no in my actual repo there's no
prefect_modal
subpackage
flows is the top level
j
darn
wahts' the wheel target
[tool.hatch.build.targets.wheel]
in pyproject. yaml
Copy code
src/flows
?
b
i'm using setuptools but its definitely correct because i can access it locally
Copy code
[tool.setuptools.packages.find]
where = ["src"]
include = ["app", "modals", "flows"]
j
just to verify 😅 in a totally separate dir and virtual env, independent of prefect and modal you can run: • git clone repo •
pip install uv
• cd cloned repo • uv sync it installs everything correctly and you can run
python -c "from flows.auto_respond import main"
b
yup that worked fine
Copy code
mkdir test-install && cd test-install && git clone <https://github.com/Team/repo> && cd exploration/leasing-copilot && uv sync --extra workflows && uv run python -c "from flows.auto_respond import main"
so i find this curious. If i do just the
uv sync ...
i get
Copy code
ModuleNotFoundError: No module named 'flows'
but if I do
Copy code
uv sync ...
uv pip install --system ...
i get
Copy code
ModuleNotFoundError: No module named 'app.baml_client'
which is my other package, but it also means that flows was able to be installed....
j
I feel like I'm more confused now versus when it didn't work lol. I guess next things I would just verify to completely sure in the modal logs are: •
UV_PROJECT_ENVRIONMENT
is getting set
Copy code
e.g.
=> Step 3: ENV UV_PROJECT_ENVIRONMENT=/usr/local
• flows is getting installed as part of
uv sync
Copy code
e.g.
Built test-flows @ file:///test-flows-main
• running a run shell script step with plain
pip freeze
to verify all your packages are getting into the system python python
Copy code
e.g.
test-flows @ file:///test-flows-main
😂 1
b
1 2 running for 3 now
woahhhh wtf wtf wtf i see something weird
OK we should be all set! Thanks so much Jake!! fun pairing together 🤝 I've updated the repo and the guide. @Trey Gilliland take a look when you can and make sure you're all set too 🙂
🙌 1
t
sweet, i’ll take a look tn/tomorrow
@Ben Epstein @Jake Kaplan got it to work following Ben's guide, thank you guys for your efforts! Maybe this could make it into the prefect docs somewhere? Also you could define that env var in the modal block if you don't want to copy paste each time
b
i think we're trying to get it on the docs!
Also you could define that env var in the modal block if you don't want to copy paste each time (edited)
sorry what do you mean? Which env var am I copy/pasting
🙌 1
t
Instead of defining in job_variables in your prefect.yaml, define it in the modal block (I didn't actually test this) so no
job_variables: { "env": { "UV_PROJECT_ENVIRONMENT": "/usr/local" } }
for each job
b
oh interesting. If you figure out a CLI command for that i'd love to update the guide with it
t
it doesn't look like it's super clean to set work pool env vars from the CLI, would probably involve dumping the base job template to a file with
prefect work-pool get-default-base-job-template [OPTIONS]
and editing it and then creating the workpool from the template with the
prefect work-pool create --base-job-template <path> NAME
I think I will leave as a job variable for now
👍 1