← all conversations

Integrating Anthropic API

2024-10-254 turns61,956 charso1-preview
api-integrationcode-modificationsoftware-upgrade

Summary

modify the ghostwriter repo to use anthropic's api instead of xai's

Messages

Starting from the repo at https://github.com/kliewerdaniel/GhostWriter and modify it to use Anthropic's API instead of XAI's api, be sure to use the most up to date model and write out all the updates to this code: import logging from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status, generics from .serializers import PersonaSerializer, BlogPostSerializer from .models import Persona, BlogPost from .utils import generate_content logger = logging.getLogger(__name__) class AnalyzeWritingSampleView(APIView): def post(self, request, *args, **kwargs): logger.debug(f"Request data: {request.data}") serializer = PersonaSerializer(data=request.data) if serializer.is_valid(): persona = serializer.save() return Response(PersonaSerializer(persona).data, status=status.HTTP_201_CREATED) else: logger.error(f"Serializer validation failed: {serializer.errors}") return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class GenerateContentView(APIView): def post(self, request): persona_id = request.data.get('persona_id') prompt = request.data.get('prompt') if not persona_id: logger.warning('persona_id is required.') return Response({'error': 'persona_id is required.'}, status=status.HTTP_400_BAD_REQUEST) if not prompt: logger.warning('prompt is required.') return Response({'error': 'prompt is required.'}, status=status.HTTP_400_BAD_REQUEST) try: persona = Persona.objects.get(id=persona_id) except Persona.DoesNotExist: logger.warning(f"Persona with ID {persona_id} not found.") return Response({'error': f'Persona with ID {persona_id} not found'}, status=status.HTTP_404_NOT_FOUND) # Call generate_content to create the blog post content blog_post_content = generate_content(persona.data, prompt) if not blog_post_content: logger.error('Failed to generate blog post content.') return Response({'error': 'Failed to generate blog post content.'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) # Ensure content is split into title and body lines = blog_post_content.strip().split('\n') title = lines[0] if lines else 'Untitled' content = '\n'.join(lines[1:]) if len(lines) > 1 else '' # Create the BlogPost object and save it to the database blog_post = BlogPost.objects.create( persona=persona, title=title, content=content, ) # Log for debugging purposes logger.debug(f"Blog post created with title: {title}") # Return the serialized blog post data return Response(BlogPostSerializer(blog_post).data, status=status.HTTP_201_CREATED) class PersonaListView(generics.ListAPIView): queryset = Persona.objects.all() serializer_class = PersonaSerializer class PersonaDetailView(APIView): def get(self, request, persona_id): try: persona = Persona.objects.get(id=persona_id) except Persona.DoesNotExist: logger.warning(f"Persona with ID {persona_id} not found.") return Response({'error': 'Persona not found'}, status=status.HTTP_404_NOT_FOUND) serializer = PersonaSerializer(persona) return Response(serializer.data, status=status.HTTP_200_OK) class BlogPostView(generics.ListAPIView): queryset = BlogPost.objects.all().order_by('-created_at') serializer_class = BlogPostSerializer import logging import requests import json import re from decouple import config logger = logging.getLogger(__name__) XAI_API_KEY = config('XAI_API_KEY') XAI_API_BASE = "https://api.x.ai/v1" def analyze_writing_sample(writing_sample): endpoint = f"{XAI_API_BASE}/chat/completions" headers = { "Content-Type": "application/json", "Authorization": f"Bearer {XAI_API_KEY}" } payload = { "messages": [ { "role": "system", "content": "You are an assistant that analyzes writing samples." }, { "role": "user", "content": f''' Please analyze the writing style and personality of the given writing sample. Provide a detailed assessment of their characteristics using the following template. Rate each applicable characteristic on a scale of 1-10 where relevant, or provide a descriptive value. Return the results in a JSON format. "name": "[Author/Character Name]", "vocabulary_complexity": [1-10], "sentence_structure": "[simple/complex/varied]", "paragraph_organization": "[structured/loose/stream-of-consciousness]", "idiom_usage": [1-10], "metaphor_frequency": [1-10], "simile_frequency": [1-10], "tone": "[formal/informal/academic/conversational/etc.]", "punctuation_style": "[minimal/heavy/unconventional]", "contraction_usage": [1-10], "pronoun_preference": "[first-person/third-person/etc.]", "passive_voice_frequency": [1-10], "rhetorical_question_usage": [1-10], "list_usage_tendency": [1-10], "personal_anecdote_inclusion": [1-10], "pop_culture_reference_frequency": [1-10], "technical_jargon_usage": [1-10], "parenthetical_aside_frequency": [1-10], "humor_sarcasm_usage": [1-10], "emotional_expressiveness": [1-10], "emphatic_device_usage": [1-10], "quotation_frequency": [1-10], "analogy_usage": [1-10], "sensory_detail_inclusion": [1-10], "onomatopoeia_usage": [1-10], "alliteration_frequency": [1-10], "word_length_preference": "[short/long/varied]", "foreign_phrase_usage": [1-10], "rhetorical_device_usage": [1-10], "statistical_data_usage": [1-10], "personal_opinion_inclusion": [1-10], "transition_usage": [1-10], "reader_question_frequency": [1-10], "imperative_sentence_usage": [1-10], "dialogue_inclusion": [1-10], "regional_dialect_usage": [1-10], "hedging_language_frequency": [1-10], "language_abstraction": "[concrete/abstract/mixed]", "personal_belief_inclusion": [1-10], "repetition_usage": [1-10], "subordinate_clause_frequency": [1-10], "verb_type_preference": "[active/stative/mixed]", "sensory_imagery_usage": [1-10], "symbolism_usage": [1-10], "digression_frequency": [1-10], "formality_level": [1-10], "reflection_inclusion": [1-10], "irony_usage": [1-10], "neologism_frequency": [1-10], "ellipsis_usage": [1-10], "cultural_reference_inclusion": [1-10], "stream_of_consciousness_usage": [1-10], "openness_to_experience": [1-10], "conscientiousness": [1-10], "extraversion": [1-10], "agreeableness": [1-10], "emotional_stability": [1-10], "dominant_motivations": "[achievement/affiliation/power/etc.]", "core_values": "[integrity/freedom/knowledge/etc.]", "decision_making_style": "[analytical/intuitive/spontaneous/etc.]", "empathy_level": [1-10], "self_confidence": [1-10], "risk_taking_tendency": [1-10], "idealism_vs_realism": "[idealistic/realistic/mixed]", "conflict_resolution_style": "[assertive/collaborative/avoidant/etc.]", "relationship_orientation": "[independent/communal/mixed]", "emotional_response_tendency": "[calm/reactive/intense]", "creativity_level": [1-10], "age": "[age or age range]", "gender": "[gender]", "education_level": "[highest level of education]", "professional_background": "[brief description]", "cultural_background": "[brief description]", "primary_language": "[language]", "language_fluency": "[native/fluent/intermediate/beginner]", "background": "[A brief paragraph describing the author's context, major influences, and any other relevant information not captured above]" Writing Sample: {writing_sample} ''' } ], "model": "grok-beta", "stream": False, "temperature": 0 } try: response = requests.post(endpoint, headers=headers, json=payload) response.raise_for_status() # Raises HTTPError for bad responses # Log the API response for debugging logger.debug(f"OpenAI API response: {response.text}") assistant_message = response.json()['choices'][0]['message']['content'].strip() logger.debug(f"Assistant message: {assistant_message}") # Extract JSON from the assistant's message json_str = re.search(r'\{.*\}', assistant_message, re.DOTALL) if json_str: analyzed_data = json.loads(json_str.group()) else: logger.error("No JSON object found in the response.") return None return analyzed_data except requests.exceptions.RequestException as e: logger.error(f"HTTP Request failed: {e}") return None except json.JSONDecodeError as e: logger.error(f"JSON decoding failed: {e}") return None except Exception as e: logger.error(f"Unexpected error: {e}") return None def generate_content(persona_data, prompt): endpoint = f"{XAI_API_BASE}/chat/completions" headers = { "Content-Type": "application/json", "Authorization": f"Bearer {XAI_API_KEY}" } # Format the persona data into a readable string characteristics = '\n'.join([ f"{key.replace('_', ' ').capitalize()}: {value}" for key, value in persona_data.items() if value is not None and key not in ['id', 'name'] ]) decoding_prompt = f''' You are to write a response in the style of {persona_data.get('name', 'Unknown Author')}, a writer with the following characteristics: {characteristics} Now, please write a response in this style about the following topic: "{prompt}" Begin with a compelling title that reflects the content of the post. ''' payload = { "messages": [ {"role": "system", "content": "You are an assistant that generates blog posts."}, {"role": "user", "content": decoding_prompt} ], "model": "grok-beta", "stream": False, "temperature": 0 } try: response = requests.post(endpoint, headers=headers, json=payload) response.raise_for_status() assistant_message = response.json()['choices'][0]['message']['content'].strip() logger.debug(f"Assistant message: {assistant_message}") return assistant_message except requests.exceptions.RequestException as e: logger.error(f"HTTP Request failed: {e}") return '' except json.JSONDecodeError as e: logger.error(f"JSON decoding failed: {e}") return '' except Exception as e: logger.error(f"Unexpected error: {e}") return '' def save_blog_post(blog_post, title): # Implement if needed pass from django.urls import path from .views import ( AnalyzeWritingSampleView, GenerateContentView, PersonaListView, PersonaDetailView, BlogPostView ) urlpatterns = [ path('analyze/', AnalyzeWritingSampleView.as_view(), name='analyze-writing-sample'), path('generate-content/', GenerateContentView.as_view(), name='generate-content'), path('personas/', PersonaListView.as_view(), name='persona-list'), path('personas/<int:persona_id>/', PersonaDetailView.as_view(), name='persona-detail'), path('blog-posts/', BlogPostView.as_view(), name='blogpost-list'), ] from rest_framework import serializers from .models import Persona, BlogPost from .utils import analyze_writing_sample import logging logger = logging.getLogger(__name__) class PersonaSerializer(serializers.ModelSerializer): writing_sample = serializers.CharField(write_only=True) class Meta: model = Persona fields = ['id', 'name', 'writing_sample', 'data'] read_only_fields = ['id', 'data'] def create(self, validated_data): writing_sample = validated_data.pop('writing_sample') logger.debug(f"Writing sample received: {writing_sample[:100]}...") # Log first 100 characters # Analyze the writing sample analyzed_data = analyze_writing_sample(writing_sample) logger.debug(f"Analyzed data: {analyzed_data}") if not analyzed_data: logger.error("analyze_writing_sample returned empty or invalid data.") raise serializers.ValidationError({"writing_sample": "Failed to analyze the writing sample."}) # Store the analyzed data validated_data['data'] = analyzed_data # Save persona return Persona.objects.create(**validated_data) class BlogPostSerializer(serializers.ModelSerializer): persona = serializers.StringRelatedField() # Displays persona name class Meta: model = BlogPost fields = ['id', 'persona', 'title', 'content', 'created_at'] from django.db import models class Persona(models.Model): name = models.CharField(max_length=100, blank=True, null=True) data = models.JSONField(blank=True, null=True) # Stores analyzed writing sample data def __str__(self): return self.name class BlogPost(models.Model): id = models.BigAutoField(primary_key=True) persona = models.ForeignKey(Persona, on_delete=models.CASCADE, related_name='blog_posts') title = models.CharField(max_length=200, blank=True, null=True) content = models.TextField() created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.title or f"BlogPost {self.id}" """ Django settings for backend project. Generated by 'django-admin startproject' using Django 5.1.2. For more information on this file, see https://docs.djangoproject.com/en/5.1/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.1/ref/settings/ """ # Ensure you have python-decouple installed: # pip install python-decouple import decouple from decouple import config from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'django-insecure-afq-u-)(^1od2wb_(4oke^ky00%p-0$+o%e6bfw0nucj30$0$r' XAI_API_KEY = config('XAI_API_KEY') # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'core', 'corsheaders', ] MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'backend.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'backend.wsgi.application' # Database # https://docs.djangoproject.com/en/5.1/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } # Password validation # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/5.1/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.1/howto/static-files/ STATIC_URL = 'static/' # Default primary key field type # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' CORS_ALLOWED_ORIGINS = [ 'http://localhost:3000', 'http://localhost:3001', ] from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('api/', include('core.urls')), # Prefix API URLs with /api/ ] import React from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import UploadSample from './components/UploadSample'; import PersonaList from './components/PersonaList'; import GenerateContent from './components/GenerateContent'; import BlogPosts from './components/BlogPosts'; import NavBar from './components/NavBar'; const App: React.FC = () => { return ( <Router> <NavBar /> <div style={{ padding: '20px' }}> <Routes> <Route path="/" element={<UploadSample />} /> <Route path="/personas" element={<PersonaList />} /> <Route path="/generate" element={<GenerateContent />} /> <Route path="/blog-posts" element={<BlogPosts />} /> </Routes> </div> </Router> ); }; export default App; import axios from 'axios'; const instance = axios.create({ baseURL: 'http://localhost:8000/api/', // Adjust the baseURL if needed }); export default instance; // src/index.tsx import React from 'react'; import ReactDOM from 'react-dom/client'; // Updated for React 18 import './index.css'; import App from './App'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( <React.StrictMode> <App /> </React.StrictMode> ); import React, { useEffect, useState } from 'react'; import axios from '../axiosConfig'; // Adjust the path if necessary import { CircularProgress, Typography, Box, Card, CardContent } from '@mui/material'; interface BlogPost { id: number; persona: string; title: string; content: string; created_at: string; } const BlogPosts: React.FC = () => { const [blogPosts, setBlogPosts] = useState<BlogPost[]>([]); const [loading, setLoading] = useState<boolean>(true); const [error, setError] = useState<string | null>(null); useEffect(() => { const fetchBlogPosts = async () => { try { const response = await axios.get('blog-posts/'); setBlogPosts(response.data); } catch (err) { console.error('Error fetching blog posts:', err); setError('Failed to load blog posts.'); } finally { setLoading(false); } }; fetchBlogPosts(); }, []); if (loading) { return ( <Box display="flex" justifyContent="center" alignItems="center" height="100vh"> <CircularProgress /> </Box> ); } if (error) { return ( <Box display="flex" justifyContent="center" alignItems="center" height="100vh"> <Typography variant="h6" color="error"> {error} </Typography> </Box> ); } return ( <Box p={4}> <Typography variant="h4" gutterBottom> Output </Typography> {blogPosts.length === 0 ? ( <Typography variant="body1">No blog posts found.</Typography> ) : ( blogPosts.map((post) => ( <Card key={post.id} variant="outlined" sx={{ mb: 2 }}> <CardContent> <Typography variant="h5" gutterBottom> {post.title || 'Untitled'} </Typography> <Typography variant="body2" paragraph> {post.content} </Typography> <Typography variant="caption" color="text.secondary"> By: {post.persona} on {new Date(post.created_at).toLocaleString()} </Typography> </CardContent> </Card> )) )} </Box> ); }; export default BlogPosts; import React, { useState } from 'react'; import axios from '../axiosConfig'; // Adjust the path if necessary import { useSearchParams } from 'react-router-dom'; import { Box, Button, TextField, Typography, Alert, CircularProgress, Card, CardContent } from '@mui/material'; interface BlogPost { id: number; persona: string; title: string; content: string; created_at: string; } const GenerateContent: React.FC = () => { const [searchParams] = useSearchParams(); const personaIdParam = searchParams.get('personaId'); const personaId = personaIdParam ? Number(personaIdParam) : null; const [prompt, setPrompt] = useState<string>(''); const [content, setContent] = useState<BlogPost | null>(null); const [loading, setLoading] = useState<boolean>(false); const [error, setError] = useState<string | null>(null); const handleGenerate = async () => { if (!prompt) { setError('Please enter a prompt.'); return; } if (!personaId) { setError('Invalid Persona ID.'); return; } setLoading(true); setError(null); try { const response = await axios.post('generate-content/', { persona_id: personaId, prompt: prompt, }); setContent(response.data); setError(null); setPrompt(''); } catch (err: any) { console.error('Error generating content:', err); if (err.response && err.response.data) { setError(JSON.stringify(err.response.data)); } else { setError('Failed to generate content.'); } } finally { setLoading(false); } }; return ( <Box p={4} maxWidth="600px" mx="auto"> <Typography variant="h4" gutterBottom> Generate Content </Typography> {error && ( <Alert severity="error" sx={{ mb: 2 }}> {error} </Alert> )} <TextField label="Prompt" variant="outlined" fullWidth multiline rows={4} value={prompt} onChange={(e) => setPrompt(e.target.value)} placeholder="Enter a topic or prompt..." required sx={{ mb: 3 }} /> <Button onClick={handleGenerate} variant="contained" color="primary" disabled={loading} fullWidth > {loading ? <CircularProgress size={24} /> : 'Generate Content'} </Button> {content && ( <Card variant="outlined" sx={{ mt: 4 }}> <CardContent> <Typography variant="h5" gutterBottom> {content.title || 'Untitled'} </Typography> <Typography variant="body1"> {content.content} </Typography> </CardContent> </Card> )} </Box> ); }; export default GenerateContent; import { AppBar, Toolbar, Tabs, Tab, Box } from '@mui/material'; import { Link, useLocation, LinkProps } from 'react-router-dom'; import { styled } from '@mui/system'; import { TabProps } from '@mui/material/Tab'; // Import TabProps const NavBar: React.FC = () => { const location = useLocation(); const currentPath = location.pathname; const StyledTab = styled(Tab)<TabProps & LinkProps>(({ theme }) => ({ // Extend with LinkProps color: '#ffffff', fontWeight: 'bold', textTransform: 'none', '&.Mui-selected': { color: '#ffffff', backgroundColor: '#333333', borderRadius: theme.spacing(0.5), }, '&:hover': { color: '#aaaaaa', }, })); const StyledAppBar = styled(AppBar)({ backgroundColor: '#000000', boxShadow: 'none', }); return ( <StyledAppBar position="static"> <Toolbar> <Box sx={{ flexGrow: 1 }}> <Tabs value={currentPath} TabIndicatorProps={{ style: { backgroundColor: '#ffffff' } }}> <StyledTab label="Upload Sample" value="/" component={Link} to="/" /> <StyledTab label="Personas" value="/personas" component={Link} to="/personas" /> <StyledTab label="Blog Posts" value="/blog-posts" component={Link} to="/blog-posts" /> </Tabs> </Box> </Toolbar> </StyledAppBar> ); }; export default NavBar; /* General Container Styling */ .persona-list-container { max-width: 800px; margin: 0 auto; padding: 20px; background-color: #f9f9f9; border-radius: 10px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } /* Title Styling */ .title { text-align: center; font-size: 2rem; margin-bottom: 20px; color: #333; } /* Loading and Error Messages */ .loading, .error, .no-personas { text-align: center; font-size: 1.2rem; color: #666; } /* Persona Card Container */ .persona-cards { display: flex; flex-wrap: wrap; gap: 20px; justify-content: center; } /* Persona Card Styling */ .persona-card { background-color: #fff; border: 1px solid #ddd; border-radius: 8px; padding: 20px; width: calc(33.33% - 20px); min-width: 200px; text-align: center; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); transition: transform 0.2s, box-shadow 0.2s; } .persona-card:hover { transform: translateY(-5px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } /* Persona Name Styling */ .persona-name { font-size: 1.5rem; margin-bottom: 15px; color: #444; } /* Button Styling */ .generate-button { background-color: #007bff; color: #fff; border: none; padding: 10px 15px; font-size: 1rem; border-radius: 5px; cursor: pointer; transition: background-color 0.2s; } .generate-button:hover { background-color: #0056b3; } import React, { useEffect, useState } from 'react'; import axios from '../axiosConfig'; // Adjust the path if necessary import { useNavigate } from 'react-router-dom'; import './PersonaList.css'; // Import the CSS file for styling interface Persona { id: number; name: string; data: Record<string, any>; } const PersonaList: React.FC = () => { const [personas, setPersonas] = useState<Persona[]>([]); const [loading, setLoading] = useState<boolean>(true); const [error, setError] = useState<string | null>(null); const navigate = useNavigate(); useEffect(() => { const fetchPersonas = async () => { try { const response = await axios.get('personas/'); setPersonas(response.data); } catch (err) { console.error('Error fetching personas:', err); setError('Failed to load personas.'); } finally { setLoading(false); } }; fetchPersonas(); }, []); const handleSelectPersona = (personaId: number) => { navigate(`/generate?personaId=${personaId}`); }; if (loading) return <div className="loading">Loading...</div>; if (error) return <div className="error">{error}</div>; return ( <div className="persona-list-container"> <h2 className="title">Saved Personas</h2> {personas.length === 0 ? ( <p className="no-personas">No personas found.</p> ) : ( <div className="persona-cards"> {personas.map((persona) => ( <div key={persona.id} className="persona-card"> <h3 className="persona-name">{persona.name}</h3> <button className="generate-button" onClick={() => handleSelectPersona(persona.id)} > Generate Content </button> </div> ))} </div> )} </div> ); }; export default PersonaList; import React, { useState } from 'react'; import axios from '../axiosConfig'; // Adjust the path if necessary import { Box, Button, TextField, Typography, Alert, Stack } from '@mui/material'; const UploadSample: React.FC = () => { const [name, setName] = useState(''); const [writingSample, setWritingSample] = useState(''); const [error, setError] = useState<string | null>(null); const [success, setSuccess] = useState<string | null>(null); const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); const payload = { name: name.trim(), writing_sample: writingSample.trim(), }; try { console.log('Payload being sent:', payload); const response = await axios.post('analyze/', payload); console.log('Response received:', response.data); setSuccess(`Persona "${response.data.name}" created successfully!`); setError(null); setName(''); setWritingSample(''); } catch (error: any) { console.error('Error uploading writing sample:', error); console.log('Error response:', error.response); if (error.response && error.response.data) { setError(JSON.stringify(error.response.data)); } else { setError('An error occurred while uploading the writing sample.'); } setSuccess(null); } }; return ( <Box p={4} maxWidth="600px" mx="auto"> <Typography variant="h4" gutterBottom> Upload Writing Sample </Typography> {error && ( <Alert severity="error" sx={{ mb: 2 }}> {error} </Alert> )} {success && ( <Alert severity="success" sx={{ mb: 2 }}> {success} </Alert> )} <form onSubmit={handleSubmit}> <Stack spacing={3}> <TextField label="Persona Name" variant="outlined" fullWidth value={name} onChange={(e) => setName(e.target.value)} required inputProps={{ maxLength: 100 }} /> <TextField label="Writing Sample" variant="outlined" fullWidth multiline rows={6} value={writingSample} onChange={(e) => setWritingSample(e.target.value)} required /> <Button type="submit" variant="contained" color="primary" size="large"> Submit </Button> </Stack> </form> </Box> ); }; export default UploadSample;
Sure! Below are the updated code snippets where we've replaced XAI's API with Anthropic's API. We've also ensured that the code uses the most up-to-date model (`claude-2`). Here are the changes: --- ### **Updated `utils.py`** ```python import logging import requests import json import re from decouple import config logger = logging.getLogger(__name__) ANTHROPIC_API_KEY = config('ANTHROPIC_API_KEY') ANTHROPIC_API_BASE = "https://api.anthropic.com" def analyze_writing_sample(writing_sample): endpoint = f"{ANTHROPIC_API_BASE}/v1/complete" headers = { "Content-Type": "application/json", "X-API-Key": ANTHROPIC_API_KEY } # Build the prompt with the required instructions and writing sample instructions = f''' Please analyze the writing style and personality of the given writing sample. Provide a detailed assessment of their characteristics using the following template. Rate each applicable characteristic on a scale of 1-10 where relevant, or provide a descriptive value. Return the results in a JSON format. "name": "[Author/Character Name]", "vocabulary_complexity": [1-10], "sentence_structure": "[simple/complex/varied]", "paragraph_organization": "[structured/loose/stream-of-consciousness]", "idiom_usage": [1-10], "metaphor_frequency": [1-10], "simile_frequency": [1-10], "tone": "[formal/informal/academic/conversational/etc.]", "punctuation_style": "[minimal/heavy/unconventional]", "contraction_usage": [1-10], "pronoun_preference": "[first-person/third-person/etc.]", "passive_voice_frequency": [1-10], "rhetorical_question_usage": [1-10], "list_usage_tendency": [1-10], "personal_anecdote_inclusion": [1-10], "pop_culture_reference_frequency": [1-10], "technical_jargon_usage": [1-10], "parenthetical_aside_frequency": [1-10], "humor_sarcasm_usage": [1-10], "emotional_expressiveness": [1-10], "emphatic_device_usage": [1-10], "quotation_frequency": [1-10], "analogy_usage": [1-10], "sensory_detail_inclusion": [1-10], "onomatopoeia_usage": [1-10], "alliteration_frequency": [1-10], "word_length_preference": "[short/long/varied]", "foreign_phrase_usage": [1-10], "rhetorical_device_usage": [1-10], "statistical_data_usage": [1-10], "personal_opinion_inclusion": [1-10], "transition_usage": [1-10], "reader_question_frequency": [1-10], "imperative_sentence_usage": [1-10], "dialogue_inclusion": [1-10], "regional_dialect_usage": [1-10], "hedging_language_frequency": [1-10], "language_abstraction": "[concrete/abstract/mixed]", "personal_belief_inclusion": [1-10], "repetition_usage": [1-10], "subordinate_clause_frequency": [1-10], "verb_type_preference": "[active/stative/mixed]", "sensory_imagery_usage": [1-10], "symbolism_usage": [1-10], "digression_frequency": [1-10], "formality_level": [1-10], "reflection_inclusion": [1-10], "irony_usage": [1-10], "neologism_frequency": [1-10], "ellipsis_usage": [1-10], "cultural_reference_inclusion": [1-10], "stream_of_consciousness_usage": [1-10], "openness_to_experience": [1-10], "conscientiousness": [1-10], "extraversion": [1-10], "agreeableness": [1-10], "emotional_stability": [1-10], "dominant_motivations": "[achievement/affiliation/power/etc.]", "core_values": "[integrity/freedom/knowledge/etc.]", "decision_making_style": "[analytical/intuitive/spontaneous/etc.]", "empathy_level": [1-10], "self_confidence": [1-10], "risk_taking_tendency": [1-10], "idealism_vs_realism": "[idealistic/realistic/mixed]", "conflict_resolution_style": "[assertive/collaborative/avoidant/etc.]", "relationship_orientation": "[independent/communal/mixed]", "emotional_response_tendency": "[calm/reactive/intense]", "creativity_level": [1-10], "age": "[age or age range]", "gender": "[gender]", "education_level": "[highest level of education]", "professional_background": "[brief description]", "cultural_background": "[brief description]", "primary_language": "[language]", "language_fluency": "[native/fluent/intermediate/beginner]", "background": "[A brief paragraph describing the author's context, major influences, and any other relevant information not captured above]" Writing Sample: {writing_sample} ''' # Prepare the prompt for Anthropic API prompt = f"Human: {instructions.strip()}\n\nAssistant:" payload = { "prompt": prompt, "model": "claude-2", "max_tokens_to_sample": 1000, "temperature": 0, "stop_sequences": ["\n\nHuman:"] } try: response = requests.post(endpoint, headers=headers, json=payload) response.raise_for_status() # Raises HTTPError for bad responses # Log the API response for debugging logger.debug(f"Anthropic API response: {response.text}") assistant_message = response.json()['completion'].strip() logger.debug(f"Assistant message: {assistant_message}") # Extract JSON from the assistant's message json_str = re.search(r'\{.*\}', assistant_message, re.DOTALL) if json_str: analyzed_data = json.loads(json_str.group()) else: logger.error("No JSON object found in the response.") return None return analyzed_data except requests.exceptions.RequestException as e: logger.error(f"HTTP Request failed: {e}") return None except json.JSONDecodeError as e: logger.error(f"JSON decoding failed: {e}") return None except Exception as e: logger.error(f"Unexpected error: {e}") return None def generate_content(persona_data, prompt_text): endpoint = f"{ANTHROPIC_API_BASE}/v1/complete" headers = { "Content-Type": "application/json", "X-API-Key": ANTHROPIC_API_KEY } # Format the persona data into a readable string characteristics = '\n'.join([ f"{key.replace('_', ' ').capitalize()}: {value}" for key, value in persona_data.items() if value is not None and key not in ['id', 'name'] ]) decoding_prompt = f''' You are to write a response in the style of {persona_data.get('name', 'Unknown Author')}, a writer with the following characteristics: {characteristics} Now, please write a response in this style about the following topic: "{prompt_text}" Begin with a compelling title that reflects the content of the post. ''' # Prepare the prompt for Anthropic API prompt = f"Human: {decoding_prompt.strip()}\n\nAssistant:" payload = { "prompt": prompt, "model": "claude-2", "max_tokens_to_sample": 1000, "temperature": 0, "stop_sequences": ["\n\nHuman:"] } try: response = requests.post(endpoint, headers=headers, json=payload) response.raise_for_status() assistant_message = response.json()['completion'].strip() logger.debug(f"Assistant message: {assistant_message}") return assistant_message except requests.exceptions.RequestException as e: logger.error(f"HTTP Request failed: {e}") return '' except json.JSONDecodeError as e: logger.error(f"JSON decoding failed: {e}") return '' except Exception as e: logger.error(f"Unexpected error: {e}") return '' ``` --- ### **Updated `settings.py`** ```python # Ensure you have python-decouple installed: # pip install python-decouple import decouple from decouple import config # ... # Replace XAI_API_KEY with ANTHROPIC_API_KEY ANTHROPIC_API_KEY = config('ANTHROPIC_API_KEY') # ... ``` --- ### **Updates in `serializers.py` (if any references to XAI)** No changes are needed in `serializers.py` as it uses the `analyze_writing_sample` function from `utils.py`, which we've already updated. --- ### **Updates in `views.py` (if any references to XAI)** No changes are needed in `views.py` as it imports `generate_content` from `utils.py`, which we've already updated. --- ### **Updates in Other Files** Ensure that in your environment variables or `.env` file, you have replaced `XAI_API_KEY` with `ANTHROPIC_API_KEY`. --- ### **Explanation of Changes** - **API Endpoint Update**: Changed the API endpoint from XAI's to Anthropic's by updating `XAI_API_BASE` to `ANTHROPIC_API_BASE` and adjusting the endpoint paths accordingly. - **Authentication Header**: Replaced `"Authorization": f"Bearer {XAI_API_KEY}"` with `"X-API-Key": ANTHROPIC_API_KEY` to match Anthropic's authentication method. - **Payload Structure**: Adjusted the payload to fit Anthropic's API requirements. Instead of using `messages`, we now construct a single `prompt` string that simulates a conversation between `Human:` and `Assistant:`. - **Model Update**: Updated the model from `"grok-beta"` to `"claude-2"` to use Anthropic's latest model. - **Prompt Formatting**: Wrapped instructions and content in `Human:` and `Assistant:` format as required by Anthropic's API. - **Response Parsing**: Updated the response parsing to extract the assistant's completion from `response.json()['completion']`. - **Error Handling**: Adjusted error handling to account for changes in the response structure. --- ### **Environment Variable** Make sure to set your Anthropic API key in your environment variables or `.env` file: ``` ANTHROPIC_API_KEY=your_anthropic_api_key_here ``` --- ### **Additional Notes** - **Max Tokens**: Adjust `max_tokens_to_sample` in the payload if you expect longer responses. - **Stop Sequences**: Added `stop_sequences` with `["\n\nHuman:"]` to ensure the assistant stops generating when it sees a new `Human:` prompt. - **Temperature**: Kept `temperature` at `0` for deterministic outputs. Adjust as needed. --- By making these changes, your application should now use Anthropic's API instead of XAI's API while maintaining the desired functionality.
o1-preview
Teflon Content Script injected contentscript.bundle.js:15045 Logger init chunk-NUMECXU6.js?v=06cd9abe:21551 Download the React DevTools for a better development experience: https://reactjs.org/link/react-devtools page_all.js:2 init command html page_all.js:2 init command http://localhost:5173/ page_all.js:2 init command clearCart page_all.js:2 init command http://localhost:5173/ page_all.js:2 init command getOrdersListProduct page_all.js:2 init command http://localhost:5173/ page_all.js:2 init command getTrackDetals page_all.js:2 init command http://localhost:5173/ page_all.js:2 init command getOrderDetail page_all.js:2 init command http://localhost:5173/ page_all.js:2 init command getReview page_all.js:2 init command http://localhost:5173/ page_all.js:2 init command clearAddres page_all.js:2 init command http://localhost:5173/ page_all.js:2 init command countAddress page_all.js:2 init command http://localhost:5173/ page_all.js:2 add class expansion-alids-init @mui_material.js?v=66a6302e:42898 MUI: The `value` provided to the Tabs component is invalid. None of the Tabs' children match with "/login". You can provide one of the following values: /, /personas, /blog-posts. getTabsMeta @ @mui_material.js?v=66a6302e:42898 (anonymous) @ @mui_material.js?v=66a6302e:42921 (anonymous) @ chunk-KI44QKTA.js?v=06cd9abe:6234 (anonymous) @ @mui_material.js?v=66a6302e:43133 commitHookEffectListMount @ chunk-NUMECXU6.js?v=06cd9abe:16915 commitPassiveMountOnFiber @ chunk-NUMECXU6.js?v=06cd9abe:18156 commitPassiveMountEffects_complete @ chunk-NUMECXU6.js?v=06cd9abe:18129 commitPassiveMountEffects_begin @ chunk-NUMECXU6.js?v=06cd9abe:18119 commitPassiveMountEffects @ chunk-NUMECXU6.js?v=06cd9abe:18109 flushPassiveEffectsImpl @ chunk-NUMECXU6.js?v=06cd9abe:19490 flushPassiveEffects @ chunk-NUMECXU6.js?v=06cd9abe:19447 performSyncWorkOnRoot @ chunk-NUMECXU6.js?v=06cd9abe:18868 flushSyncCallbacks @ chunk-NUMECXU6.js?v=06cd9abe:9119 (anonymous) @ chunk-NUMECXU6.js?v=06cd9abe:18627 Show 14 more frames Show lessUnderstand this error @mui_material.js?v=66a6302e:42898 MUI: The `value` provided to the Tabs component is invalid. None of the Tabs' children match with "/login". You can provide one of the following values: /, /personas, /blog-posts. getTabsMeta @ @mui_material.js?v=66a6302e:42898 (anonymous) @ @mui_material.js?v=66a6302e:43040 (anonymous) @ chunk-KI44QKTA.js?v=06cd9abe:6234 (anonymous) @ @mui_material.js?v=66a6302e:43136 commitHookEffectListMount @ chunk-NUMECXU6.js?v=06cd9abe:16915 commitPassiveMountOnFiber @ chunk-NUMECXU6.js?v=06cd9abe:18156 commitPassiveMountEffects_complete @ chunk-NUMECXU6.js?v=06cd9abe:18129 commitPassiveMountEffects_begin @ chunk-NUMECXU6.js?v=06cd9abe:18119 commitPassiveMountEffects @ chunk-NUMECXU6.js?v=06cd9abe:18109 flushPassiveEffectsImpl @ chunk-NUMECXU6.js?v=06cd9abe:19490 flushPassiveEffects @ chunk-NUMECXU6.js?v=06cd9abe:19447 performSyncWorkOnRoot @ chunk-NUMECXU6.js?v=06cd9abe:18868 flushSyncCallbacks @ chunk-NUMECXU6.js?v=06cd9abe:9119 (anonymous) @ chunk-NUMECXU6.js?v=06cd9abe:18627 Show 14 more frames Show lessUnderstand this error @mui_material.js?v=66a6302e:42898 MUI: The `value` provided to the Tabs component is invalid. None of the Tabs' children match with "/login". You can provide one of the following values: /, /personas, /blog-posts. getTabsMeta @ @mui_material.js?v=66a6302e:42898 (anonymous) @ @mui_material.js?v=66a6302e:42921 (anonymous) @ chunk-KI44QKTA.js?v=06cd9abe:6234 (anonymous) @ @mui_material.js?v=66a6302e:43133 commitHookEffectListMount @ chunk-NUMECXU6.js?v=06cd9abe:16915 invokePassiveEffectMountInDEV @ chunk-NUMECXU6.js?v=06cd9abe:18324 invokeEffectsInDev @ chunk-NUMECXU6.js?v=06cd9abe:19701 commitDoubleInvokeEffectsInDEV @ chunk-NUMECXU6.js?v=06cd9abe:19686 flushPassiveEffectsImpl @ chunk-NUMECXU6.js?v=06cd9abe:19503 flushPassiveEffects @ chunk-NUMECXU6.js?v=06cd9abe:19447 performSyncWorkOnRoot @ chunk-NUMECXU6.js?v=06cd9abe:18868 flushSyncCallbacks @ chunk-NUMECXU6.js?v=06cd9abe:9119 (anonymous) @ chunk-NUMECXU6.js?v=06cd9abe:18627 Show 13 more frames Show lessUnderstand this error @mui_material.js?v=66a6302e:42898 MUI: The `value` provided to the Tabs component is invalid. None of the Tabs' children match with "/login". You can provide one of the following values: /, /personas, /blog-posts. getTabsMeta @ @mui_material.js?v=66a6302e:42898 (anonymous) @ @mui_material.js?v=66a6302e:43040 (anonymous) @ chunk-KI44QKTA.js?v=06cd9abe:6234 (anonymous) @ @mui_material.js?v=66a6302e:43136 commitHookEffectListMount @ chunk-NUMECXU6.js?v=06cd9abe:16915 invokePassiveEffectMountInDEV @ chunk-NUMECXU6.js?v=06cd9abe:18324 invokeEffectsInDev @ chunk-NUMECXU6.js?v=06cd9abe:19701 commitDoubleInvokeEffectsInDEV @ chunk-NUMECXU6.js?v=06cd9abe:19686 flushPassiveEffectsImpl @ chunk-NUMECXU6.js?v=06cd9abe:19503 flushPassiveEffects @ chunk-NUMECXU6.js?v=06cd9abe:19447 performSyncWorkOnRoot @ chunk-NUMECXU6.js?v=06cd9abe:18868 flushSyncCallbacks @ chunk-NUMECXU6.js?v=06cd9abe:9119 (anonymous) @ chunk-NUMECXU6.js?v=06cd9abe:18627 Show 13 more frames Show lessUnderstand this error @mui_material.js?v=66a6302e:42898 MUI: The `value` provided to the Tabs component is invalid. None of the Tabs' children match with "/login". You can provide one of the following values: /, /personas, /blog-posts. getTabsMeta @ @mui_material.js?v=66a6302e:42898 (anonymous) @ @mui_material.js?v=66a6302e:42921 (anonymous) @ chunk-KI44QKTA.js?v=06cd9abe:6234 (anonymous) @ @mui_material.js?v=66a6302e:43133 commitHookEffectListMount @ chunk-NUMECXU6.js?v=06cd9abe:16915 commitPassiveMountOnFiber @ chunk-NUMECXU6.js?v=06cd9abe:18156 commitPassiveMountEffects_complete @ chunk-NUMECXU6.js?v=06cd9abe:18129 commitPassiveMountEffects_begin @ chunk-NUMECXU6.js?v=06cd9abe:18119 commitPassiveMountEffects @ chunk-NUMECXU6.js?v=06cd9abe:18109 flushPassiveEffectsImpl @ chunk-NUMECXU6.js?v=06cd9abe:19490 flushPassiveEffects @ chunk-NUMECXU6.js?v=06cd9abe:19447 (anonymous) @ chunk-NUMECXU6.js?v=06cd9abe:19328 workLoop @ chunk-NUMECXU6.js?v=06cd9abe:197 flushWork @ chunk-NUMECXU6.js?v=06cd9abe:176 performWorkUntilDeadline @ chunk-NUMECXU6.js?v=06cd9abe:384 Show 15 more frames Show lessUnderstand this error @mui_material.js?v=66a6302e:42898 MUI: The `value` provided to the Tabs component is invalid. None of the Tabs' children match with "/login". You can provide one of the following values: /, /personas, /blog-posts. getTabsMeta @ @mui_material.js?v=66a6302e:42898 (anonymous) @ @mui_material.js?v=66a6302e:43040 (anonymous) @ chunk-KI44QKTA.js?v=06cd9abe:6234 (anonymous) @ @mui_material.js?v=66a6302e:43136 commitHookEffectListMount @ chunk-NUMECXU6.js?v=06cd9abe:16915 commitPassiveMountOnFiber @ chunk-NUMECXU6.js?v=06cd9abe:18156 commitPassiveMountEffects_complete @ chunk-NUMECXU6.js?v=06cd9abe:18129 commitPassiveMountEffects_begin @ chunk-NUMECXU6.js?v=06cd9abe:18119 commitPassiveMountEffects @ chunk-NUMECXU6.js?v=06cd9abe:18109 flushPassiveEffectsImpl @ chunk-NUMECXU6.js?v=06cd9abe:19490 flushPassiveEffects @ chunk-NUMECXU6.js?v=06cd9abe:19447 (anonymous) @ chunk-NUMECXU6.js?v=06cd9abe:19328 workLoop @ chunk-NUMECXU6.js?v=06cd9abe:197 flushWork @ chunk-NUMECXU6.js?v=06cd9abe:176 performWorkUntilDeadline @ chunk-NUMECXU6.js?v=06cd9abe:384 Show 15 more frames Show lessUnderstand this error @mui_material.js?v=66a6302e:42898 MUI: The `value` provided to the Tabs component is invalid. None of the Tabs' children match with "/login". You can provide one of the following values: /, /personas, /blog-posts. getTabsMeta @ @mui_material.js?v=66a6302e:42898 (anonymous) @ @mui_material.js?v=66a6302e:42921 (anonymous) @ chunk-KI44QKTA.js?v=06cd9abe:6234 (anonymous) @ @mui_material.js?v=66a6302e:43064 later @ chunk-KI44QKTA.js?v=06cd9abe:6068 setTimeout debounced @ chunk-KI44QKTA.js?v=06cd9abe:6071 Show 6 more frames Show lessUnderstand this error UploadSample.tsx:22 Payload being sent: {name: 'Karamazov 3', writing_sample: 'Alexey Fyodorovitch Karamazov was the third son of…ebated everywhere at the time—the position of the'} UploadSample.tsx:23 POST http://localhost:8000/api/personas/ 400 (Bad Request) dispatchXhrRequest @ axios.js?v=a3e43c70:1680 xhr @ axios.js?v=a3e43c70:1560 dispatchRequest @ axios.js?v=a3e43c70:2035 Promise.then _request @ axios.js?v=a3e43c70:2222 request @ axios.js?v=a3e43c70:2141 httpMethod @ axios.js?v=a3e43c70:2269 wrap @ axios.js?v=a3e43c70:8 handleSubmit @ UploadSample.tsx:23 callCallback2 @ chunk-NUMECXU6.js?v=06cd9abe:3674 invokeGuardedCallbackDev @ chunk-NUMECXU6.js?v=06cd9abe:3699 invokeGuardedCallback @ chunk-NUMECXU6.js?v=06cd9abe:3733 invokeGuardedCallbackAndCatchFirstError @ chunk-NUMECXU6.js?v=06cd9abe:3736 executeDispatch @ chunk-NUMECXU6.js?v=06cd9abe:7014 processDispatchQueueItemsInOrder @ chunk-NUMECXU6.js?v=06cd9abe:7034 processDispatchQueue @ chunk-NUMECXU6.js?v=06cd9abe:7043 dispatchEventsForPlugins @ chunk-NUMECXU6.js?v=06cd9abe:7051 (anonymous) @ chunk-NUMECXU6.js?v=06cd9abe:7174 batchedUpdates$1 @ chunk-NUMECXU6.js?v=06cd9abe:18913 batchedUpdates @ chunk-NUMECXU6.js?v=06cd9abe:3579 dispatchEventForPluginEventSystem @ chunk-NUMECXU6.js?v=06cd9abe:7173 dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay @ chunk-NUMECXU6.js?v=06cd9abe:5478 dispatchEvent @ chunk-NUMECXU6.js?v=06cd9abe:5472 dispatchDiscreteEvent @ chunk-NUMECXU6.js?v=06cd9abe:5449 Show 22 more frames Show lessUnderstand this error UploadSample.tsx:30 Error uploading writing sample: AxiosError {message: 'Request failed with status code 400', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …} handleSubmit @ UploadSample.tsx:30 await in handleSubmit callCallback2 @ chunk-NUMECXU6.js?v=06cd9abe:3674 invokeGuardedCallbackDev @ chunk-NUMECXU6.js?v=06cd9abe:3699 invokeGuardedCallback @ chunk-NUMECXU6.js?v=06cd9abe:3733 invokeGuardedCallbackAndCatchFirstError @ chunk-NUMECXU6.js?v=06cd9abe:3736 executeDispatch @ chunk-NUMECXU6.js?v=06cd9abe:7014 processDispatchQueueItemsInOrder @ chunk-NUMECXU6.js?v=06cd9abe:7034 processDispatchQueue @ chunk-NUMECXU6.js?v=06cd9abe:7043 dispatchEventsForPlugins @ chunk-NUMECXU6.js?v=06cd9abe:7051 (anonymous) @ chunk-NUMECXU6.js?v=06cd9abe:7174 batchedUpdates$1 @ chunk-NUMECXU6.js?v=06cd9abe:18913 batchedUpdates @ chunk-NUMECXU6.js?v=06cd9abe:3579 dispatchEventForPluginEventSystem @ chunk-NUMECXU6.js?v=06cd9abe:7173 dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay @ chunk-NUMECXU6.js?v=06cd9abe:5478 dispatchEvent @ chunk-NUMECXU6.js?v=06cd9abe:5472 dispatchDiscreteEvent @ chunk-NUMECXU6.js?v=06cd9abe:5449 Show 15 more frames Show lessUnderstand this error UploadSample.tsx:31 Error response: {data: {…}, status: 400, statusText: 'Bad Request', headers: AxiosHeaders, config: {…}, …}October 25, 2024 - 17:03:18 Django version 5.1.2, using settings 'backend.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C. [25/Oct/2024 17:04:13] "POST /api/token/ HTTP/1.1" 200 483 HTTP Request failed: 400 Client Error: Bad Request for url: https://api.anthropic.com/v1/complete Failed to analyze writing sample. Bad Request: /api/personas/ [25/Oct/2024 17:04:29] "POST /api/personas/ HTTP/1.1" 400 58
Based on the error logs you've provided, it seems that when you submit a writing sample to be analyzed, the server encounters a `400 Bad Request` error from Anthropic's API: ``` HTTP Request failed: 400 Client Error: Bad Request for url: https://api.anthropic.com/v1/complete Failed to analyze writing sample. ``` This indicates that the request sent to Anthropic's API is malformed or contains invalid parameters. Let's work through the possible causes and solutions step by step. --- ## **1. Verify the API Key** **Issue**: An incorrect or invalid API key can cause a `400 Bad Request` error. **Solution**: - **Check the Environment Variable**: Ensure that the `ANTHROPIC_API_KEY` environment variable is set correctly in your environment or `.env` file. ```env ANTHROPIC_API_KEY=your_actual_anthropic_api_key ``` - **Restart the Server**: After setting the environment variable, restart your Django server to make sure it picks up the new value. - **Test the API Key**: You can test your API key using a simple `curl` command or Postman to make sure it's valid. --- ## **2. Add Detailed Logging for the API Response** **Issue**: Without the exact error message from Anthropic's API, it's challenging to diagnose the problem. **Solution**: Modify your `utils.py` functions to log the response content when a request fails. ### **Updated `utils.py`** ```python import logging import requests import json import re from decouple import config logger = logging.getLogger(__name__) ANTHROPIC_API_KEY = config('ANTHROPIC_API_KEY') ANTHROPIC_API_BASE = "https://api.anthropic.com" def analyze_writing_sample(writing_sample): endpoint = f"{ANTHROPIC_API_BASE}/v1/complete" headers = { "Content-Type": "application/json", "X-API-Key": ANTHROPIC_API_KEY } # ... [rest of your code remains the same] try: response = requests.post(endpoint, headers=headers, json=payload) response.raise_for_status() # Raises HTTPError for bad responses # Log the API response for debugging logger.debug(f"Anthropic API response: {response.text}") # ... [rest of your code remains the same] except requests.exceptions.HTTPError as e: logger.error(f"HTTP Error: {e}") logger.error(f"Response content: {response.text}") # Log the response content return None except requests.exceptions.RequestException as e: logger.error(f"Request Exception: {e}") return None except json.JSONDecodeError as e: logger.error(f"JSON decoding failed: {e}") return None except Exception as e: logger.error(f"Unexpected error: {e}") return None # Similarly update the generate_content function to log response content on errors def generate_content(persona_data, prompt_text): # ... [rest of your code remains the same] try: response = requests.post(endpoint, headers=headers, json=payload) response.raise_for_status() assistant_message = response.json()['completion'].strip() logger.debug(f"Assistant message: {assistant_message}") return assistant_message except requests.exceptions.HTTPError as e: logger.error(f"HTTP Error: {e}") logger.error(f"Response content: {response.text}") # Log the response content return '' except requests.exceptions.RequestException as e: logger.error(f"Request Exception: {e}") return '' except json.JSONDecodeError as e: logger.error(f"JSON decoding failed: {e}") return '' except Exception as e: logger.error(f"Unexpected error: {e}") return '' ``` **Explanation**: - **Log Response Content**: When an `HTTPError` occurs, we log the `response.text` to capture the error message returned by Anthropic's API. - **Separate Exception Handling**: Differentiate between `HTTPError` and other `RequestException` to provide more specific error information. --- ## **3. Verify Payload Structure and Parameters** **Issue**: The payload sent to Anthropic's API might not match their expected format. **Solution**: - **Review Anthropic's API Documentation**: Ensure that the payload parameters match exactly what the API expects. - **Check Model Name**: Confirm that `"model": "claude-2"` is a valid model name you have access to. Some users might need to specify the full model name like `"claude-2"` or `"claude-instant-1"` depending on their account privileges. - **Adjust Payload**: Here's the updated payload with potential corrections: ```python payload = { "prompt": prompt, "model": "claude-2", "max_tokens_to_sample": 1000, "temperature": 0, "stop_sequences": ["\n\nHuman:"], "stream": False # Add if streaming is not desired } ``` - **Ensure Correct Data Types**: - `temperature` should be a float between 0 and 1. - `max_tokens_to_sample` should be an integer within allowed limits. - **Remove Unnecessary Parameters**: If the API does not expect certain parameters, remove them. --- ## **4. Test the API Call Independently** **Issue**: There might be issues specific to your application code. **Solution**: - **Use `curl` or Postman**: Make a direct API call to Anthropic's endpoint using the same payload and headers to see if it works outside your application. ```bash curl https://api.anthropic.com/v1/complete \ -H 'Content-Type: application/json' \ -H 'X-API-Key: your_anthropic_api_key' \ -d '{ "prompt": "Human: Hello\n\nAssistant:", "model": "claude-2", "max_tokens_to_sample": 100, "temperature": 0 }' ``` - **Check the Response**: If you receive a successful response, the issue might be in your application code. If not, the API key or payload might be incorrect. --- ## **5. Ensure Prompt Formatting** **Issue**: Anthropic's API is sensitive to prompt formatting, especially the `Human:` and `Assistant:` tokens. **Solution**: - **Clean Up the Prompt**: Ensure there are no extra spaces or newlines that could affect the parsing. - **Example Prompt**: ```python prompt = f"Human: {instructions.strip()}\n\nAssistant:" ``` Ensure that `instructions` does not contain unintended leading or trailing whitespace. --- ## **6. Update Exception Handling to Provide More Insights** **Issue**: The current exception handling may swallow important error information. **Solution**: Modify your exception handling to capture and log more details. ```python except requests.exceptions.HTTPError as e: logger.error(f"HTTP Error: {e}") logger.error(f"Status Code: {response.status_code}") logger.error(f"Response Content: {response.content.decode()}") return None ``` --- ## **7. Possible Issues with Model Access** **Issue**: Your API key might not have access to the specified model. **Solution**: - **Verify Model Availability**: Check if your Anthropic account has access to the `claude-2` model. - **Try a Different Model**: If unsure, try using a model like `claude-instant-1` which might have broader availability. ```python payload = { "prompt": prompt, "model": "claude-instant-1", "max_tokens_to_sample": 1000, "temperature": 0, "stop_sequences": ["\n\nHuman:"] } ``` --- ## **8. Confirm API Endpoint and Version** **Issue**: There might be an issue with the API endpoint URL. **Solution**: - **Double-Check Endpoint**: Ensure that the endpoint is correct: `https://api.anthropic.com/v1/complete` - **Check for Updates**: Verify if Anthropic has updated their API endpoints or parameters since you last checked. --- ## **9. Sample Code to Test the API Separately** Create a simple Python script to test the API call: ```python import requests import os ANTHROPIC_API_KEY = os.getenv('ANTHROPIC_API_KEY') ANTHROPIC_API_BASE = "https://api.anthropic.com" endpoint = f"{ANTHROPIC_API_BASE}/v1/complete" headers = { "Content-Type": "application/json", "X-API-Key": ANTHROPIC_API_KEY } prompt = "Human: Hello\n\nAssistant:" payload = { "prompt": prompt, "model": "claude-2", "max_tokens_to_sample": 100, "temperature": 0 } response = requests.post(endpoint, headers=headers, json=payload) print("Status Code:", response.status_code) print("Response Content:", response.text) ``` Run this script to see if the API responds correctly. --- ## **10. Check Rate Limits and Quotas** **Issue**: You might have exceeded your API usage limits. **Solution**: - **Check Account Usage**: Log into your Anthropic account dashboard to check your usage and ensure you haven't exceeded any limits. - **Handle Rate Limiting**: If you're making multiple rapid requests, consider adding rate-limiting logic. --- ## **Summary** By following these steps, you should be able to identify and resolve the issue causing the `400 Bad Request` error when communicating with Anthropic's API. The key actions are: - Verify your API key and ensure it's correctly set. - Add detailed logging to capture the response content from Anthropic's API. - Ensure that the payload sent matches the API's expected format. - Test the API call independently to isolate the issue. - Adjust model names or parameters as necessary. --- If you continue to face issues after these steps, please provide the specific error message returned in the response content from Anthropic's API (make sure to redact any sensitive information). This will help in diagnosing the problem more precisely.
o1-preview