A natural language debugger.
Project description
rubber duck debugging: a method of debugging code by articulating a problem in spoken or written natural language. The name is a reference to a story in the book The Pragmatic Programmer in which a programmer would carry around a rubber duck and debug their code by forcing themselves to explain it, line-by-line, to the duck. [1]
robo duck debugging: a bit like rubber duck debugging, but the duck talks back.
About
Have you ever wanted to ask your program why it's not working?
Many AI-powered dev tools help you write boilerplate more quickly, but the hardest and most time-consuming part of programming is often the last mile. Roboduck's goal is to help you understand and fix those bugs. It essentially embeds an LLM (large language model) in the Python interpreter, providing drop-in natural language replacements for Python's standard approaches to:
- debugging
- error handling
- logging
Quickstart
Install
pip install roboduck
API Key Setup
You need an openai API key to begin using roboduck. Once you have an account (sign up here), you can visit https://platform.openai.com/account/api-keys to retrieve your key. Your simplest option is then to call roboduck.set_openai_api_key(api_key, update_config_=True)
which essentially does the following:
mkdir ~/.roboduck
echo "openai_api_key: your_api_key" > ~/.roboduck/config.yaml
Manually setting an OPENAI_API_KEY environment variable also works.
Roboduck does not store your API key or collect any usage data.
Debugger
We provide a natural language equivalent of python's built-in breakpoint
function. Once you're in an interactive session, you can use the standard pdb commands to navigate your code (cmd+f "debugger commands" here. TLDR: type n
to execute the next line, a variable name to view its current value, or q
to quit the debugging session). However, you can also type a question like "Why do we get an index error when j changes from 3 to 4?" or "Why does nums have three 9s in it when the input list only had one?". Concretely, any time you type something including a question mark, an LLM will try to answer. This is not just performing static analysis - the LLM can access information about the current state of your program.
from roboduck import duck
def bubble_sort(nums):
for i in range(len(nums)):
for j in range(len(nums)):
if nums[j] > nums[j + 1]:
nums[j + 1] = nums[j]
nums[j] = nums[j + 1]
duck() # <--------------------------- instead of breakpoint()
return nums
nums = [3, 1, 9, 2, 1]
bubble_sort(nums)
Errors
Roboduck is also good at explaining error messages. Importing the errors module automatically enables optional error explanations. errors.disable()
reverts to python's regular behavior on errors. errors.enable()
can be used to re-enable error explanations or to change settings. For example, setting auto=True automatically explains all errors rather than asking the user if they want an explanation (y/n) when an error occurs (this is probably excessive for most use cases, but you're free to do it).
from roboduck import errors
data = {'x': 0}
y = data.x
errors.disable()
y = data.x
errors.enable(auto=True)
y = data.x
Jupyter Magic
Jupyter provides a %debug
magic that can be used after an error occurs to enter a postmortem debugging session. Roboduck's %duck
magic works similarly, but with all of our debugging module's conversational capabilities:
# cell 1
from roboduck import magic
nums = [1, 2, 3]
nums.add(4)
# cell 2
%duck
Logging
Roboduck also provides a logger that can write to stdout and/or a file. Whenever you log an Exception object, an LLM will try to diagnose and suggest a fix for the problem. (Unlike the debug module, the logger does not type responses live because we assume logs will typically be viewed after the fact.)
from roboduck import logging
logger = logging.getLogger(path='/tmp/log.txt')
data = {'x': 0}
try:
x = data.x
except Exception as e:
logger.error(e)
CLI
You can also run a python script with error explanations enabled:
duck my_script.py
Run duck --help
for more info.
Usage Advice
Language models are not infallible. You should not blindly assume that roboduck's code snippets are flawless or that its explanations are a source of unimpeachable truth. But that's kind of the whole reason roboduck is useful - if LLMs were perfectly reliable, humans wouldn't need to write code at all. We could simply generate it, ./deploy.sh
, and call it a day. Maybe we'll get there eventually but in the meantime, I believe LLMs are best viewed as tools to augment human thought. It's ultimately still up to you to assess and make use of what they tell you.
It comes back to the name of the library. Sure, as a pun it only kind of makes sense, but it's a good mental cue. Conversing with rubber ducks isn't an effective debugging strategy because bath toys are brilliant programmers - the practice just encourages you to hone in on what the problem is, what you understand and what you don't, what would need to be true for your program to function correctly. Roboduck obviously takes a more active role in the conversation, but that mindset is still useful.
Contributing
To create a virtual environment and install relevant packages:
make dev_env
To run unit tests:
make test
To rebuild the docs locally:
make docs
Start of auto-generated file data.
Last updated: 2023-06-02 21:08:08
File | Summary | Line Count | Last Modified | Size |
---|---|---|---|---|
__init__.py | _ | 17 | 2023-06-01 23:26:46 | 548.00 b |
config.py | Allow us to easily read from and write to roboduck's config file. Roboduck creates a config file at `~/.roboduck/config.yaml`. This currently supports only two fields: - `openai_api_key`: See the [Quickstart](https://hdmamin.github.io/roboduck/) for setup help. - `model_name` (optional): Roboduck is configured to use gpt-4o-mini by default. This field lets you change that (e.g. to gpt-4). If present in the config file, this will take priority over any model_name field specified in a chat template (e.g. our [default debug prompt template](https://github.com/hdmamin/roboduck/blob/7ff904972921fd3f82b8b9fd862c4ffc7b61aee4/lib/roboduck/prompts/chat/debug.yaml#L2)). You can view valid options with `roboduck.available_models()`. You can still override the config default by manually passing a value into a function, e.g. `duck(model_name='gpt-4-32k')`. You can manually edit your config file or use a command like `roboduck.update_config(model_name='gpt-4')`. Passing in a value of None (e.g. `roboduck.update_config(model_name=None)`) will delete that field from your config file. |
181 | 2023-05-23 22:09:40 | 7.41 kb |
debug.py | A conversational debugger and drop-in replacement for pdb. Python's default interactive debugging session is already a crude conversation with your program or interpreter, in a sense - this just lets your program communicate to you more effectively. Quickstart ---------- Here's a broken version of bubble sort that places a `duck()` call on the second to last line where you might normally call `breakpoint()`. ``` from roboduck import duck def bubble_sort(nums): for i in range(len(nums)): for j in range(len(nums) - 1): if nums[j] > nums[j + 1]: nums[j + 1] = nums[j] nums[j] = nums[j + 1] duck() # <--------------------------- instead of breakpoint() return nums nums = [3, 1, 9, 2, 1] bubble_sort(nums) ``` |
571 | 2023-05-31 00:16:13 | 22.98 kb |
decorators.py | Miscellaneous decorators used throughout the library. | 305 | 2023-05-29 15:21:23 | 10.86 kb |
errors.py | Errors that explain themselves! Or more precisely, errors that are explained to you by a gpt-esque model. Simply importing this module will change python's default behavior when it encounters an error. Quickstart ---------- Importing the errors module automatically enables optional error explanations. `disable()` reverts to python's regular behavior on errors. `enable()` can be used to re-enable error explanations or to change settings. For example, setting auto=True automatically explains all errors rather than asking the user if they want an explanation (y/n) when an error occurs. ``` from roboduck import errors data = {'x': 0} y = data.x errors.disable() y = data.x errors.enable(auto=True) y = data.x ``` |
279 | 2023-05-29 16:15:02 | 11.44 kb |
ipy_utils.py | Functions related to loading, saving, or otherwise working with ipython sessions or jupyter notebooks. |
186 | 2023-05-24 21:37:48 | 5.70 kb |
logging.py | Logger that attempts to diagnose and propose a solution for any errors it is asked to log. Unlike our debugger and errors modules, explanations are not streamed because the intended use case is not focused on live development. Quickstart ---------- ``` from roboduck import logging logger = logging.getLogger(path='/tmp/log.txt') data = {'x': 0} try: x = data.x except Exception as e: logger.error(e) ``` |
158 | 2023-05-29 16:15:02 | 6.33 kb |
magic.py | GPT-powered rough equivalent of the `%debug` Jupyter magic. After an error occurs, just run %duck in the next cell to get an explanation. This is very similar to using the errors module, but is less intrusive - you only call it when you want an explanation, rather than having to type y/n after each error. We also provide `paste` mode, which attempts to paste a solution into a new code cell below, and `interactive` mode, which throws you into a conversational debugging session (technically closer to the original `%debug` magic functionality. Quickstart ---------- ``` # cell 1 from roboduck import magic nums = [1, 2, 3] nums.add(4) ``` ``` # cell 2 %duck ``` |
127 | 2023-05-30 22:28:43 | 5.18 kb |
utils.py | Utility functions used by other roboduck modules. | 420 | 2023-05-27 21:32:29 | 15.14 kb |
End of auto-generated file data. Do not add anything below this.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.