diff --git a/alttexter.py b/alttexter.py index 80636de..62755e2 100644 --- a/alttexter.py +++ b/alttexter.py @@ -2,20 +2,23 @@ import logging import mimetypes import os -import time from typing import List, Optional, Tuple import cairosvg -from langchain import callbacks -from langchain.callbacks.tracers.langchain import wait_for_all_tracers from langchain.output_parsers import PydanticOutputParser from langchain.prompts import ChatPromptTemplate from langchain_core.messages import HumanMessage, SystemMessage from langchain_openai import AzureChatOpenAI, ChatOpenAI -from langsmith import Client +from langfuse.callback import CallbackHandler from schema import AlttexterResponse, ImageAltText +langfuse_handler = CallbackHandler( + public_key=os.getenv('LANGFUSE_PUBLIC_KEY'), + secret_key=os.getenv('LANGFUSE_SECRET_KEY'), + host=os.getenv('LANGFUSE_HOST') +) + def determine_llm() -> ChatOpenAI: """Determine which LLM to use based on environment variable.""" @@ -23,17 +26,16 @@ def determine_llm() -> ChatOpenAI: if model_env == "openai": return ChatOpenAI(verbose=True, temperature=0, - model="gpt-4-vision-preview", - max_tokens=4096) + model="gpt-4o") elif model_env == "openai_azure": return AzureChatOpenAI(verbose=True, temperature=0, openai_api_version="2024-02-15-preview", azure_deployment=os.getenv("AZURE_OPENAI_DEPLOYMENT"), - model="vision-preview", - max_tokens=4096) + model="gpt-4o") else: raise ValueError(f"Unsupported model specified: {model_env}") + def svg_to_png_base64(svg_data): """ Converts SVG data to PNG and returns the base64 encoded PNG image. @@ -48,6 +50,7 @@ def svg_to_png_base64(svg_data): return base64.b64encode(png_data).decode('utf-8') + def alttexter(input_text: str, images: dict, image_urls: List[str]) -> Tuple[List[ImageAltText], Optional[str]]: """ Processes input text and images to generate alt text and title attributes. @@ -122,29 +125,11 @@ def alttexter(input_text: str, images: dict, image_urls: List[str]) -> Tuple[Lis alttexts = None run_url = None - tracing_enabled = os.getenv("LANGCHAIN_TRACING_V2", "").lower() == "true" - if tracing_enabled: - client = Client() - try: - with callbacks.collect_runs() as cb: - alttexts = llm.invoke(messages.format_messages()) - - # Ensure that all tracers complete their execution - wait_for_all_tracers() - - if alttexts: - # Get public URL for run - run_id = cb.traced_runs[0].id - time.sleep(2) - client.share_run(run_id) - run_url = client.read_run_shared_link(run_id) - except Exception as e: - logging.error(f"Error during LLM invocation with tracing: {str(e)}") + if os.getenv("LANGFUSE_TRACING", "False"): + alttexts = llm.invoke(messages.format_messages(), config={"callbacks": [langfuse_handler]}) + run_url = str(langfuse_handler.get_trace_url()) else: - try: - alttexts = llm.invoke(messages.format_messages()) - except Exception as e: - logging.error(f"Error during LLM invocation without tracing: {str(e)}") + alttexts = llm.invoke(messages.format_messages()) if alttexts: try: diff --git a/client-example.py b/client-example.py index 2ee07e6..b49f98c 100755 --- a/client-example.py +++ b/client-example.py @@ -1,13 +1,14 @@ +import argparse +import base64 +import getpass import json +import logging import os import re -import base64 -import logging -import requests -import getpass -import argparse from datetime import datetime +import requests + logging.basicConfig(level=logging.INFO, format='%(levelname)s [%(asctime)s] %(message)s', datefmt='%d-%m-%Y %H:%M:%S') @@ -67,7 +68,7 @@ def log_payload_summary(encoded_images, image_urls): logging.info(f"Image URLs: {image_urls}") -def send_file_to_api(md_content, encoded_images, image_urls, url, token, full_payload): +def send_file_to_api(md_content, encoded_images, image_urls, url, token, full_payload, verify_ssl=True): if full_payload: log_full_payload(md_content, encoded_images, image_urls) else: @@ -86,7 +87,20 @@ def send_file_to_api(md_content, encoded_images, image_urls, url, token, full_pa } logging.info("Sending payload to alttexter...") - response = requests.post(url, headers=headers, data=actual_payload, timeout=120) + try: + response = requests.post(url, headers=headers, data=actual_payload, timeout=120, verify=verify_ssl) + response.raise_for_status() # Raises an HTTPError for bad responses + except requests.exceptions.SSLError as ssl_err: + if verify_ssl: + logging.error(f"SSL Error occurred: {ssl_err}") + raise + else: + logging.warning("SSL verification is disabled. Proceeding with insecure request.") + response = requests.post(url, headers=headers, data=actual_payload, timeout=120, verify=False) + response.raise_for_status() + except requests.exceptions.RequestException as req_err: + logging.error(f"An error occurred while sending the request: {req_err}") + raise timestamp = datetime.now().strftime('%d-%m-%Y %H:%M:%S') logging.info(f"Response received at {timestamp}") @@ -98,6 +112,7 @@ def send_file_to_api(md_content, encoded_images, image_urls, url, token, full_pa parser = argparse.ArgumentParser(description="Send markdown file to alttexter") parser.add_argument("md_file_path", help="Path to file containing markdown formatted text.") parser.add_argument("--full", action="store_true", help="Log the full payload instead of the summary") + parser.add_argument("--no-verify-ssl", action="store_true", help="Disable SSL certificate verification") args = parser.parse_args() @@ -117,5 +132,10 @@ def send_file_to_api(md_content, encoded_images, image_urls, url, token, full_pa local_images, image_urls = extract_images_from_markdown(md_content) encode_local_images(local_images, base_dir) - response = send_file_to_api(md_content, local_images, image_urls, url, token, args.full) - print(response) + verify_ssl = not args.no_verify_ssl + try: + response = send_file_to_api(md_content, local_images, image_urls, url, token, args.full, verify_ssl) + print(response) + except requests.exceptions.RequestException as e: + logging.error(f"Error occurred: {e}") + print(f"Failed to get a response from the server: {e}") diff --git a/docker-compose.yml b/docker-compose.yml index 171d9f4..6a2c56b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,10 +11,10 @@ services: volumes: - ${ALTTEXTER_CERTS_DIR}:/certs environment: - - LANGCHAIN_TRACING_V2 - - LANGCHAIN_ENDPOINT - - LANGCHAIN_PROJECT - - LANGCHAIN_API_KEY + - LANGFUSE_TRACING + - LANGFUSE_PUBLIC_KEY + - LANGFUSE_SECRET_KEY + - LANGFUSE_HOST - ALTTEXTER_MODEL - OPENAI_API_KEY - AZURE_OPENAI_ENDPOINT diff --git a/requirements.txt b/requirements.txt index 634f7cd..ee250ee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,5 @@ pydantic==1.10.12 uvicorn==0.27.1 tiktoken==0.5.2 nbformat==5.9.2 -cairosvg==2.7.0 \ No newline at end of file +cairosvg==2.7.0 +langfuse==2.36.1 \ No newline at end of file