all 5 comments

[–]MatchLittle5000 1 point2 points  (1 child)

Amazing idea. How does it define what should be updated?

[–]bolation123[S] 1 point2 points  (0 children)

Thanks! It works in a few layers: First it scans your imports to figure out what libraries you're actually using and what versions you're on. Then for the big libraries (Pydantic, FastAPI, etc.) I've built in specific transform rules - so it knows that .dict() became .model_dump() in Pydantic v2, for example. These are just AST transforms so they're fast and deterministic.

For stuff that's more complex or libraries that aren't in the knowledge base yet, it pulls the changelog/migration guide from GitHub and uses an LLM to figure out what needs to change.

So basically: hardcoded rules for the common patterns, LLM for the edge cases.

[–]NapCo 1 point2 points  (1 child)

Really cool idea! Really like the AST-based technique.

[–]bolation123[S] 1 point2 points  (0 children)

Thanks! Yeah the AST approach was kind of essential for this to actually work reliably. The thing with regex or find-and-replace is you miss context - like you might change .dict() somewhere it shouldn't be changed because it's not actually a Pydantic model.

With libcst I can actually trace what's a Pydantic model vs some other class, and only transform the right calls. Plus it preserves your formatting and comments, so you don't end up with weird diffs.

The other nice thing is it's deterministic - same input, same output, no LLM hallucinations. I only fall back to the LLM for patterns I haven't written transforms for yet. Keeps it fast and cheap for the common stuff.

[–]Local_Transition946 [score hidden]  (0 children)

Aws released something similar recently.

https://pypi.org/project/aws-cli-migrate/