Skip to main content

Unvibe: Generate code that passes unit-tests

Installing via pip and running PyPI - Python Version PyPI - Version

[//]: # (The solution to Vibe Coding not scaling to large projects is just to write Unit-Tests, then use a tool to generate many implementations until if finds one that pass all the tests (check Unvibe on GitHub). Basically you vibe the tests, and Unvibe the code.)

Unvibe quickly generates many alternative implementations for functions and classes you annotate with @ai, and re-runs your unit-tests until it finds a correct implementation.

This approach has been demonstrated in research and in practice to produce much better results than simply using code-generation alone. For more details, read the related article: Unvibe: Generate code that passes Unit-tests..

It's particularly effective on large projects with decent test coverage.

It works with most AI providers: local Ollama, OpenAI, DeepSeek, Claude, Gemini,

Unvibe diagram

Install

Just add unvibe as a dependency to your project:

pip install unvibe

Example

First define a new function in your existing Python project. Then annotate it with @ai: Let's implement a Lisp interpreter in Python with Unvibe. We start with creating a lisp.py:

from unvibe import ai


@ai
def lisp(expr: str) -> bool:
    """A simple lisp interpreter compatible with Python lists and functions"""
    pass

Now, let's write a few unit-tests, to define how the function should behave. In test_list.py:

import unvibe
from lisp import lisp


# You can also inherit unittest.TestCase, but unvibe.TestCase provides a better reward function
class LispInterpreterTestClass(unvibe.TestCase):
    def test_calculator(self):
        self.assertEqual(lisp("(+ 1 2)"), 3)
        self.assertEqual(lisp("(* 2 3)"), 6)

    def test_nested(self):
        self.assertEqual(lisp("(* 2 (+ 1 2))"), 6)
        self.assertEqual(lisp("(* (+ 1 2) (+ 3 4))"), 21)

    def test_list(self):
        self.assertEqual(lisp("(list 1 2 3)"), [1, 2, 3])

    def test_call_python_functions(self):
        self.assertEqual(lisp("(list (range 3)"), [0, 1, 2])
        self.assertEqual(lisp("(sum (list 1 2 3)"), 6)

Now, we can use Unvibe to search for a valid implementation that passes all the tests:

$ python -m unvibe lisp.py test_lisp.py

The library will re-run the tests and generate many alternatives, and keep exploring the ones that pass more tests, while feeding back the test errors to the LLM. In the end you will find a new file called unvibe_lisp.py with a valid implementation:

# Unvibe Execution output.
# This implementation passed all tests
# Score: 1.0
# Passed assertions: 7/7 


def lisp(exp):
    def tokenize(exp):
        return exp.replace('(', ' ( ').replace(')', ' ) ').split()

    def parse(tokens):
        if len(tokens) == 0:
            raise SyntaxError('Unexpected EOF')
        token = tokens.pop(0)
        if token == '(':
            L = []
            while tokens[0] != ')':
                L.append(parse(tokens))
            tokens.pop(0)  # Remove ')'
            return L
        elif token == ')':
            raise SyntaxError('Unexpected )')
        else:
            try:
                return int(token)
            except ValueError:
                return token

    def evaluate(x):
        if isinstance(x, list):
            op = x[0]
            args = x[1:]
            if op == '+':
                return sum(evaluate(arg) for arg in args)
            elif op == '*':
                result = 1
                for arg in args:
                    result *= evaluate(arg)
                return result
            elif op == 'list':
                return [evaluate(arg) for arg in args]
            else:
                # Call Python functions
                return globals()[op](*[evaluate(arg) for arg in args])
        return x

    tokens = tokenize(exp)
    return evaluate(parse(tokens))

Setup & Configuration

$ pip install unvibe

Write in your project folder a .unvibe.toml config file.

# For example, to use Claude:
[ai]
provider = "claude"
api_key = "sk-..."
model = "claude-3-5-haiku-latest"
max_tokens = 5000

# Or, to use a local Ollama:
[ai]
provider = "ollama"
model = "deepseek-r1:8b"
host = "http://localhost:11434"

# To use OpenAI or DeepSeek API:
[ai]
provider = "openai"
base_url = "https://api.deepseek.com"
api_key = "sk-..."
temperature = 0.0
max_tokens = 1024

# To Use Gemini API:
[ai]
provider = "gemini"
api_key = "..."
model = "gemini-2.0-flash"

# Advanced Parameters to tune the search: 
[search]
initial_spread = 10     # How many random tries to make at depth=0.
random_spread = 2       # How many random tries to make before selecting the best move.
max_depth = 30          # Maximum depth of the search tree.
max_temperature = 1     # Tries random temperature, up to this value.
max_minutes = 60        # Stop after 60 minutes of search.
                        # Some models perform better at lower temps, in general
                        # Higher temperature = more exploration.
cache = true            # Caches AI responses to a local file to speed up re-runs and
                        # save money.

Research

Similar approaches have been explored in various research papers from DeepMind and Microsoft Research:

Related Article

For more information, check the original article: Unvibe: Generate code that passes unit-tests

Metadata

Release files for unvibe 0.1.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for unvibe 0.1.5
File Size Uploaded Provenance Actions
unvibe-0.1.5.tar.gz 20.2 kB Without provenance Details for unvibe-0.1.5.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for unvibe 0.1.5
File Interpreter ABI Platform Actions
unvibe-0.1.5-py3-none-any.whl Python 3 none any Details for unvibe-0.1.5-py3-none-any.whl

Total release size: 42.5 kB

Release files / unvibe-0.1.5.tar.gz

Download URL unvibe-0.1.5.tar.gz
Size 20.2 kB
Tags Source
SHA-256 checksum
How to use checksums
2856234396d75a698bbe22dc22fea5d4c6b8cb35116aff2a3e3f130f8938bd03
BLAKE2b-256 checksum
How to use checksums
34ba404977618ddbfd683b15d45954f1ee54dbf79b893cfe71f3427dce27ecf2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.0.0 CPython/3.11.11 Darwin/23.6.0

Release files / unvibe-0.1.5-py3-none-any.whl

Download URL unvibe-0.1.5-py3-none-any.whl
Size 22.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
119f829de4060d906fa50709800e0deaf8ea784ce824f71c3a679ab78fc167cd
BLAKE2b-256 checksum
How to use checksums
8ffa3dd7e9002768a978bd4bdc30f8a48d9c1779cb0634c705e4da075ece9225
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.0.0 CPython/3.11.11 Darwin/23.6.0

Security for unvibe 0.1.5

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page