Refactor with Personas Integration
2024-12-292 turns11,054 charsgpt-4o
Summary
User wants a prompt for continue.dev to refactor main.py to integrate personas from the ngpg05-2 project.
Messages
help me write a prompt I can give to continue.dev to help refactor a main.py file so that it will load the personas generated by this project: https://github.com/kliewerdaniel/ngpg05-2, this is the main.py file: # main.py
import click
import os
import networkx as nx
from langchain import PromptTemplate, LLMChain
from langchain.llms import Ollama
import json
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.post("/api/process-graph")
async def process_graph_endpoint(request: dict):
iterations = request.get('iterations', 1)
# Call your existing process_graph function
result = process_graph(G, iterations) # You'll need to modify this to return the graph data
return result
# Define the Node class
class Node:
def __init__(self, node_id, prompt_text, persona_name):
self.id = node_id
self.prompt_text = prompt_text
self.response_text = None
self.context = ""
self.persona_name = persona_name
self.persona_attributes = {}
# Initialize the graph
G = nx.DiGraph()
def build_graph(nodes_info, edges_info):
G = nx.DiGraph()
nodes = {}
# Create nodes
for node_info in nodes_info:
node_id = node_info['id']
prompt_text = node_info['prompt_text']
persona_name = node_info['persona_name']
node = Node(node_id, prompt_text, persona_name)
G.add_node(node_id, data=node)
nodes[node_id] = node
# Add edges
for edge in edges_info:
G.add_edge(edge['from'], edge['to'])
return G
def process_graph(G, iterations):
"""Process the graph for the specified number of iterations."""
for iteration in range(iterations):
print(f"\nProcessing iteration {iteration + 1}/{iterations}")
# Store previous responses for context
previous_responses = {}
for node_id in G.nodes():
node = G.nodes[node_id]['data']
if node.response_text:
previous_responses[node_id] = node.response_text
# Process each node in topological order
for node_id in nx.topological_sort(G):
node = G.nodes[node_id]['data']
if node.persona_name != "Analyst":
node.context = collect_context(node_id, G, iteration, previous_responses)
node.response_text = generate_response(node, iteration)
update_markdown(node, iteration)
else:
analyze_responses(node, G, iteration)
def load_personas(persona_dir):
personas = {}
for filename in os.listdir(persona_dir):
if filename.endswith('.json'):
filepath = os.path.join(persona_dir, filename)
with open(filepath, 'r', encoding='utf-8') as f:
persona_data = json.load(f)
name = persona_data.get('name')
if name:
personas[name] = persona_data
return personas
# Load personas
persona_dir = 'personas' # Directory where persona JSON files are stored
personas = load_personas(persona_dir)
# Function to collect context from predecessor nodes
def collect_context(node_id, G, iteration, previous_responses):
"""Collect context including previous iterations."""
predecessors = list(G.predecessors(node_id))
context = ""
# Add context from previous iterations if they exist
if iteration > 0:
context += f"\nPrevious iteration responses:\n"
for pred_id in predecessors:
if pred_id in previous_responses:
pred_node = G.nodes[pred_id]['data']
context += f"From {pred_node.persona_name} (previous round):\n{previous_responses[pred_id]}\n\n"
# Add context from current iteration
context += f"\nCurrent iteration responses:\n"
for pred_id in predecessors:
pred_node = G.nodes[pred_id]['data']
if pred_node.response_text:
context += f"From {pred_node.persona_name}:\n{pred_node.response_text}\n\n"
return context
# Function to generate responses using LangChain and Ollama
def generate_response(node, iteration):
"""Generate response with awareness of the current iteration."""
persona = personas.get(node.persona_name)
if not persona:
raise ValueError(f"Persona '{node.persona_name}' not found.")
node.persona_attributes = persona
system_prompt = build_system_prompt(persona)
# Modify the prompt to include iteration information
iteration_prompt = f"This is round {iteration + 1} of the conversation. "
if iteration > 0:
iteration_prompt += "Please consider the previous responses in your reply. "
prompt_template = PromptTemplate(
input_variables=["system_prompt", "iteration_prompt", "context", "prompt"],
template="{system_prompt}\n\n{iteration_prompt}\n\n{context}\n\n{prompt}"
)
llm = Ollama(
base_url="http://localhost:11434",
model="qwq",
)
chain = LLMChain(llm=llm, prompt=prompt_template)
response = chain.run(
system_prompt=system_prompt,
iteration_prompt=iteration_prompt,
context=node.context,
prompt=node.prompt_text
)
return response
def build_system_prompt(persona):
# Construct descriptive sentences based on persona attributes
# We'll focus on key attributes for brevity
name = persona.get('name', 'The speaker')
tone = persona.get('tone', 'neutral')
sentence_structure = persona.get('sentence_structure', 'varied')
vocabulary_complexity = persona.get('vocabulary_complexity', 5)
formality_level = persona.get('formality_level', 5)
pronoun_preference = persona.get('pronoun_preference', 'third-person')
language_abstraction = persona.get('language_abstraction', 'mixed')
# Create a description
description = (
f"You are {name}, writing in a {tone} tone using {sentence_structure} sentences. "
f"Your vocabulary complexity is {vocabulary_complexity}/10, and your formality level is {formality_level}/10. "
f"You prefer {pronoun_preference} narration and your language abstraction is {language_abstraction}."
)
# Include any other attributes as needed
# ...
return description
# Function to log interactions to a markdown file
def update_markdown(node, iteration):
"""Update markdown file with iteration information."""
with open("conversation.md", "a", encoding="utf-8") as f:
f.write(f"## Iteration {iteration + 1} - Node {node.id}: {node.persona_name}\n\n")
f.write(f"**Prompt:**\n\n{node.prompt_text}\n\n")
f.write(f"**Response:**\n\n{node.response_text}\n\n---\n\n")
# Function for nodes that perform analysis
def analyze_responses(node, G, iteration):
"""Analyze responses with awareness of iteration context."""
predecessors = list(G.predecessors(node.id))
analysis_input = f"Analysis for Iteration {iteration + 1}:\n\n"
for pred_id in predecessors:
pred_node = G.nodes[pred_id]['data']
analysis_input += f"{pred_node.persona_name}'s response:\n{pred_node.response_text}\n\n"
node.prompt_text = (
f"Provide an analysis comparing the following perspectives from iteration {iteration + 1}:\n\n"
f"{analysis_input}\n"
f"Consider how the conversation has evolved across iterations."
)
node.context = ""
node.response_text = generate_response(node, iteration)
update_markdown(node, iteration)
@click.group()
def cli():
pass
@cli.command()
def list_personas():
"""List all available personas."""
for persona_name in personas.keys():
print(persona_name)
@cli.command()
@click.option('--nodes', '-n', default=2, help='Number of nodes (excluding the analyst node).')
@click.option('--iterations', '-i', default=1, help='Number of conversation iterations.')
def run(nodes, iterations):
"""Run the application with the specified number of nodes and iterations."""
# Clear previous conversation file
with open("conversation.md", "w", encoding="utf-8") as f:
f.write("# Conversation Log\n\n")
# Let the user select personas and input prompts for each node
nodes_info = []
for i in range(1, nodes + 1):
print(f"\nConfiguring Node {i}")
persona_name = click.prompt('Enter the persona name', type=str)
while persona_name not in personas:
print('Persona not found. Available personas:')
for name in personas.keys():
print(f" - {name}")
persona_name = click.prompt('Enter the persona name', type=str)
prompt_text = click.prompt('Enter the prompt text', type=str)
node_info = {
'id': i,
'prompt_text': prompt_text,
'persona_name': persona_name
}
nodes_info.append(node_info)
# Add the analyst node
analyst_node_id = nodes + 1
analyst_node_info = {
'id': analyst_node_id,
'prompt_text': '',
'persona_name': 'Analyst'
}
nodes_info.append(analyst_node_info)
# Define edges
edges_info = []
for i in range(1, nodes + 1):
edges_info.append({'from': i, 'to': analyst_node_id})
# Build and process the graph
G = build_graph(nodes_info, edges_info)
# Process the graph for the specified number of iterations
process_graph(G, iterations)
print(f"\nConversation with {iterations} iterations has been generated and logged to conversation.md")
if __name__ == '__main__':
cli()
Here's a concise and structured prompt you can use with continue.dev to help refactor the `main.py` file:
---
**Prompt:**
I have a Python project where I want to refactor `main.py` to integrate persona files generated by the project [https://github.com/kliewerdaniel/ngpg05-2](https://github.com/kliewerdaniel/ngpg05-2). Here's what I need help with:
1. Modify the current `load_personas` function to seamlessly load personas using the JSON format generated by the referenced project.
2. Ensure each persona is dynamically added to the graph's nodes (`Node` objects) with their respective attributes.
3. Refactor the `generate_response` and `build_system_prompt` functions to handle the extended persona attributes without hardcoding the attribute list. The attributes should be parsed dynamically based on the JSON structure.
4. Enhance code readability and modularity, introducing helper functions or classes as necessary for persona handling.
5. Validate that existing functionality (like CLI commands and FastAPI endpoints) works with the new persona integration.
The current `main.py` file is as follows:
```python
# <Insert the full code of main.py here>
```
Please provide a refactored version of the code and suggest improvements or any edge cases to address.
gpt-4o