Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions anthropic_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import anthropic
import json
import os
import configparser
from requests.exceptions import RequestException, Timeout
import uuid

def guid_generator():
return str(uuid.uuid4())

TIMEOUT_SECONDS = 60

home_dir = os.path.expanduser("~")
bundle_dir = os.path.join(home_dir, "Library", "Application Support", "CopyCat")
models_path = os.path.join(bundle_dir, "models.json")

class AnthropicAPI:
def __init__(self, api_key, config_file=None):
self.api_key = api_key
self.client = anthropic.Anthropic(api_key=api_key)
self.config_file = config_file

def generate_response(self, messages, model, max_tokens=None, temperature=0.8):
"""
Generate a response using Anthropic's Claude models.

Args:
messages: List of message objects with role and content
model: The Claude model to use
max_tokens: Maximum number of tokens to generate
temperature: Temperature for response generation

Returns:
Response text, prompt tokens, completion tokens, and total tokens
"""
try:
# Convert messages to Anthropic format
system_prompt = None
anthropic_messages = []

for message in messages:
if message["role"] == "system":
system_prompt = message["content"]
else:
anthropic_messages.append({
"role": message["role"],
"content": message["content"]
})

# If no max_tokens specified, use a default
if not max_tokens:
with open(models_path, "r") as f:
models = json.load(f)
if model in models:
max_tokens = int(models[model]["token_size"] * 0.9) # 90% of max
else:
max_tokens = 4000 # Default fallback

# Create the message for Claude
response = self.client.messages.create(
model=model,
messages=anthropic_messages,
system=system_prompt,
max_tokens=max_tokens,
temperature=temperature
)

# Extract response content
response_text = response.content[0].text

# Get token usage
prompt_tokens = response.usage.input_tokens
completion_tokens = response.usage.output_tokens
total_tokens = prompt_tokens + completion_tokens

return response_text, prompt_tokens, completion_tokens, total_tokens

except Exception as e:
print(f"Anthropic API Error: {str(e)}")
raise e

def calculate_anthropic_cost(prompt_tokens, completion_tokens, model=None):
"""Calculate the cost of an Anthropic API request."""
with open(models_path, "r") as f:
models = json.load(f)

if model in models:
input_price_per_token = models[model]["input_price_per_1k_tokens"] / 1000
output_price_per_token = models[model]["output_price_per_1k_tokens"] / 1000
total_price = (prompt_tokens * input_price_per_token) + (completion_tokens * output_price_per_token)
return total_price
else:
return 0
1 change: 1 addition & 0 deletions config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ user = COPYCAT
mem_on_off = True
codemode = False
include_urls = True
provider = OpenAI
6 changes: 4 additions & 2 deletions copycat.py
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,9 @@ def prompt_user(clip, img=False):
window.refresh()
break
model = values["-MODEL-"]
CONFIG["OpenAI"]["model"] = model
provider = provider_models.get(model, "openai")
CONFIG[provider]["model"] = model
CONFIG["GUI"]["provider"] = provider

# window["-MODEL-"].update(values["-MODEL-"])
include_urls = values["-URLS-"]
Expand Down Expand Up @@ -906,4 +908,4 @@ def main(PROMPT, SKIP, prompt_user):
5000,
use_fade_in=False,
location=(0, 0),
)
)
154 changes: 127 additions & 27 deletions extract.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import sys, os
import re
import requests
import configparser
from PIL import Image
from base64 import b64decode, b64encode
from PIL import ImageGrab, Image
from bs4 import BeautifulSoup
import openai
import anthropic
import google.generativeai as genai

home_dir = os.path.expanduser("~")
bundle_dir = os.path.join(home_dir, "Library", "Application Support", "CopyCat")
config_path = os.path.join(bundle_dir, "config.ini")

def get_config():
config = configparser.ConfigParser(strict=False, interpolation=None)
config.read(config_path)
return config


def image_to_base64(image_path):
Expand All @@ -15,39 +27,127 @@ def image_to_base64(image_path):

def caption_image(base64_image):
"""
Generate a caption for an image using the OpenAI GPT-4 Vision model.
Generate a caption for an image using the selected AI model.

Parameters:
image_base64 (str): The base64-encoded string of the image to caption.

Returns:
str: The caption generated by the GPT-4 Vision model.
str: The caption generated by the AI model.
"""

# Replace 'YOUR_OPENAI_API_KEY' with your actual OpenAI API key

# Prepare the message payload with the system prompt and the image
messages = [
{"role": "system", "content": "Generate a caption for the following image."},
{
"role": "user",
"content": [
{"type": "text", "text": "What is shown in this image?"},
config = get_config()
provider = config.get("GUI", "provider", fallback="OpenAI")

if provider == "OpenAI":
# Use OpenAI for image captioning
openai.api_key = config.get("OpenAI", "api_key")
model = config.get("OpenAI", "model", fallback="gpt-4o")

# Prepare the message payload with the system prompt and the image
messages = [
{"role": "system", "content": "Generate a caption for the following image."},
{
"role": "user",
"content": [
{"type": "text", "text": "What is shown in this image?"},
{
"type": "image_url",
"image_url": f"data:image/jpeg;base64,{base64_image}",
},
],
},
]

# Make the API call using the selected model
response = openai.ChatCompletion.create(
model=model,
messages=messages,
max_tokens=100, # Adjust max_tokens if needed
)

return response.choices[0].message["content"].strip()

elif provider == "Anthropic":
# Use Anthropic for image captioning
api_key = config.get("Anthropic", "api_key")
model = config.get("Anthropic", "model", fallback="claude-3-haiku-20240307")

client = anthropic.Anthropic(api_key=api_key)

# Create the message for Claude
response = client.messages.create(
model=model,
max_tokens=100,
messages=[
{
"type": "image_url",
"image_url": f"data:image/jpeg;base64,{base64_image}",
},
],
},
]
# Make the API call using the gpt-4-vision-preview model
response = openai.ChatCompletion.create(
model="gpt-4o",
messages=messages,
max_tokens=100, # Adjust max_tokens if needed
)

return response.choices[0].message["content"].strip()
"role": "user",
"content": [
{
"type": "text",
"text": "Generate a caption for the following image. What is shown in this image?"
},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": base64_image
}
}
]
}
]
)

return response.content[0].text

elif provider == "Google":
# Use Google for image captioning
api_key = config.get("Google", "api_key")
model = config.get("Google", "model", fallback="gemini-pro")

genai.configure(api_key=api_key)

# Initialize the model
gemini_model = genai.GenerativeModel(model_name=model)

# Create the prompt
prompt = "Generate a caption for the following image. What is shown in this image?"

# Generate response with image
response = gemini_model.generate_content(
[prompt, {"mime_type": "image/jpeg", "data": base64_image.encode('utf-8')}]
)

return response.text

else:
# Default to OpenAI if provider not recognized
openai.api_key = config.get("OpenAI", "api_key")

# Prepare the message payload with the system prompt and the image
messages = [
{"role": "system", "content": "Generate a caption for the following image."},
{
"role": "user",
"content": [
{"type": "text", "text": "What is shown in this image?"},
{
"type": "image_url",
"image_url": f"data:image/jpeg;base64,{base64_image}",
},
],
},
]

# Make the API call using gpt-4o
response = openai.ChatCompletion.create(
model="gpt-4o",
messages=messages,
max_tokens=100, # Adjust max_tokens if needed
)

return response.choices[0].message["content"].strip()


# Example usage:
Expand Down Expand Up @@ -270,4 +370,4 @@ def isTwitterLink(url):
return False
return url.startswith("https://www.twitter.com") or url.startswith(
"https://twitter.com"
)
)
Loading