Pair Programming with a Large Language Model

Pair Programming with a Large Language Model

A Developer's Guide

Introduction

Large Language Model's (LLMs) are changing the way we write code. As developers many times we come across unfamiliar libraries that we need to use to develop something in such cases rather than directly going through its documentation we can use LLM's to generate code which we can test and fix later as per our requirements. Many developers are using LLM's in many ways to speed up their work. In this blog you will learn some of the emerging best practices including how to get a LLM to help you with error handling, performance improvement and lot more.

Here we will learn how to use an LLM to simplify and improve the code, write test cases, debug the code, refractor the code, work with complex existing codebase where there maybe technical debt where LLM can help you explain document and format that existing code base.

Getting Started

PaLM API

The PaLM API is a simple entry point for Google’s large language models, which can be used for a variety of applications. It will provide developers access to models that are optimized for multi-turn use cases, such as content generation and chat, and general purpose models that are optimized for use cases such as summarization, classification, and more.

Setup

  1. If you wanted to use the PaLM API on your own machine, you would first install the library:

    pip install -q google.generativeai
    

    The optional flag -q installs "quietly" without printing out details of the installation.

  2. Get your own API key for your project from the below link https://developers.generativeai.google/

  3. Set the MakerSuite API key with the provided helper function.

from utils import get_api_key
from utils import get_api_key
import google.generativeai as palm

palm.configure(api_key=get_api_key())

Explore the available models

for m in palm.list_models():
    print(f"name: {m.name}")
    print(f"description: {m.description}")
    print(f"generation methods:{m.supported_generation_methods}\n")

Output

name: models/chat-bison-001
description: Chat-optimized generative language model.
generation methods:['generateMessage', 'countMessageTokens']

name: models/text-bison-001
description: Model targeted for text generation.
generation methods:['generateText', 'countTextTokens', 'createTunedTextModel']

name: models/embedding-gecko-001
description: Obtain a distributed representation of a text.
generation methods:['embedText']

Note: The model names are based on animal names the bigger the animal the bigger the model will be.

Filter models by their supported generation methods

  • generateText is currently recommended for coding-related prompts.
  • generateMessage is optimized for multi-turn chats (dialogues) with an LLM.
models = [m for m in palm.list_models() 
          if 'generateText' 
          in m.supported_generation_methods]
print(models)
model_bison = models[0]
print(model_bison)

Helper function to generate text

  • The @retry decorator helps you to retry the API call if it fails.
  • We set the temperature to 0.0 so that the model returns the same output (completion) if given the same input (the prompt).
from google.api_core import retry
@retry.Retry()
def generate_text(prompt,
                  model=model_bison,
                  temperature=0.0):
    return palm.generate_text(prompt=prompt,
                              model=model,
                              temperature=temperature)

Ask the LLM how to write some code

prompt = "Show me how to iterate across a list in Python."
completion = generate_text(prompt)
print(completion.result)

Output

#To iterate across a list in Python, you can use the for loop. The syntax is as #follows:

for item in list:
  # do something with item


# For example, the following code prints each item in the list my_list:
my_list = ["a", "b", "c"]

for item in my_list:
  print(item)

Output:
a
b
c

# You can also use the enumerate() function to iterate over a list and get the 
# index of each item. The syntax is as follows:

for index, item in enumerate(list):
  # do something with index and item


# For example, the following code prints the index and value of each item in the # list my_list:

my_list = ["a", "b", "c"]

for index, item in enumerate(my_list):
  print(index, item)

Output:
0 a
1 b
2 c

Tip: The words "show me" tends to encourage the PaLM LLM to give more details and explanations compared to if you were to ask "write code to ..."

Try out the code

  • Try copy-pasting some of the generated code and running it in the notebook.
  • Remember to test out the LLM-generated code and debug it make sure it works as intended.

Try asking your own coding question

# Modify the prompt with your own question
prompt = "Show me how to [...]"

completion = generate_text(prompt)

Using a String Template

Prompt template

  1. priming: getting the LLM ready for the type of task you'll ask it to do.
  2. question: the specific task.
  3. decorator: how to provide or format the output.
prompt_template = """
{priming}

{question}

{decorator}

Your solution:
"""
priming_text = "You are an expert at writing clear, concise, Python code."

question = "create a doubly linked list"

decorator = "Insert comments for each line of code."

Observe how the decorator affects the output

  • In other non-coding prompt engineering tasks, it's common to use "chain-of-thought prompting" by asking the model to work through the task "step by step".
  • For certain tasks like generating code, you may want to experiment with other wording that would make sense if you were asking a developer the same question.
prompt = prompt_template.format(priming=priming_text,
                                question=question,
                                decorator=decorator)
print(prompt)

Call the API to get the completion

completion = generate_text(prompt)
print(completion.result)

Pair Programming Scenarios

Scenario 1: Improve existing code

  • An LLM can help you rewrite your code in the way that's recommended for that particular language.
  • You can ask an LLM to rewrite your Python code in a way that is more 'Pythonic".
prompt_template = """
I don't think this code is the best way to do it in Python, can you help me?

{question}

Please explain, in detail, what you did to improve it.
"""
question = """
def func_x(array)
  for i in range(len(array)):
    print(array[i])
"""
completion = generate_text(
    prompt = prompt_template.format(question=question)
)
print(completion.result)

Ask for multiple ways of rewriting your code

prompt_template = """
I don't think this code is the best way to do it in Python, can you help me?

{question}

Please explore multiple ways of solving the problem, and explain each.
"""
completion = generate_text(
    prompt = prompt_template.format(question=question)
)
print(completion.result)

Ask the model to recommend one of the methods as most 'Pythonic'

prompt_template = """
I don't think this code is the best way to do it in Python, can you help me?

{question}

Please explore multiple ways of solving the problem, and explain each.
"""

completion = generate_text(
    prompt = prompt_template.format(question=question)
)
print(completion.result)

Ask the model to recommend one of the methods as most 'Pythonic'

prompt_template = """
I don't think this code is the best way to do it in Python, can you help me?

{question}

Please explore multiple ways of solving the problem, 
and tell me which is the most Pythonic
"""

completion = generate_text(
    prompt = prompt_template.format(question=question)
)
print(completion.result)

Scenario 2: Simplify code

  • Ask the LLM to perform a code review.
  • Note that adding/removing newline characters may affect the LLM completion that gets output by the LLM.
# option 1
prompt_template = """
Can you please simplify this code for a linked list in Python?

{question}

Explain in detail what you did to modify it, and why.
"""
# option 2
prompt_template = """
Can you please simplify this code for a linked list in Python? \n
You are an expert in Pythonic code.

{question}

Please comment each line in detail, \n
and explain in detail what you did to modify it, and why.
"""
question = """
class Node:
  def __init__(self, dataval=None):
    self.dataval = dataval
    self.nextval = None

class SLinkedList:
  def __init__(self):
    self.headval = None

list1 = SLinkedList()
list1.headval = Node("Mon")
e2 = Node("Tue")
e3 = Node("Wed")
list1.headval.nextval = e2
e2.nextval = e3

"""
completion = generate_text(
    prompt = prompt_template.format(question=question)
)
print(completion.result)

Scenario 3: Write test cases

  • It may help to specify that you want the LLM to output "in code" to encourage it to write unit tests instead of just returning test cases in English.
prompt_template = """
Can you please create test cases in code for this Python code?

{question}

Explain in detail what these test cases are designed to achieve.
"""
question = """
class Node:
  def __init__(self, dataval=None):
    self.dataval = dataval
    self.nextval = None

class SLinkedList:
  def __init__(self):
    self.head = None

def create_linked_list(data):
  head = Node(data[0])
  for i in range(1, len(data)):
    node = Node(data[i])
    node.nextval = head
    head = node
  return head

list1 = create_linked_list(["Mon", "Tue", "Wed"])
"""
completion = generate_text(
    prompt = prompt_template.format(question=question)
)
print(completion.result)

Scenario 4: Make code more efficient

  • Improve runtime by potentially avoiding inefficient methods (such as ones that use recursion when not needed).
prompt_template = """
Can you please make this code more efficient?

{question}

Explain in detail what you changed and why.
"""
question = """
# Returns index of x in arr if present, else -1
def binary_search(arr, low, high, x):
    # Check base case
    if high >= low:
        mid = (high + low) // 2
        if arr[mid] == x:
            return mid
        elif arr[mid] > x:
            return binary_search(arr, low, mid - 1, x)
        else:
            return binary_search(arr, mid + 1, high, x)
    else:
        return -1

# Test array
arr = [ 2, 3, 4, 10, 40 ]
x = 10

# Function call
result = binary_search(arr, 0, len(arr)-1, x)

if result != -1:
    print("Element is present at index", str(result))
else:
    print("Element is not present in array")

"""
completion = generate_text(
    prompt = prompt_template.format(question=question)
)
print(completion.result)

Scenario 5: Debug your code


prompt_template = """
Can you please help me to debug this code?

{question}

Explain in detail what you found and why it was a bug.
"""
question = """
class Node:
   def __init__(self, data):
      self.data = data
      self.next = None
      self.prev = None

class doubly_linked_list:
   def __init__(self):
      self.head = None

# Adding data elements
   def push(self, NewVal):
      NewNode = Node(NewVal)
      NewNode.next = self.head
      if self.head is not None:
         self.head.prev = NewNode
      self.head = NewNode

# Print the Doubly Linked list in order
   def listprint(self, node):
       print(node.data),
       last = node
       node = node.next

dllist = doubly_linked_list()
dllist.push(12)
dllist.push(8)
dllist.push(62)
dllist.listprint(dllist.head)

"""
completion = generate_text(
    prompt = prompt_template.format(question=question)
)
print(completion.result)

Technical Debt

Ask an LLM to explain a complex code base

CODE_BLOCK = """ 
         YOUR CODE GOES HERE
"""
prompt_template = """
Can you please explain how this code works?

{question}

Use a lot of detail and make it as clear as possible.
"""
completion = generate_text(
    prompt = prompt_template.format(question=CODE_BLOCK)
)
print(completion.result)

Ask an LLM to document a complex code base

prompt_template = """
Please write technical documentation for this code and \n
make it easy for a non swift developer to understand:

{question}

Output the results in markdown
"""
completion = generate_text(
    prompt = prompt_template.format(question=CODE_BLOCK)
)
print(completion.result)

Conclusion

There is a lot of fear, insecurity and doubt when it comes to generative code, and there is a lot of hype saying LLM's will put developers out of work but LLM's can be great help to developers making them more efficient. In this blog we stepped through a number of ways in which you can use LLM's that go beyond just simple code generation and which can help you become a better software engineer. I hope that they inspired many use-cases in your mind about how you can be a better developer.

Reference

DeepLearning.AI