mgbam commited on
Commit
1687ea3
·
verified ·
1 Parent(s): f60d626

Rename file_processing.py to services.py

Browse files
Files changed (2) hide show
  1. file_processing.py +0 -90
  2. services.py +109 -0
file_processing.py DELETED
@@ -1,90 +0,0 @@
1
- import os
2
- import mimetypes
3
- import PyPDF2
4
- import docx
5
- import cv2
6
- import numpy as np
7
- from PIL import Image
8
- import pytesseract
9
-
10
- def process_image_for_model(image):
11
- """Convert image to base64 for model input"""
12
- if image is None:
13
- return None
14
-
15
- # Convert numpy array to PIL Image if needed
16
- import io
17
- import base64
18
-
19
- # Handle numpy array from Gradio
20
- if isinstance(image, np.ndarray):
21
- image = Image.fromarray(image)
22
-
23
- buffer = io.BytesIO()
24
- image.save(buffer, format='PNG')
25
- img_str = base64.b64encode(buffer.getvalue()).decode()
26
- return f"data:image/png;base64,{img_str}"
27
-
28
- def extract_text_from_image(image_path):
29
- """Extract text from image using OCR"""
30
- try:
31
- # Check if tesseract is available
32
- try:
33
- pytesseract.get_tesseract_version()
34
- except Exception:
35
- return "Error: Tesseract OCR is not installed. Please install Tesseract to extract text from images. See install_tesseract.md for instructions."
36
-
37
- image = cv2.imread(image_path)
38
- if image is None:
39
- return "Error: Could not read image file"
40
-
41
- image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
42
- gray = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2GRAY)
43
- _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
44
- text = pytesseract.image_to_string(binary, config='--psm 6')
45
- return text.strip() if text.strip() else "No text found in image"
46
-
47
- except Exception as e:
48
- return f"Error extracting text from image: {e}"
49
-
50
- def extract_text_from_file(file_path):
51
- if not file_path:
52
- return ""
53
- ext = os.path.splitext(file_path)[1].lower()
54
- try:
55
- if ext == ".pdf":
56
- with open(file_path, "rb") as f:
57
- reader = PyPDF2.PdfReader(f)
58
- return "\n".join(page.extract_text() or "" for page in reader.pages)
59
- elif ext in [".txt", ".md", ".csv"]:
60
- with open(file_path, "r", encoding="utf-8") as f:
61
- return f.read()
62
- elif ext == ".docx":
63
- doc = docx.Document(file_path)
64
- return "\n".join([para.text for para in doc.paragraphs])
65
- elif ext.lower() in [".jpg", ".jpeg", ".png", ".bmp", ".tiff", ".tif", ".gif", ".webp"]:
66
- return extract_text_from_image(file_path)
67
- else:
68
- return ""
69
- except Exception as e:
70
- return f"Error extracting text: {e}"
71
-
72
- def create_multimodal_message(text, image=None):
73
- """Create a multimodal message with text and optional image"""
74
- if image is None:
75
- return {"role": "user", "content": text}
76
-
77
- content = [
78
- {
79
- "type": "text",
80
- "text": text
81
- },
82
- {
83
- "type": "image_url",
84
- "image_url": {
85
- "url": process_image_for_model(image)
86
- }
87
- }
88
- ]
89
-
90
- return {"role": "user", "content": content}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
services.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # /services.py
2
+
3
+ """
4
+ Manages interactions with external services like LLM providers and web search APIs.
5
+
6
+ This module uses a class-based approach to encapsulate API clients and their
7
+ logic, making it easy to manage connections and mock services for testing.
8
+ """
9
+ import os
10
+ import logging
11
+ from typing import Dict, Any, Generator, List
12
+
13
+ from dotenv import load_dotenv
14
+ from huggingface_hub import InferenceClient
15
+ from tavily import TavilyClient
16
+
17
+ # --- Setup Logging ---
18
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
19
+
20
+ # --- Load Environment Variables ---
21
+ load_dotenv()
22
+ HF_TOKEN = os.getenv("HF_TOKEN")
23
+ TAVILY_API_KEY = os.getenv("TAVILY_API_KEY")
24
+
25
+ if not HF_TOKEN:
26
+ raise ValueError("HF_TOKEN environment variable is not set. Please get a token from https://huggingface.co/settings/tokens")
27
+
28
+ # --- Type Definitions ---
29
+ Messages = List[Dict[str, Any]]
30
+
31
+ class LLMService:
32
+ """A wrapper for the Hugging Face Inference API."""
33
+ def __init__(self, api_key: str = HF_TOKEN):
34
+ if not api_key:
35
+ raise ValueError("Hugging Face API key is required.")
36
+ self.api_key = api_key
37
+
38
+ def get_client(self, model_id: str, provider: str = "auto") -> InferenceClient:
39
+ """Initializes and returns an InferenceClient."""
40
+ return InferenceClient(provider=provider, api_key=self.api_key, bill_to="huggingface")
41
+
42
+ def generate_code_stream(
43
+ self, model_id: str, messages: Messages, provider: str = "auto", max_tokens: int = 10000
44
+ ) -> Generator[str, None, None]:
45
+ """
46
+ Streams code generation from the specified model.
47
+ Yields content chunks as they are received.
48
+ """
49
+ client = self.get_client(model_id, provider)
50
+ try:
51
+ stream = client.chat.completions.create(
52
+ model=model_id,
53
+ messages=messages,
54
+ stream=True,
55
+ max_tokens=max_tokens,
56
+ )
57
+ for chunk in stream:
58
+ if chunk.choices and chunk.choices[0].delta.content:
59
+ yield chunk.choices[0].delta.content
60
+ except Exception as e:
61
+ logging.error(f"LLM API Error for model {model_id}: {e}")
62
+ yield f"Error: Could not get a response from the model. Details: {str(e)}"
63
+ # Re-raise or handle as appropriate for your application flow
64
+ # For this app, we yield an error message to the user.
65
+
66
+
67
+ class SearchService:
68
+ """A wrapper for the Tavily Search API."""
69
+ def __init__(self, api_key: str = TAVILY_API_KEY):
70
+ if not api_key:
71
+ logging.warning("TAVILY_API_KEY not set. Web search will be disabled.")
72
+ self.client = None
73
+ else:
74
+ try:
75
+ self.client = TavilyClient(api_key=api_key)
76
+ except Exception as e:
77
+ logging.error(f"Failed to initialize Tavily client: {e}")
78
+ self.client = None
79
+
80
+ def is_available(self) -> bool:
81
+ """Checks if the search service is configured and available."""
82
+ return self.client is not None
83
+
84
+ def search(self, query: str, max_results: int = 5) -> str:
85
+ """
86
+ Performs a web search and returns a formatted string of results.
87
+ """
88
+ if not self.is_available():
89
+ return "Web search is not available."
90
+
91
+ try:
92
+ response = self.client.search(
93
+ query,
94
+ search_depth="advanced",
95
+ max_results=min(max(1, max_results), 10)
96
+ )
97
+ results = [
98
+ f"Title: {res.get('title', 'N/A')}\nURL: {res.get('url', 'N/A')}\nContent: {res.get('content', 'N/A')}"
99
+ for res in response.get('results', [])
100
+ ]
101
+ return "Web Search Results:\n\n" + "\n---\n".join(results) if results else "No search results found."
102
+ except Exception as e:
103
+ logging.error(f"Tavily search error: {e}")
104
+ return f"Search error: {str(e)}"
105
+
106
+ # --- Singleton Instances ---
107
+ # These instances can be imported and used throughout the application.
108
+ llm_service = LLMService()
109
+ search_service = SearchService()