# Upgrade Python Dependencies in a Streamlit App

Yes. Each week an agent upgrades a Streamlit app's requirements with pip, runs AppTest and ruff after each group, and opens one pull request.

[Run this on Rush](https://cloud.getrush.ai/run/upgrade-python-dependencies/streamlit/), in a Project that has your repository.

## What the agent does

The agent starts with the requirement files, because a Streamlit app rarely has a lockfile and the pins tell it which packages are free to move. In streamlit/llm-examples, `requirements.txt` has range pins on `streamlit>=1.28`, `openai>=1.2`, `anthropic>=0.3.0` and `trubrics>=1.4.3`, bare names for `duckduckgo-search` and `streamlit-feedback`, and two exact pins that belong together: `langchain==0.0.350` and `langchain-community==0.0.3`. `requirements-dev.txt` pins `black==23.3.0`, `mypy==1.4.1` and `pre-commit==3.3.3` exactly, with `watchdog` and `pytest` unpinned. There is no `pyproject.toml` and no lockfile, so the installed set is whatever pip resolves on the day.

Then it reads what the checks are. `app_test.py` holds two tests on `streamlit.testing.v1.AppTest`: one runs `Chatbot.py`, patches `openai.resources.chat.Completions.create` and checks the assistant message that comes back; the other runs `pages/3_Langchain_Quickstart.py` and patches `langchain.llms.OpenAI.__call__`. `.github/workflows/app-testing.yml` runs on Python 3.11 with `streamlit/streamlit-app-action`, pointed at `Chatbot.py`, with `ruff: true` and pytest writing a JUnit report. So the gate for every bump is `pytest -v`, `ruff check .` and each page starting under `AppTest.from_file(...).run()` with no exception.

The two exact pins decide the shape of the run. `pages/2_Chat_with_search.py` imports `initialize_agent`, `StreamlitCallbackHandler`, `ChatOpenAI` and `DuckDuckGoSearchRun` from the top-level `langchain` package, and `pages/3_Langchain_Quickstart.py` and `pages/4_Langchain_PromptTemplate.py` import `OpenAI` from `langchain.llms`. Later LangChain releases moved those classes into `langchain_openai` and `langchain_community`, which also changes the patch target in `app_test.py`. A LangChain major is therefore a code port across three pages and the test file, not a version bump; the agent keeps the pair pinned by default, tries the port in a separate commit, and keeps it only if the suite passes. `pages/1_File_Q&A.py` uses the Anthropic SDK's text-completions call with `anthropic.HUMAN_PROMPT` and the model name `claude-v1`; a newer SDK still installs, but the agent notes in the pull request that the call targets a retired model and leaves rewriting it to a separate task.

The rest moves in groups. `streamlit` and `openai` go first, together, since the tests exercise both; then `anthropic`, `trubrics`, `streamlit-feedback` and `duckduckgo-search`, which the tests only import; then the dev tools, whose bumps change what `black` and `mypy` complain about rather than what runs, with `pre-commit run --all-files` as their check. After each group the agent runs `pytest -v`, `ruff check .` and an `AppTest` start of all six scripts. A group that passes is a commit; a group that fails is reverted and its error kept. The edited `requirements.txt` keeps the file's style, raising a range's floor only where the code needs the newer API and leaving exact pins exact.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Upgrade this Streamlit app's Python dependencies with pip in groups: streamlit and openai together, then the other runtime packages, then the dev tools in requirements-dev.txt, then each exact-pinned package on its own. Keep langchain and langchain-community in step; if moving them needs import changes in the pages or in app_test.py, make that port in its own commit and keep it only if everything passes. After each group run pytest -v, ruff check ., and start every script with streamlit.testing.v1.AppTest and assert no exception. Keep a group only if everything passes; otherwise revert it and record the error. Edit requirements.txt and requirements-dev.txt in their existing style. Open one pull request with one commit per group, one line per bump with old and new versions, and every upgrade left out with the reason.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a pull request that changes `requirements.txt` and `requirements-dev.txt`, in one commit per group that passed, plus a separate commit for the LangChain port if it succeeded, touching the three pages that import from `langchain` and the patch target in `app_test.py`. Nothing else in the app changes.

The description lists each bump with the version pip resolved before and after, the upgrades left out with the import error or failing assertion that excluded them, and the result lines of `pytest -v`, `ruff check .` and the `AppTest` start of each script. The trajectory keeps the full output for every group, including the reverted ones.

Before merging, a reviewer checks that `langchain` and `langchain-community` moved together or not at all, that a range pin was raised only where the description names the API that needed it, and that the File Q&A page's note about the retired model is tracked somewhere rather than lost.

## Run it on every Monday morning

Create an automation in the Project with this prompt and a `schedule` trigger (`cron` `0 9 * * 1` in your `timezone`). Each week the run opens one pull request with that week's bumps, or none when nothing moved. Because each group is its own commit, dropping one bump before merge is a revert of one commit.

To have the link reach your server, start the weekly run from your own scheduler instead, with `POST /o/{org}/sessions` and an API key that has a webhook URL: Rush then POSTs `run.completed` with the `session_url` when the turn ends, and a handler on your side can post the pull request link where the app's maintainers read. A run in which no group passed opens no pull request and says why in its result.

## Why run it on Rush?

A Streamlit app with range pins and no lockfile drifts silently: a fresh deploy picks up whatever pip resolves, and the first sign of a breaking LangChain release is a page that fails to import in production. On Rush the agent resolves the new set in a sandbox, runs the `AppTest` suite and starts every page after each group, reverts what fails, and keeps the output, so the pull request that reaches you already passed and the failures are written down.

Starting six scripts headless under `AppTest` after every group is the kind of check a bot pull request never does. The record shows each start and its result, and the same request runs on every Streamlit app you maintain at once.

## Frequently asked questions

### The tests mock the model calls. How does it know an upgrade works?

The tests prove imports, widget wiring and the response path. The agent adds a start of every script under `AppTest`, which catches an import that moved or a widget argument that was removed. A model call that only fails with a real key is outside what the sandbox can prove, and the pull request says so.

### Will it move LangChain off 0.0.350?

Only in its own commit and only if the port passes. The pages import `ChatOpenAI`, `OpenAI`, `DuckDuckGoSearchRun` and `StreamlitCallbackHandler` from the old top-level paths, and `app_test.py` patches one of them. Moving the pair means changing those imports and the patch target; if that works, the commit stays, and if not, the pair stays pinned and the error is in the description.

### There is no lockfile. What does the pull request pin?

It edits the files as they are: a range stays a range, with its floor raised only where the code needs the newer API, and an exact pin stays exact at the new version. The resolved versions the suite passed with are in the description so a reader can reproduce the set.

### Does it run on Python 3.11 like CI?

Yes. The workflow pins `python-version: '3.11'`, so the sandbox uses 3.11 to resolve and test, and an upgrade that drops 3.11 support is left out with that reason.

### Can I exclude a package?

Name it in the prompt and the agent leaves it at its current pin and says so. Packages that need a real API key to exercise, like `trubrics`, are candidates to exclude if their import check is not enough for you.
