<type>documentation</type>
<domain>ai</domain>
<id>ai</id>
<title>AI Assisted Development</title>
<date>$Date: 2006-05-24 22:53:07 -0400 (Wed, 24 May 2006) $</date>
<version>$Revision: 13098 $</version>
<product>pro+</product>

.. note::

  This page gives some examples of the kinds of tasks you can complete with AI assisted
  development in Wing Pro.  For a quicker introduction to using AI-assisted development 
  in Wing, see the `AI Quick Start Guide <ref:ai/quickstart>`__.
  
Wing Pro's Artificial Intelligence (AI) capabilities can be used to suggest new code at the current
insertion point, modify and write new code according to a description of its intended
functionality and design, and chat iteratively about existing code or a development task.

AI-assisted development is an extremely powerful and productive paradigm that can replace
manual development with AI-assisted generation or editing of larger chunks of code. Your
role changes to one of directing an intelligent assistant capable of completing a wide
range of programming tasks in relatively short periods of time. Instead of typing out code
by hand every step of the way, you are essentially directing someone else to work through
the logical steps of software development.

AI-assisted development cannot replace you as a programmer, because it does not have a
true understanding of the development process, but it can considerably reduce the tedium
that is often associated with software development. You will still need to review and
accept or reject the AI's work, and will need to learn how to create effective requests
that provide the AI both with the necessary relevant context and also clear and specific
instructions.

Wing currently supports four types of AI-assisted development: (1) AI code suggestion,
where you request the AI assistant to make suggestions of code to add at the current
insertion point, (2) description-driven code development, where you ask the AI to write 
new code at the current insertion point, (3) AI-assisted code transformation, where you 
ask the AI assistant to make specific changes to existing code, and (4) AI chat, where you 
ask the AI about code or a development task in an environment that prevents it from
directly changing your code.

**AI Code Suggestion**

You can use AI code suggestion at any point in your code to insert new code appropriate to
that context. The AI uses the code that surrounds your insertion point as instructions for
what it should insert. It works remarkably well when the code and comments that precede
the current insertion point make it clear what you want to insert.

For example if you enter the following into the editor and request AI code suggestion at
the point indicated by ``|`` (by pressing ``Ctrl-?`` or using the items int he AI menu) 
then the AI will try to implement the function for you:

.. python::

  def compute_fibobnacci_sequence|  
    
One possible result is:

.. python::

    def compute_fibonacci_sequence(n):
        a, b = 0, 1
        sequence = []
        while len(sequence) < n:
            sequence.append(a)
            a, b = b, a + b
        return sequence

Another example is to use AI suggestion to fill in the implementation of methods
in a partially completed class.  Again, ``|`` is the position of the caret in the editor:

.. python::

  class Test:
    
    def __init__(self):
      self.people = {}
      
    def add_person(self, name, age, address):
      self.people[name] = (age, address)
      
    def remove_person|
  
This might implement the missing method as follows:

.. python::

  def remove_person(self, name):
    if name in self.people:
      del self.people[name]

Much more complex tasks are also often successful, if there is enough existing code to work from,
either in the current file, in other provided context, or in the AI's training set. Most
importantly, the context at the insertion point must make it clear what code should be placed there.
If that is not the case (and often it is not) then you will get much better results by using
description-driven development, as described below.

**Description-Driven Development**

You can also write new code at the current insertion point, according to a description of
its functionality and design. For example, let's start with a file that contains the
following:

.. python::

    import sys
    import time
    
    class Test:
    
       def __init__(self):
          self.people = {}
    
       def add_person(self, name, age, address):
          self.people[name] = (age, address)
          sys.stderr.write('added {}\n'.format(name))
    
       def remove_person(self, name):
          if name in self.people:
             del self.people[name]
          sys.stderr.write('removed {}\n'.format(name))
    
       def update_person(self, name, age=None, address=None):
          if name in self.people:
             if age is not None:
                self.people[name] = (age, self.people[name][1])
             if address is not None:
                self.people[name] = (self.people[name][0], address)
          sys.stderr.write('updated {}\n'.format(name))

Placing the insertion point at the end of the file, the ``AI Coder`` tool in Wing can be used to
request the AI to "write asynchronous RPC client and server classes that use sockets and JSON to
expose the public methods of class Test." This produces the following result:

.. python::

    import json
    import socket
    import asyncio
    
    class TestRPCServer:
       def __init__(self, host, port, test_instance):
          self.host = host
          self.port = port
          self.test_instance = test_instance
          self.loop = asyncio.get_event_loop()
          self.server = None
    
       async def handle_client(self, reader, writer):
          while True:
             data = await reader.read(1024)
             if not data:
                break
             request = json.loads(data.decode())
             method = request.get('method')
             params = request.get('params', {})
             response = self.handle_request(method, params)
             writer.write(json.dumps(response).encode())
             await writer.drain()
          writer.close()
    
       def handle_request(self, method, params):
          try:
             if hasattr(self.test_instance, method) and callable(getattr(self.test_instance, method)):
                result = getattr(self.test_instance, method)(**params)
                return {'result': result, 'error': None}
             else:
                return {'result': None, 'error': f'Method {method} not found'}
          except Exception as e:
             return {'result': None, 'error': str(e)}
    
       def start_server(self):
          self.server = asyncio.start_server(self.handle_client, self.host, self.port)
          self.loop.run_until_complete(self.server)
          self.loop.run_forever()
    
    class TestRPCClient:
       def __init__(self, host, port):
          self.host = host
          self.port = port
          self.loop = asyncio.get_event_loop()
    
       async def send_request(self, method, **params):
          reader, writer = await asyncio.open_connection(self.host, self.port)
          request = json.dumps({'method': method, 'params': params})
          writer.write(request.encode())
          await writer.drain()
          response = await reader.read(1024)
          writer.close()
          return json.loads(response.decode())
    
       def call(self, method, **params):
          return self.loop.run_until_complete(self.send_request(method, **params))
       
    def start_server():
       test_instance = Test()
       server = TestRPCServer('localhost', 8888, test_instance)
       server.start_server()
      
Next you can ask it to "write an example that starts TestRPCServer in a separate process
and then makes requests using TestRPCClient."  This generates something like the following:

.. python::

    def start_rpc_server():
       test = Test()
       test_rpc_server = TestRPCServer('localhost', 8888, test)
       test_rpc_server.start_server()
    
    if __name__ == '__main__':
       from multiprocessing import Process
    
       server_process = Process(target=start_rpc_server)
       server_process.start()
    
       try:
          time.sleep(1)  # Allow server process to start
          test_rpc_client = TestRPCClient('localhost', 8888)
          
          # Example usage of the TestRPCClient
          print(test_rpc_client.call('add_person', name='Alice', age=30, address='123 Main St'))
          print(test_rpc_client.call('update_person', name='Alice', age=31))
          print(test_rpc_client.call('remove_person', name='Alice'))
       finally:
          server_process.terminate()
          server_process.join()
            
If you try the above examples be aware that, just as with humans, work created by an AI
will vary with each invocation, even if the context and request are the exactly the same.

Of course the code produced by AI is not always complete and correct. For the examples
above, it took several attempts before the AI placed the 'start_rpc_server' function at
the top level of the module, so that the server child process would be able to access it.
In some of the attempts the necessary 'time.sleep(1)' was missing, so the client attempted
to connect to the server too early. 

These are typical of the kinds of errors that generative AI may make, often similar to
those made by humans writing the same code. You still need to know what you are doing,
will need to find and fix the bugs using Wing's debugger, and will sometimes have to
decide to throw away an implementation rather than proceeding with it. Even so, on the
whole you will spend far less time writing code manually.

**AI Transformation**

AI transformation lets you rewrite or redesign code according to a written description of the change
you want to make. It is also implemented by Wing's ``AI Coder`` tool and works the same way as
description-driven development, after first indicating the target code that the transformation is
allowed to change. As an example, let's start with the following code we wrote earlier using AI code
suggestion:

.. python::

    def compute_fibonacci_sequence(n):
        a, b = 0, 1
        sequence = []
        while len(sequence) < n:
            sequence.append(a)
            a, b = b, a + b
        return sequence

After selecting the code, asking the ``AI Coder`` to "convert this into a Python generator
that goes on forever and doesn't stop after n iterations" results in the following new code:

.. python::

    def compute_fibonacci_sequence():
        a, b = 0, 1
        while True:
            yield a
            a, b = b, a + b

Indeed, this code is correct and will work if invoked as follows:

.. python::

    fib = compute_fibonacci_sequence()
    for i in fib:
      print(i)

Here's another example of improving some code with AI transformation. This is code that was
also written mostly using AI suggestion:

.. python::

  class Test:
    
    def __init__(self):
      self.people = {}
      
    def add_person(self, name, age, address):
      self.people[name] = (age, address)
      
    def remove_person(self, name):
      if name in self.people:
        del self.people[name]
        
    def update_person(self, name, age=None, address=None):
      if name in self.people:
          if age is not None:
              self.people[name] = (age, self.people[name][1])
          if address is not None:
              self.people[name] = (self.people[name][0], address)
            
Requesting the ``AI Coder`` to "clean up this code" results in the following arguably
better version of the remove_person and update_person methods:

.. python::

    class Test:
    
        def __init__(self):
            self.people = {}
            
        def add_person(self, name, age, address):
            self.people[name] = {'age': age, 'address': address}
            
        def remove_person(self, name):
            self.people.pop(name, None)
            
        def update_person(self, name, age=None, address=None):
            person = self.people.get(name)
            if person:
                if age is not None:
                    person['age'] = age
                if address is not None:
                    person['address'] = address

Now the code can be documented, for example by asking ``AI Coder`` to "add documentation strings
written in French":

.. python::

    class Test:
        """Représente une collection de personnes."""
    
        def __init__(self):
            """Initialise un dictionnaire vide pour stocker les informations des personnes."""
            self.people = {}
            
        def add_person(self, name, age, address):
            """Ajoute une personne avec son nom, âge et adresse au dictionnaire.
            
            Arguments:
            name -- le nom de la personne à ajouter
            age -- l'âge de la personne
            address -- l'adresse de la personne
            """
            self.people[name] = {'age': age, 'address': address}
            
        def remove_person(self, name):
            """Supprime une personne du dictionnaire par son nom.
            
            Arguments:
            name -- le nom de la personne à supprimer
            """
            self.people.pop(name, None)
            
        def update_person(self, name, age=None, address=None):
            """Met à jour l'âge et/ou l'adresse d'une personne dans le dictionnaire.
            
            Arguments:
            name -- le nom de la personne à mettre à jour
            age -- le nouvel âge de la personne (facultatif)
            address -- la nouvelle adresse de la personne (facultatif)
            """
            person = self.people.get(name)
            if person:
                if age is not None:
                    person['age'] = age
                if address is not None:
                    person['address'] = address

Other example AI transformation requests that you might try include "add a summary comment at
the top", "add a unittest class that tests class A", and even things like "convert this
code from asynchronous to threaded".

**AI Chat**

The ``AI Chat`` tool in Wing lets you ask about a file, selection, or other unit of code in a chat
session, where you can build and refine your requests iteratively in a conversation. You can ask the
AI to summarize the purpose of code, then drill down in more detail as needed. You can produce
example code that uses code that you are focusing on. You can ask the AI to suggest how to rewrite
code to add certain functionality or how to adapt it for a particular use. And you can ask the AI to
find problems or suggest general improvements by inspecting code.

For example, starting with the following code open in an editor, you might ask the AI
Chat *"Can you suggest a way to redesign this code so it'll work asynchronously?"*

.. python::

    import threading
    import time
    
    def print_numbers():
      for i in range(10):
        time.sleep(1)
        print(i)
    
    # Create a thread
    t = threading.Thread(target=print_numbers)
    
    # Start the thread
    t.start()
    
    # Main thread continues to run
    for i in range(10,20):
      time.sleep(1.5)
      print(i)
    
    # Wait for the thread to complete
    t.join()
    
    print("Done")

This produces a reply that includes working code and an explanation of how it works::

    To redesign the code to work asynchronously, you can use the `asyncio` library in Python, 
    which provides a framework for writing concurrent code using the async/await syntax. Here's 
    how you can modify your code to use `asyncio`.

.. python::

    import asyncio
    
    async def print_numbers_async():
      for i in range(10):
        await asyncio.sleep(1)  # Non-blocking sleep
        print(i)
    
    async def print_numbers_main():
      for i in range(10, 20):
        await asyncio.sleep(1.5)  # Non-blocking sleep
        print(i)
    
    async def main():
      # Schedule both tasks to run concurrently
      task1 = asyncio.create_task(print_numbers_async())
      task2 = asyncio.create_task(print_numbers_main())
    
      # Wait for both tasks to complete
      await task1
      await task2
    
      print("Done")
    
    # Run the main function in the asyncio event loop
    asyncio.run(main())

::

    In this code:
        
    - The `async def` syntax is used to define coroutine functions `print_numbers_async` and 
      `print_numbers_main`.
    - `await asyncio.sleep(x)` is the asynchronous equivalent of `time.sleep(x)` but doesn't 
      block the event loop, allowing other tasks to run in the meantime.
    - `asyncio.create_task()` schedules the coroutines to be run concurrently.
    - The `main()` coroutine is the entry point for the asynchronous program, which awaits the 
      completion of the other tasks.
    - `asyncio.run(main())` starts the event loop and executes the `main()` coroutine.
    
    This code will execute the `print_numbers_async()` and `print_numbers_main()` functions 
    concurrently, printing numbers from two ranges simultaneously in an asynchronous manner. 
    After both functions have finished executing, it will print "Done". Remember that 
    asynchronous programming is about concurrency, not parallelism, and it's particularly 
    well-suited for IO-bound operations. If you need to run CPU-bound operations in parallel, 
    you would still want to use threading or multiprocessing.

From here, you can right-click to move code into the editor, or refine your request, ask about 
specific details, and request further changes to the code that was produced.

**Getting Started**

The best way to get a feel for the power of AI assisted development is to try requests
like these in your own code base.  See the `AI Quick Start Guide <ref:ai/quickstart>`__
to get up and running.
