Commit graph

10395 commits

Author SHA1 Message Date
Paul Gauthier
14289ab44f tweaked --map-tokens help 2023-06-03 18:07:01 -07:00
Paul Gauthier
ef22f44d19 final details on --map-tokens 2023-06-03 18:06:14 -07:00
Paul Gauthier
1f9e9be64d aider: Replaced use_ctags with map_tokens parameter in RepoMap class.
# Context:
USER: update `aider/repomap.py`
ASSISTANT: Please provide the specific changes you would like me to make in the `aider/repomap.py` file.
USER: to use map_tokens not `use_ctags`
ASSISTANT: I will update the `aider/repomap.py` file to use `map_tokens` instead of `use_ctags`.

```python
aider/repomap.py
<<<<<<< ORIGINAL
    def __init__(self, use_ctags=None, root=None, main_model="gpt-4", io=None):
        self.io = io

        if not root:
            root = os.getcwd()
        self.root = root

        self.load_ident_cache()
        self.load_tags_cache()

        if use_ctags is None:
            self.use_ctags = self.check_for_ctags()
        else:
            self.use_ctags = use_ctags

        self.tokenizer = tiktoken.encoding_for_model(main_model)
=======
    def __init__(self, map_tokens=1024, root=None, main_model="gpt-4", io=None):
        self.io = io

        if not root:
            root = os.getcwd()
        self.root = root

        self.load_ident_cache()
        self.load_tags_cache()

        self.map_tokens = map_tokens

        self.tokenizer = tiktoken.encoding_for_model(main_model)
>>>>>>> UPDATED
```

This change replaces the `use_ctags` parameter with the `map_tokens` parameter in the `__init__` method of the `RepoMap` class. Make sure to update any other parts of the code that use `use_ctags` to use `map_tokens` instead.
2023-06-03 17:48:45 -07:00
Paul Gauthier
d996fbe1c3 aider: Updated the Coder class in aider/coder.py to accept the map_tokens parameter and pass it to the RepoMap class instead of use_ctags.
# Context:
USER: I added these *read-write* files: aider/coder.py
ASSISTANT: Great! Now I will modify the `aider/coder.py` file to accept and use the `map_tokens` parameter instead of `use_ctags`.

```python
aider/coder.py
<<<<<<< ORIGINAL
    def __init__(
        self,
        io,
        main_model="gpt-4",
        fnames=None,
        pretty=True,
        show_diffs=False,
        auto_commits=True,
        dirty_commits=True,
        dry_run=False,
        use_ctags=False,
        verbose=False,
        openai_api_key=None,
    ):
        ...
        self.repo_map = RepoMap(use_ctags, self.root, self.main_model, rm_io)
=======
    def __init__(
        self,
        io,
        main_model="gpt-4",
        fnames=None,
        pretty=True,
        show_diffs=False,
        auto_commits=True,
        dirty_commits=True,
        dry_run=False,
        map_tokens=1024,
        verbose=False,
        openai_api_key=None,
    ):
        ...
        self.repo_map = RepoMap(map_tokens, self.root, self.main_model, rm_io)
>>>>>>> UPDATED
```

This change updates the `Coder` class in `aider/coder.py` to accept the `map_tokens` parameter and pass it to the `RepoMap` class instead of `use_ctags`.
2023-06-03 17:47:45 -07:00
Paul Gauthier
15f34e2864 aider: Changed use_ctags to map_tokens parameter in main.py to pass to Coder class.
# Context:
USER: pass it to Coder as map_tokens instead of `use_ctags`
ASSISTANT: I will modify the `aider/main.py` file to pass `map_tokens` to the `Coder` class instead of `use_ctags`.

```python
aider/main.py
<<<<<<< ORIGINAL
    coder = Coder(
        io,
        main_model=args.model,
        fnames=args.files,
        pretty=args.pretty,
        show_diffs=args.show_diffs,
        auto_commits=args.auto_commits,
        dirty_commits=args.dirty_commits,
        dry_run=args.dry_run,
        use_ctags=args.ctags,
        verbose=args.verbose,
        openai_api_key=args.openai_api_key,
    )
=======
    coder = Coder(
        io,
        main_model=args.model,
        fnames=args.files,
        pretty=args.pretty,
        show_diffs=args.show_diffs,
        auto_commits=args.auto_commits,
        dirty_commits=args.dirty_commits,
        dry_run=args.dry_run,
        map_tokens=args.map_tokens,
        verbose=args.verbose,
        openai_api_key=args.openai_api_key,
    )
>>>>>>> UPDATED
```

Now, the `map_tokens` value is passed to the `Coder` class instead of `use_ctags`. Please make sure to update the `Coder` class in `aider/coder.py` to accept and use the `map_tokens` parameter.
2023-06-03 17:47:05 -07:00
Paul Gauthier
dcdad393c8 aider: Changed the --ctags switch into --map-tokens, which is an int that defaults to 1024.
# Context:
USER: change the --ctags switch into --map-tokens, which is an int that defaults to 1024
ASSISTANT: To make this change, I need to modify the `aider/main.py` file. Please make it *read-write* and let me know when it's done.
USER: I added these *read-write* files: aider/main.py
ASSISTANT: Great! I will now modify the `aider/main.py` file to change the `--ctags` switch into `--map-tokens`, which is an int that defaults to 1024.

```python
aider/main.py
<<<<<<< ORIGINAL
    parser.add_argument(
        "--ctags",
        type=lambda x: (str(x).lower() == "true"),
        nargs="?",
        const=True,
        default=None,
        help=(
            "Add ctags to the chat to help GPT understand the codebase (default: check for ctags"
            " executable)"
        ),
    )
=======
    parser.add_argument(
        "--map-tokens",
        type=int,
        default=1024,
        help="Number of tokens to map for code understanding (default: 1024)",
    )
>>>>>>> UPDATED
```

This change replaces the `--ctags` argument with the `--map-tokens` argument, which takes an integer value and defaults to 1024.
2023-06-03 17:46:11 -07:00
Paul Gauthier
05e3d2bfdb Merge branch 'main' into call-graph 2023-06-03 17:43:45 -07:00
paul-gauthier
23f972fb2e
Merge pull request #12 from paul-gauthier/handle-openai-exceptions 2023-06-03 06:31:00 -07:00
Paul Gauthier
5263367af0 added import 2023-06-03 06:23:07 -07:00
Paul Gauthier
2d1fc9f6da aider: Added a new test case test_send_with_retries_connection_error to ensure that send_with_retries handles the ConnectionError correctly and calls time.sleep once before retrying.
# Context:
USER: make another test like that, but raise requests.exceptions.ConnectionError
ASSISTANT: To add another test case for `coder.send_with_retries(model, messages)` that raises `requests.exceptions.ConnectionError`, we will modify the `tests/test_coder.py` file. We will create a new test method called `test_send_with_retries_connection_error` and use `patch` to mock the `openai.ChatCompletion.create` and `time.sleep` methods.

Here's the code change for the new test case:

```python
tests/test_coder.py
<<<<<<< ORIGINAL
if __name__ == "__main__":
    unittest.main()
=======
    @patch("aider.coder.openai.ChatCompletion.create")
    @patch("aider.coder.time.sleep")
    def test_send_with_retries_connection_error(self, mock_sleep, mock_chat_completion_create):
        # Mock the IO object
        mock_io = MagicMock()

        # Initialize the Coder object with the mocked IO and mocked repo
        coder = Coder(io=mock_io, openai_api_key="fake_key")

        # Set up the mock to raise ConnectionError on the first call and return None on the second call
        mock_chat_completion_create.side_effect = [requests.exceptions.ConnectionError("Connection error"), None]

        # Call the send_with_retries method
        coder.send_with_retries("model", ["message"])

        # Assert that time.sleep was called once
        mock_sleep.assert_called_once()

if __name__ == "__main__":
    unittest.main()
>>>>>>> UPDATED
```

This test case will ensure that `send_with_retries` handles the `ConnectionError` correctly and calls `time.sleep` once before retrying.
2023-06-03 06:22:33 -07:00
Paul Gauthier
3bdddf4b36 added import 2023-06-03 06:21:33 -07:00
Paul Gauthier
2a8e5a727c aider: Added a test case for coder.send_with_retries(model, messages) to handle RateLimitError and call time.sleep once.
# Context:
USER: add a test case for coder.send_with_retries(model, messages). it should patch openai.ChatCompletion.create so that on the first call it raises openai.error import RateLimitError. and on the 2nd call it returns None. the test should confirm that time.sleep is called 1 time.
ASSISTANT: To add the test case for `coder.send_with_retries(model, messages)`, we will modify the `tests/test_coder.py` file. We will create a new test method called `test_send_with_retries_rate_limit_error` and use `patch` to mock the `openai.ChatCompletion.create` and `time.sleep` methods.

Here's the code change for the new test case:

```python
tests/test_coder.py
<<<<<<< ORIGINAL
if __name__ == "__main__":
    unittest.main()
=======
    @patch("aider.coder.openai.ChatCompletion.create")
    @patch("aider.coder.time.sleep")
    def test_send_with_retries_rate_limit_error(self, mock_sleep, mock_chat_completion_create):
        # Mock the IO object
        mock_io = MagicMock()

        # Initialize the Coder object with the mocked IO and mocked repo
        coder = Coder(io=mock_io, openai_api_key="fake_key")

        # Set up the mock to raise RateLimitError on the first call and return None on the second call
        mock_chat_completion_create.side_effect = [openai.error.RateLimitError("Rate limit exceeded"), None]

        # Call the send_with_retries method
        coder.send_with_retries("model", ["message"])

        # Assert that time.sleep was called once
        mock_sleep.assert_called_once()

if __name__ == "__main__":
    unittest.main()
>>>>>>> UPDATED
```

This test case will ensure that `send_with_retries` handles the `RateLimitError` correctly and calls `time.sleep` once before retrying.
2023-06-03 06:20:56 -07:00
Paul Gauthier
c27483193b refac 2023-06-03 06:18:45 -07:00
Paul Gauthier
1882cf55db Handle ConnectionError 2023-06-03 06:16:33 -07:00
Paul Gauthier
575c6e2db6 Stop closing 2023-06-02 16:40:21 -07:00
Paul Gauthier
c5ed12e0c3 aider: Replaced shelve with diskcache for persistent dictionary-like object.
# Context:
USER: is there another persistent dictionary besides shelve?
ASSISTANT: Yes, there are several alternatives to `shelve` for creating persistent dictionaries in Python. Some popular options include:

1. `pickle`: A built-in Python module for object serialization and deserialization. You can use it to store dictionaries in binary format and load them back into memory when needed.

2. `json`: Another built-in Python module for working with JSON data. You can use it to store dictionaries in a human-readable format and load them back into memory when needed.

3. `sqlite3`: A built-in Python module for working with SQLite databases. You can use it to create a simple key-value store with SQL queries.

4. `TinyDB`: A lightweight, document-oriented database for Python
^C KeyboardInterrupt
USER: i want something which presents as a dict though
ASSISTANT: In that case, you can use the `sqlite3` module to create a custom class that behaves like a dictionary. Here's an example of how you can create a persistent dictionary using SQLite:

```python
import sqlite3

class
^C KeyboardInterrupt
USER: i don't want to build something. i want an existing package.
ASSISTANT: I apologize for the confusion. In that case, you can use the `diskcache` package, which provides a dictionary-like interface for disk-based storage. It is easy to use and has good performance.

To install `diskcache`, you can use pip:

```
pip install diskcache
```

Here's an example of how to use `diskcache`:

```python
from diskcache import Cache

cache = Cache('path/to/cache/directory')

# Set a key-value pair
cache['key'] = 'value'

# Get the value for a key
value = cache['key']

# Check if a key is in the cache
if 'key' in cache:
    print('Key exists')

# Delete a key-value pair
del cache['key']

# Close the cache when done
cache.close()
```

This will create a persistent dictionary-like object using the `diskcache` package. The data will be stored on disk in the specified directory, and you can use the standard dictionary methods to interact with it.
USER: great. use that.
ASSISTANT: To use `diskcache` in the `aider/repomap.py` file, you'll need to replace the `shelve` usage with `diskcache`. Here are the changes you need to make:

1. Install `diskcache` using pip:

```
pip install diskcache
```

2. Modify the `aider/repomap.py` file:

```python
aider/repomap.py
<<<<<<< ORIGINAL
import colorsys
import json
import os
import random
import subprocess
import sys
import tempfile
from collections import Counter, defaultdict

# import shelve
=======
import colorsys
import json
import os
import random
import subprocess
import sys
import tempfile
from collections import Counter, defaultdict
from diskcache import Cache

>>>>>>> UPDATED
```

```python
aider/repomap.py
<<<<<<< ORIGINAL
    IDENT_CACHE_FILE = ".aider.ident.cache"
    TAGS_CACHE_FILE = ".aider.tags.cache"
=======
    IDENT_CACHE_DIR = ".aider.ident.cache"
    TAGS_CACHE_DIR = ".aider.tags.cache"
>>>>>>> UPDATED
```

```python
aider/repomap.py
<<<<<<< ORIGINAL
    def load_tags_cache(self):
        self.TAGS_CACHE = dict()  # shelve.open(self.TAGS_CACHE_FILE)

    def save_tags_cache(self):
        # self.TAGS_CACHE.sync()
        pass

    def load_ident_cache(self):
        self.IDENT_CACHE = dict()  # shelve.open(self.IDENT_CACHE_FILE)

    def save_ident_cache(self):
        # self.IDENT_CACHE.sync()
        pass
=======
    def load_tags_cache(self):
        self.TAGS_CACHE = Cache(self.TAGS_CACHE_DIR)

    def save_tags_cache(self):
        self.TAGS_CACHE.close()

    def load_ident_cache(self):
        self.IDENT_CACHE = Cache(self.IDENT_CACHE_DIR)

    def save_ident_cache(self):
        self.IDENT_CACHE.close()
>>>>>>> UPDATED
```

These changes will replace the usage of `shelve` with `diskcache` for the `IDENT_CACHE` and `TAGS_CACHE` dictionaries. Make sure to install the `diskcache` package before running the updated code.
2023-06-02 16:36:03 -07:00
Paul Gauthier
100deccbff Merge branch 'main' into call-graph 2023-06-02 16:30:26 -07:00
Paul Gauthier
4f1e1181c4 Tweaked prompt 2023-06-02 16:30:19 -07:00
Paul Gauthier
eb38a470a3 Merge branch 'main' into call-graph 2023-06-02 13:28:42 -07:00
Paul Gauthier
f5b68a4f69 personalization 2023-06-02 13:28:40 -07:00
Paul Gauthier
68434cbaa1 cleanup 2023-06-02 13:24:29 -07:00
Paul Gauthier
47293c5ba9 removed graph 2023-06-02 13:22:25 -07:00
Paul Gauthier
36f21fcfb6 refac 2023-06-02 13:22:00 -07:00
Paul Gauthier
360335f624 small repo map 2023-06-02 13:09:52 -07:00
Paul Gauthier
bd81ef6201 added newline to prompt 2023-06-02 12:54:28 -07:00
Paul Gauthier
ac21a4a992 wip 2023-06-02 12:54:06 -07:00
Paul Gauthier
777a36d366 aider: Modified get_ranked_tags_map to use binary search.
# Context:
USER: make `get_ranked_tags_map` use binary search
ASSISTANT: To modify the `get_ranked_tags_map` function to use binary search, follow these steps:

1. Replace the for loop with a binary search implementation.
2. Update the loop to calculate the middle index and generate the tree with the middle index.
3. Check if the number of tokens is less than `max_map_tokens`. If it is, update the lower bound. Otherwise, update the upper bound.
4. Continue the binary search until the lower bound is greater than or equal to the upper bound.

Here's the updated `get_ranked_tags_map` function:

```python
aider/repomap.py
<<<<<<< ORIGINAL
    def get_ranked_tags_map(self, fnames):
        # 1/4 of gpt-4's context window
        max_map_tokens = 2048

        ranked_tags = self.get_ranked_tags(fnames)
        num_tags = len(ranked_tags)

        for i in range(num_tags, 0, -1):
            tree = to_tree(ranked_tags[:i])
            num_tokens = self.token_count(tree)
            dump(i, num_tokens)
            if num_tokens < max_map_tokens:
                return tree
=======
    def get_ranked_tags_map(self, fnames):
        # 1/4 of gpt-4's context window
        max_map_tokens = 2048

        ranked_tags = self.get_ranked_tags(fnames)
        num_tags = len(ranked_tags)

        lower_bound = 0
        upper_bound = num_tags
        best_tree = None

        while lower_bound <= upper_bound:
            middle = (lower_bound + upper_bound) // 2
            tree = to_tree(ranked_tags[:middle])
            num_tokens = self.token_count(tree)

            if num_tokens < max_map_tokens:
                best_tree = tree
                lower_bound = middle + 1
            else:
                upper_bound = middle - 1

        return best_tree
>>>>>>> UPDATED
```
2023-06-02 12:46:03 -07:00
Paul Gauthier
1bbe8e63af find the best tags map that fits in max_tokens 2023-06-02 12:44:46 -07:00
Paul Gauthier
f92249a7ea refac 2023-06-02 12:40:40 -07:00
Paul Gauthier
e3aed997bc ranked_tags 2023-06-02 12:34:28 -07:00
Paul Gauthier
6e51675072 graph 2023-06-02 11:25:10 -07:00
Paul Gauthier
903d318a8a no weights 2023-06-02 10:55:43 -07:00
Paul Gauthier
746f4ccb56 rank flows evenly *out* from a node 2023-06-01 14:58:15 -07:00
Paul Gauthier
33f649dbbd noop 2023-06-01 14:54:04 -07:00
Paul Gauthier
594c4b600b cleanup 2023-06-01 13:24:52 -07:00
Paul Gauthier
bb0d58020c use in-edges 2023-06-01 13:12:15 -07:00
Paul Gauthier
915a9132de working ranked defs 2023-06-01 11:26:49 -07:00
Paul Gauthier
27566353a6 ssh 2023-06-01 10:57:48 -07:00
Paul Gauthier
11a7f9a80a aider: Calculated the sum of the weights of every inbound edge to each node and stored the result in the inbound_weights dictionary.
# Context:
USER: do the todo
ASSISTANT: I see a "todo" comment in the `aider/repomap.py` file. To complete the task, we need to calculate the sum of the weights of every inbound edge to each node in the graph. Here's how to do it:

```python
aider/repomap.py
<<<<<<< ORIGINAL
    for node in G.nodes:
        # todo: sum up the weight of every inbound edge to node
        pass
=======
    inbound_weights = {}
    for node in G.nodes:
        inbound_weights[node] = sum(data["weight"] for _, _, data in G.in_edges(node, data=True))
>>>>>>> UPDATED
```

This code calculates the sum of the weights of every inbound edge to each node and stores the result in the `inbound_weights` dictionary.
2023-06-01 10:56:41 -07:00
Paul Gauthier
d913d08f39 Merge branch 'main' into call-graph 2023-06-01 10:55:49 -07:00
Paul Gauthier
d1f05ca18a Handle io=none 2023-06-01 10:55:39 -07:00
Paul Gauthier
1aa8c3983d wip 2023-06-01 10:54:43 -07:00
Paul Gauthier
3c9991b38f Merge branch 'main' into call-graph 2023-06-01 09:07:26 -07:00
Paul Gauthier
7bc26e6762 Added --verbose support to repomap 2023-06-01 09:04:08 -07:00
Paul Gauthier
6472c152b4 Merge branch 'main' into call-graph 2023-06-01 08:59:00 -07:00
Paul Gauthier
e43a48ee10 patch check_model_availability in tests 2023-06-01 08:58:35 -07:00
Paul Gauthier
2cc87a4afe aider: Added patch to setUp method and stored mock object as an instance variable.
# Context:
USER: how can i move the patch to setUp so that it is active for all tests?
ASSISTANT: To move the patch to the `setUp` method and make it active for all tests, you can use the `patch` as a context manager in the `setUp` method and store the mock object as an instance variable. Then, in the `tearDown` method, you can stop the patch. Here's how to do it:

```python
tests/test_main.py
<<<<<<< ORIGINAL
    def setUp(self):
        os.environ["OPENAI_API_KEY"] = "deadbeef"
        self.original_cwd = os.getcwd()
        self.tempdir = tempfile.mkdtemp()
        os.chdir(self.tempdir)

    def tearDown(self):
        os.chdir(self.original_cwd)
        shutil.rmtree(self.tempdir)
=======
    def setUp(self):
        os.environ["OPENAI_API_KEY"] = "deadbeef"
        self.original_cwd = os.getcwd()
        self.tempdir = tempfile.mkdtemp()
        os.chdir(self.tempdir)
        self.patcher = patch("aider.main.Coder.check_model_availability")
        self.mock_check = self.patcher.start()
        self.mock_check.return_value = True

    def tearDown(self):
        os.chdir(self.original_cwd)
        shutil.rmtree(self.tempdir)
        self.patcher.stop()
>>>>>>> UPDATED
```

Now the patch is active for all tests, and you can remove the `with` statement from the `test_main_with_empty_dir_no_files_on_command` method:

```python
tests/test_main.py
<<<<<<< ORIGINAL
    def test_main_with_empty_dir_no_files_on_command(self):
        with patch("aider.main.Coder.check_model_availability") as mock_check:
            mock_check.return_value = True

            pipe_input = create_input(StringIO(""))
            main([], input=pipe_input, output=DummyOutput())
            pipe_input.close()
=======
    def test_main_with_empty_dir_no_files_on_command(self):
        pipe_input = create_input(StringIO(""))
        main([], input=pipe_input, output=DummyOutput())
        pipe_input.close()
>>>>>>> UPDATED
```
2023-06-01 08:54:15 -07:00
Paul Gauthier
a5414473d9 wip 2023-06-01 08:52:21 -07:00
Paul Gauthier
99693fe614 aider: Modified setUp method to patch check_model_availability with MagicMock object that always returns True.
# Context:
USER: the patch in `setUp` is not correcting forcing a return of true
ASSISTANT: To ensure that the patch in `setUp` correctly forces a return of `True`, you need to modify the `setUp` method in the `TestMain` class in the `tests/test_main.py` file. Here's the change:

```python
tests/test_main.py
<<<<<<< ORIGINAL
    def setUp(self):
        os.environ["OPENAI_API_KEY"] = "deadbeef"
        self.original_cwd = os.getcwd()
        self.tempdir = tempfile.mkdtemp()
        os.chdir(self.tempdir)

        with patch("aider.main.Coder.check_model_availability") as self.mock_check:
            self.mock_check.return_value = True
        super().setUp()
=======
    def setUp(self):
        os.environ["OPENAI_API_KEY"] = "deadbeef"
        self.original_cwd = os.getcwd()
        self.tempdir = tempfile.mkdtemp()
        os.chdir(self.tempdir)

        self.mock_check = MagicMock(return_value=True)
        with patch("aider.main.Coder.check_model_availability", new=self.mock_check):
            super().setUp()
>>>>>>> UPDATED
```

This change ensures that the `check_model_availability` method is patched with a `MagicMock` object that always returns `True`.
2023-06-01 08:50:55 -07:00
Paul Gauthier
4774f0518c wip 2023-06-01 08:49:55 -07:00