Simplified API Rewrite
2024-11-214 turns27,650 charso1-preview
Summary
user wants a simplified api program that uses text entries and local ollama model
Messages
This application does not work, can you rewrite it as a simpler program that does work: # api.py
from persona_interface import PersonaInterface
from fastapi import FastAPI, File, UploadFile
from pydantic import BaseModel
from typing import Optional
import uvicorn
app = FastAPI()
# Store persona instance globally (in production, you'd want proper session management)
persona_interface = None
class ChatRequest(BaseModel):
message: str
adjust_emotional_variability: Optional[float] = None
@app.post("/initialize")
async def initialize_persona(audio_file: UploadFile = File(...)):
global persona_interface
# Save uploaded file temporarily
file_location = f"temp_{audio_file.filename}"
with open(file_location, "wb+") as file_object:
file_object.write(await audio_file.read())
try:
persona_interface = PersonaInterface()
persona_interface.initialize_from_audio(file_location)
# Clean up temporary file
import os
os.remove(file_location)
return {"status": "success", "message": "Persona initialized successfully"}
except Exception as e:
return {"status": "error", "message": str(e)}
@app.post("/chat")
async def chat(request: ChatRequest):
global persona_interface
if not persona_interface:
return {"status": "error", "message": "Persona not initialized"}
try:
# Adjust emotional variability if specified
if request.adjust_emotional_variability is not None:
persona_interface.adjust_emotional_variability(request.adjust_emotional_variability)
# Generate response
response = persona_interface.chat(request.message)
# Get current metrics
metrics = persona_interface.get_persona_metrics()
return {
"status": "success",
"response": response,
"emotional_state": metrics['emotional_state'],
"personality_metrics": metrics['personality']
}
except Exception as e:
return {"status": "error", "message": str(e)}
@app.get("/metrics")
async def get_metrics():
global persona_interface
if not persona_interface:
return {"status": "error", "message": "Persona not initialized"}
return {
"status": "success",
"metrics": persona_interface.get_persona_metrics()
}
@app.post("/save")
async def save_persona(filename: str):
global persona_interface
if not persona_interface:
return {"status": "error", "message": "Persona not initialized"}
try:
persona_interface.save_persona(f"{filename}.json")
return {"status": "success", "message": "Persona saved successfully"}
except Exception as e:
return {"status": "error", "message": str(e)}
@app.post("/load")
async def load_persona(filename: str):
global persona_interface
try:
persona_interface = PersonaInterface()
persona_interface.load_persona(f"{filename}.json")
return {"status": "success", "message": "Persona loaded successfully"}
except Exception as e:
return {"status": "error", "message": str(e)}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
# emotional_core.py
import numpy as np
from textblob import TextBlob
from typing import Dict, List, Tuple
class EmotionalCore:
def __init__(self, base_emotional_state: Dict[str, float] = None):
self.base_emotional_state = base_emotional_state or {
'valence': 0.0, # positive/negative (-1 to 1)
'arousal': 0.0, # energy level (0 to 1)
'dominance': 0.5 # confidence level (0 to 1)
}
self.emotional_history = []
self.chaos_factor = 0.2 # adjustable chaos/stability parameter
def analyze_emotion(self, text: str) -> Dict[str, float]:
"""Analyze emotional content of text using TextBlob"""
analysis = TextBlob(text)
# Extract sentiment metrics
sentiment = analysis.sentiment
return {
'valence': sentiment.polarity,
'arousal': abs(sentiment.polarity) * sentiment.subjectivity,
'dominance': sentiment.subjectivity
}
def apply_emotional_dynamics(self) -> Dict[str, float]:
"""Apply chaotic dynamics to emotional state"""
current_state = self.base_emotional_state.copy()
# Add controlled randomness
for dimension in current_state:
noise = np.random.normal(0, self.chaos_factor)
current_state[dimension] = np.clip(
current_state[dimension] + noise,
-1.0, 1.0
)
self.emotional_history.append(current_state)
return current_state
# emotional_memory.py
from collections import deque
from typing import Dict, List
import numpy as np
class EmotionalMemory:
def __init__(self, memory_size: int = 10):
self.memory_size = memory_size
self.interaction_history = deque(maxlen=memory_size)
self.emotional_trends = {
'valence': deque(maxlen=memory_size),
'arousal': deque(maxlen=memory_size),
'dominance': deque(maxlen=memory_size)
}
def add_interaction(self, prompt: str, response: str, emotional_state: Dict[str, float]):
"""Store interaction and emotional state"""
self.interaction_history.append({
'prompt': prompt,
'response': response,
'emotional_state': emotional_state
})
for dimension, value in emotional_state.items():
if dimension in self.emotional_trends:
self.emotional_trends[dimension].append(value)
def get_emotional_context(self) -> Dict[str, float]:
"""Calculate emotional context based on recent history"""
if not self.interaction_history:
return None
context = {}
for dimension in self.emotional_trends:
if self.emotional_trends[dimension]:
# Calculate weighted average, giving more weight to recent interactions
weights = np.exp(np.linspace(-1, 0, len(self.emotional_trends[dimension])))
values = np.array(list(self.emotional_trends[dimension]))
context[dimension] = np.average(values, weights=weights)
return context
def get_relevant_memories(self, prompt: str, k: int = 3) -> List[Dict]:
"""Retrieve relevant past interactions based on prompt similarity"""
if not self.interaction_history:
return []
# Simple keyword-based relevance (could be improved with embedding similarity)
prompt_words = set(prompt.lower().split())
relevant = []
for interaction in reversed(self.interaction_history):
interaction_words = set(interaction['prompt'].lower().split())
similarity = len(prompt_words.intersection(interaction_words)) / len(prompt_words)
if similarity > 0.2: # threshold for relevance
relevant.append(interaction)
if len(relevant) >= k:
break
return relevant
# example_usage.py
def main():
# Initialize the persona interface
persona = PersonaInterface()
# Load from an audio interview
try:
persona.initialize_from_audio("interview.wav")
except Exception as e:
print(f"Error loading audio: {e}")
return
# Example conversation loop
print("Persona initialized. Start chatting! (type 'quit' to exit)")
while True:
user_input = input("You: ").strip()
if user_input.lower() == 'quit':
break
try:
response = persona.chat(user_input)
print(f"Persona: {response}")
# Optional: print current emotional state
metrics = persona.get_persona_metrics()
print(f"\nCurrent emotional state: {metrics['emotional_state']}")
except Exception as e:
print(f"Error generating response: {e}")
# Save persona state
persona.save_persona("persona_state.json")
if __name__ == "__main__":
main()
# persona_generator.py
from typing import List, Dict
import whisper
from emotional_core import EmotionalCore
class PersonaGenerator:
def __init__(self):
self.emotional_core = EmotionalCore()
self.persona_metrics = {}
self.transcriber = whisper.load_model("base") # Initialize Whisper model
def transcribe_interview(self, audio_path: str) -> str:
"""Transcribe audio interview to text"""
result = self.transcriber.transcribe(audio_path)
return result["text"]
def parse_qa_pairs(self, transcript: str) -> List[Dict[str, str]]:
"""Parse transcript into question-answer pairs"""
# This is a simplified version - you'd need more sophisticated parsing
qa_pairs = []
segments = transcript.split("\n")
for i in range(0, len(segments), 2):
if i + 1 < len(segments):
qa_pairs.append({
'question': segments[i],
'answer': segments[i + 1]
})
return qa_pairs # Return the list
def analyze_qa_pairs(self, qa_pairs: List[Dict[str, str]]) -> Dict:
"""Analyze Q&A pairs for emotional patterns and personality metrics"""
emotional_patterns = []
personality_metrics = {
'openness': 0.0,
'conscientiousness': 0.0,
'extraversion': 0.0,
'agreeableness': 0.0,
'neuroticism': 0.0
}
for qa in qa_pairs:
# Analyze answer emotions
emotion = self.emotional_core.analyze_emotion(qa['answer'])
emotional_patterns.append(emotion)
# Update personality metrics based on answer content
# This is a simplified example - you'd want more sophisticated analysis
personality_metrics['openness'] += emotion['valence'] * 0.2
personality_metrics['extraversion'] += emotion['arousal'] * 0.3
personality_metrics['neuroticism'] += (1 - emotion['dominance']) * 0.25
# Normalize personality metrics
for metric in personality_metrics:
personality_metrics[metric] = np.clip(personality_metrics[metric], 0, 1)
return {
'emotional_patterns': emotional_patterns,
'personality_metrics': personality_metrics
}
def generate_persona(self, audio_path: str) -> Dict:
"""Generate complete persona from audio interview"""
# Transcribe interview
transcript = self.transcribe_interview(audio_path)
# Parse into Q&A pairs
qa_pairs = self.parse_qa_pairs(transcript)
# Analyze patterns
analysis = self.analyze_qa_pairs(qa_pairs)
# Create persona profile
persona = {
'qa_database': qa_pairs,
'emotional_baseline': analysis['emotional_patterns'],
'personality_metrics': analysis['personality_metrics'],
'response_style': self.extract_response_style(qa_pairs)
}
self.persona_metrics = persona
return persona
def extract_response_style(self, qa_pairs: List[Dict[str, str]]) -> Dict:
"""Extract linguistic style patterns from responses"""
style_metrics = {
'avg_response_length': 0,
'vocabulary_diversity': 0,
'formality_level': 0
}
all_words = []
for qa in qa_pairs:
words = qa['answer'].split()
all_words.extend(words)
style_metrics['avg_response_length'] += len(words)
style_metrics['avg_response_length'] /= len(qa_pairs)
style_metrics['vocabulary_diversity'] = len(set(all_words)) / len(all_words)
return style_metrics
# persona_interface.py
from response_generator import ResponseGenerator
from persona_generator import PersonaGenerator
from typing import Dict
class PersonaInterface:
def __init__(self, audio_path: str = None):
self.persona_generator = PersonaGenerator()
self.emotional_memory = EmotionalMemory()
self.response_generator = None
if audio_path:
self.initialize_from_audio(audio_path)
def initialize_from_audio(self, audio_path: str):
"""Initialize persona from audio interview"""
persona_metrics = self.persona_generator.generate_persona(audio_path)
self.response_generator = ResponseGenerator(persona_metrics)
def chat(self, prompt: str) -> str:
"""Generate a response to user input"""
if not self.response_generator:
raise ValueError("Persona not initialized. Please provide an audio interview first.")
# Get emotional context
emotional_context = self.emotional_memory.get_emotional_context()
# Generate response
response = self.response_generator.generate_response(prompt, emotional_context)
# Store interaction
current_emotional_state = self.response_generator.emotional_core.base_emotional_state
self.emotional_memory.add_interaction(prompt, response, current_emotional_state)
return response
def get_persona_metrics(self) -> Dict:
"""Get current persona metrics and state"""
if not self.response_generator:
return None
return {
'personality': self.persona_generator.persona_metrics['personality_metrics'],
'emotional_state': self.response_generator.emotional_core.base_emotional_state,
'response_style': self.persona_generator.persona_metrics['response_style'],
'interaction_history': len(self.emotional_memory.interaction_history)
}
def adjust_emotional_variability(self, chaos_factor: float):
"""Adjust how variable the emotional responses are"""
if not 0 <= chaos_factor <= 1:
raise ValueError("Chaos factor must be between 0 and 1")
self.response_generator.emotional_core.chaos_factor = chaos_factor
def save_persona(self, filepath: str):
"""Save persona state to file"""
import json
state = {
'persona_metrics': self.persona_generator.persona_metrics,
'emotional_state': self.response_generator.emotional_core.base_emotional_state,
'emotional_history': list(self.emotional_memory.interaction_history)
}
with open(filepath, 'w') as f:
json.dump(state, f)
def load_persona(self, filepath: str):
"""Load persona state from file"""
import json
with open(filepath, 'r') as f:
state = json.load(f)
self.persona_generator.persona_metrics = state['persona_metrics']
self.response_generator = ResponseGenerator(state['persona_metrics'])
self.response_generator.emotional_core.base_emotional_state = state['emotional_state']
# Restore interaction history
for interaction in state['emotional_history']:
self.emotional_memory.add_interaction(
interaction['prompt'],
interaction['response'],
interaction['emotional_state']
)
openai
numpy
textblob
fastapi
uvicorn
openai-whisper
requests
torch
# response_generator.py
from typing import Dict
import numpy as np
import requests
from emotional_core import EmotionalCore
class ResponseGenerator:
def __init__(self, persona_metrics: Dict):
self.persona = persona_metrics
self.emotional_core = EmotionalCore(
base_emotional_state=self.calculate_emotional_baseline()
)
def calculate_emotional_baseline(self) -> Dict[str, float]:
"""Calculate baseline emotional state from persona metrics"""
emotional_patterns = self.persona['emotional_baseline']
return {
'valence': np.mean([e['valence'] for e in emotional_patterns]),
'arousal': np.mean([e['arousal'] for e in emotional_patterns]),
'dominance': np.mean([e['dominance'] for e in emotional_patterns])
}
def adjust_response_style(self, response: str) -> str:
"""Adjust response based on persona's linguistic style"""
style_metrics = self.persona['response_style']
# Adjust response length
target_length = style_metrics['avg_response_length']
current_length = len(response.split())
if current_length > target_length * 1.5:
# Truncate response
return ' '.join(response.split()[:int(target_length * 1.2)])
elif current_length < target_length * 0.5:
# Expand response (you'd want more sophisticated expansion)
return response + " " + self.generate_expansion(response)
return response
def generate_expansion(self, base_response: str) -> str:
"""Generate additional content to expand a response"""
prompt = f"{base_response} Furthermore,"
expansion = self.generate_with_ollama(prompt)
return expansion.replace(prompt, "")
def generate_response(self, prompt: str, context: Dict = None) -> str:
"""Generate a response based on prompt and emotional state"""
# Get current emotional state
emotional_state = self.emotional_core.apply_emotional_dynamics()
# Prepare prompt with emotional context
emotional_prompt = self.prepare_emotional_prompt(prompt, emotional_state)
# Generate base response using Ollama
response = self.generate_with_ollama(emotional_prompt)
# Apply style adjustments
response = self.adjust_response_style(response)
return response
def prepare_emotional_prompt(self, prompt: str, emotional_state: Dict[str, float]) -> str:
"""Prepare prompt with emotional context"""
emotion_descriptor = self.emotion_to_text(emotional_state)
return f"[{emotion_descriptor}] {prompt}"
def emotion_to_text(self, emotional_state: Dict[str, float]) -> str:
"""Convert emotional state to text description"""
valence = emotional_state['valence']
arousal = emotional_state['arousal']
if valence > 0.3:
if arousal > 0.5:
return "enthusiastically"
else:
return "contentedly"
elif valence < -0.3:
if arousal > 0.5:
return "angrily"
else:
return "sadly"
else:
if arousal > 0.5:
return "energetically"
else:
return "calmly"
def generate_with_ollama(self, prompt: str) -> str:
"""Generate a response using Ollama API"""
url = 'http://localhost:11434/api/generate' # Default Ollama API endpoint
data = {
'model': 'llama3.2', # Replace with your actual model name in Ollama
'prompt': prompt,
'max_tokens': 100,
'temperature': 0.8,
'top_p': 0.9
}
response = requests.post(url, json=data)
if response.status_code == 200:
return response.json().get('response', '').strip()
else:
raise Exception(f"Ollama API Error: {response.status_code} {response.text}")
Sure, I can help you rewrite the application into a simpler program that works. Below is a simplified version that provides similar functionality using fewer components. This program:
- Uses FastAPI to create an API with two endpoints: `/initialize` and `/chat`.
- Uses Whisper to transcribe an audio file uploaded via `/initialize`.
- Stores the transcript globally.
- Uses OpenAI's API to generate responses in `/chat`, using the transcript as context.
Here's the simplified code:
```python
# app.py
from fastapi import FastAPI, File, UploadFile
from pydantic import BaseModel
import uvicorn
import openai
import whisper
import os
app = FastAPI()
# Global variable to store the transcript
transcript = ""
# Load Whisper model
whisper_model = whisper.load_model("base")
# Set your OpenAI API key
openai.api_key = os.getenv("OPENAI_API_KEY") # Ensure you have set the OPENAI_API_KEY environment variable
class ChatRequest(BaseModel):
message: str
@app.post("/initialize")
async def initialize_persona(audio_file: UploadFile = File(...)):
global transcript
# Save uploaded file temporarily
file_location = f"temp_{audio_file.filename}"
with open(file_location, "wb") as file_object:
file_object.write(await audio_file.read())
try:
# Transcribe audio file
result = whisper_model.transcribe(file_location)
transcript = result["text"]
# Clean up temporary file
os.remove(file_location)
return {"status": "success", "message": "Persona initialized successfully"}
except Exception as e:
return {"status": "error", "message": str(e)}
@app.post("/chat")
async def chat(request: ChatRequest):
global transcript
if not transcript:
return {"status": "error", "message": "Persona not initialized"}
try:
# Use OpenAI API to generate a response
messages = [
{"role": "system", "content": f"The following is a transcript of an interview: {transcript}"},
{"role": "user", "content": request.message}
]
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo", # or "gpt-4" if available
messages=messages,
max_tokens=150,
n=1,
stop=None,
temperature=0.7,
)
answer = response.choices[0].message['content'].strip()
return {
"status": "success",
"response": answer
}
except Exception as e:
return {"status": "error", "message": str(e)}
if __name__ == "__main__":
uvicorn.run("app:app", host="0.0.0.0", port=8000)
```
**Instructions:**
1. **Install Required Packages:**
```bash
pip install fastapi uvicorn openai openai-whisper
```
2. **Set Your OpenAI API Key:**
Make sure you have your OpenAI API key and set it as an environment variable:
```bash
export OPENAI_API_KEY='your-api-key-here'
```
Or, you can set it directly in the code by replacing `os.getenv("OPENAI_API_KEY")` with your API key string (not recommended for security reasons).
3. **Run the Application:**
```bash
uvicorn app:app --host 0.0.0.0 --port 8000
```
4. **Usage:**
- **Initialize Persona:**
Send a POST request to `/initialize` with an audio file. This will transcribe the audio and store the transcript.
Example using `curl`:
```bash
curl -X POST "http://localhost:8000/initialize" -F "audio_file=@path_to_your_audio_file.wav"
```
- **Chat:**
Send a POST request to `/chat` with a JSON body containing your message.
Example:
```bash
curl -X POST "http://localhost:8000/chat" -H "Content-Type: application/json" -d '{"message": "Hello, how are you?"}'
```
The response will be a JSON containing the AI's reply.
**Note:**
- This simplified version removes the emotional core, emotional memory, and other complexities.
- It focuses on initializing the persona from an audio file and generating responses based on that transcript.
- Ensure that you have the audio file in a format supported by Whisper (e.g., WAV, MP3).
- The application uses the OpenAI API, so you may incur costs depending on your usage and pricing plan.
**Dependencies:**
- `fastapi`
- `uvicorn`
- `openai`
- `openai-whisper`
You can install them all with:
```bash
pip install fastapi uvicorn openai openai-whisper
```
o1-preview
no I want the application to use text entries and not use voice and I want it to use the locally ollama model
Certainly! Below is a simplified version of your application that uses text inputs instead of voice and utilizes a locally running Ollama model for generating responses. This application uses FastAPI to create an API with a `/chat` endpoint.
```python
# app.py
from fastapi import FastAPI
from pydantic import BaseModel
import requests
import uvicorn
app = FastAPI()
class ChatRequest(BaseModel):
message: str
@app.post("/chat")
async def chat(request: ChatRequest):
try:
# Use Ollama API to generate a response
url = 'http://localhost:11434/api/generate' # Default Ollama API endpoint
data = {
'model': 'your_model_name', # Replace with your actual model name in Ollama
'prompt': request.message,
'max_tokens': 150,
'temperature': 0.7,
'top_p': 0.9
}
response = requests.post(url, json=data)
if response.status_code == 200:
# Extract the generated text from the response
answer = response.json().get('output', '').strip()
return {
"status": "success",
"response": answer
}
else:
return {
"status": "error",
"message": f"Ollama API Error: {response.status_code} {response.text}"
}
except Exception as e:
return {"status": "error", "message": str(e)}
if __name__ == "__main__":
uvicorn.run("app:app", host="0.0.0.0", port=8000)
```
### Instructions:
1. **Install Required Packages:**
```bash
pip install fastapi uvicorn requests
```
2. **Set Up Ollama:**
- Ensure you have [Ollama](https://ollama.ai/) installed and running locally.
- Start the Ollama server:
```bash
ollama serve
```
- Make sure your desired model is installed in Ollama. For example, to install the Llama 2 model:
```bash
ollama pull llama2
```
3. **Update the Model Name:**
- In the code, replace `'your_model_name'` with the actual model name you've installed in Ollama (e.g., `'llama2'`).
4. **Run the Application:**
```bash
uvicorn app:app --host 0.0.0.0 --port 8000
```
5. **Usage:**
- **Chat Endpoint:**
Send a POST request to `/chat` with a JSON body containing your message.
Example using `curl`:
```bash
curl -X POST "http://localhost:8000/chat" -H "Content-Type: application/json" -d '{"message": "Hello, how are you?"}'
```
The response will be a JSON containing the AI's reply.
### Dependencies:
- `fastapi`
- `uvicorn`
- `requests`
You can install all dependencies with:
```bash
pip install fastapi uvicorn requests
```
### Additional Notes:
- **Ollama API Endpoint:**
- The default API endpoint for Ollama is `http://localhost:11434/api/generate`. If your Ollama server is running on a different host or port, update the `url` variable accordingly.
- **Model Configuration:**
- Adjust the `max_tokens`, `temperature`, and `top_p` parameters in the `data` dictionary to fine-tune the response generation according to your needs.
- **Error Handling:**
- The application includes basic error handling for API responses and exceptions.
### Example:
**Request:**
```bash
curl -X POST "http://localhost:8000/chat" -H "Content-Type: application/json" -d '{"message": "Tell me a joke."}'
```
**Response:**
```json
{
"status": "success",
"response": "Why did the scarecrow win an award? Because he was outstanding in his field!"
}
```
This simplified application meets your requirements by:
- Accepting text inputs instead of voice.
- Utilizing your locally running Ollama model for generating responses.
- Providing an easy-to-use API with a `/chat` endpoint.
Feel free to customize the code further to suit your specific needs.
o1-preview