Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from streamlit_webrtc import webrtc_streamer, VideoProcessorBase, RTCConfiguration
|
3 |
+
import av
|
4 |
+
import cv2
|
5 |
+
from pyzbar import pyzbar
|
6 |
+
import google.generativeai as genai
|
7 |
+
from google.generativeai import protos
|
8 |
+
import threading
|
9 |
+
import time
|
10 |
+
import json
|
11 |
+
import re
|
12 |
+
from google import genai
|
13 |
+
from google.genai import types
|
14 |
+
# ----------------------------
|
15 |
+
# App Configuration
|
16 |
+
# ----------------------------
|
17 |
+
st.set_page_config(
|
18 |
+
page_title="Real-Time Drug Code Verifier",
|
19 |
+
page_icon="π",
|
20 |
+
layout="wide"
|
21 |
+
)
|
22 |
+
|
23 |
+
# ----------------------------
|
24 |
+
# API Key Setup
|
25 |
+
# ----------------------------
|
26 |
+
# It's recommended to use st.secrets for API keys in a deployed app
|
27 |
+
GOOGLE_API_KEY = "AIzaSyAXFL0YdFIKUNISOQuV9FH-JTSnMC3PSMA"
|
28 |
+
|
29 |
+
# Load key from secrets
|
30 |
+
try:
|
31 |
+
if not GOOGLE_API_KEY:
|
32 |
+
GOOGLE_API_KEY = st.secrets["GOOGLE_API_KEY"]
|
33 |
+
except (FileNotFoundError, KeyError):
|
34 |
+
st.error("π GOOGLE_API_KEY not found. Please add it to your Streamlit secrets.")
|
35 |
+
st.stop()
|
36 |
+
|
37 |
+
#.configure(api_key=GOOGLE_API_KEY)
|
38 |
+
client = genai.Client(api_key=GOOGLE_API_KEY)
|
39 |
+
# Define the grounding tool
|
40 |
+
grounding_tool = types.Tool(
|
41 |
+
google_search=types.GoogleSearch()
|
42 |
+
)
|
43 |
+
|
44 |
+
# Configure generation settings
|
45 |
+
config = types.GenerateContentConfig(
|
46 |
+
tools=[grounding_tool]
|
47 |
+
)
|
48 |
+
|
49 |
+
|
50 |
+
# ----------------------------
|
51 |
+
# Main Gemini Verification Function
|
52 |
+
# ----------------------------
|
53 |
+
@st.cache_data(show_spinner=False)
|
54 |
+
def verify_code_with_gemini(code: str, barcode_type: str):
|
55 |
+
"""
|
56 |
+
Verifies a product code by letting Gemini use its native Google Search tool.
|
57 |
+
"""
|
58 |
+
# Define the built-in Google Search tool using the specific syntax required by the library
|
59 |
+
|
60 |
+
|
61 |
+
prompt = f"""
|
62 |
+
You are a product verification assistant. A user has scanned a product with the code '{code}' (type: {barcode_type}).
|
63 |
+
Your goal is to use your built-in Google Search tool to augment your own knowledge and provide a comprehensive analysis.
|
64 |
+
|
65 |
+
Follow these steps:
|
66 |
+
1. Use your search tool to find information about the provided barcode and product type. This will give you real-time context.
|
67 |
+
2. Combine the information from the web search with your own general knowledge about products, manufacturers, and barcode standards.
|
68 |
+
3. Provide a detailed summary. This should include the likely product name, manufacturer, and a well-reasoned assessment of the code's authenticity. If the search results are inconclusive or suspicious, use your own knowledge to explain why (e.g., "This barcode format is unusual for this type of product," or "No major retailers list this code, which is suspicious.").
|
69 |
+
4. From the search results, find the best, most representative image URL for the product. If no good image is found, return an empty string.
|
70 |
+
5. IMPORTANT: Your final output MUST be ONLY the raw JSON object. Do not include ```json``` markers or any other explanatory text.
|
71 |
+
|
72 |
+
Example Output:
|
73 |
+
{{
|
74 |
+
"summary_text": "This EAN-13 code corresponds to 'Product Name' by 'Manufacturer'. The code format is valid and listed by several major retailers in the search results, suggesting it is authentic. No recalls or warnings were found.",
|
75 |
+
"image_url": "https://example.com/product_image.jpg"
|
76 |
+
}}
|
77 |
+
"""
|
78 |
+
response = None # Initialize response to None
|
79 |
+
try:
|
80 |
+
st.write("π§ Generating content with Gemini and Google Search...")
|
81 |
+
response = client.models.generate_content(
|
82 |
+
model="gemini-2.5-flash",
|
83 |
+
contents=prompt,
|
84 |
+
config=config,
|
85 |
+
)
|
86 |
+
st.write("β
Gemini response received. Parsing JSON...")
|
87 |
+
# Extract JSON from the response text, which might be wrapped in markdown
|
88 |
+
raw_text = response.text
|
89 |
+
json_match = re.search(r'\{.*\}', raw_text, re.DOTALL)
|
90 |
+
|
91 |
+
if json_match:
|
92 |
+
json_str = json_match.group(0)
|
93 |
+
result_data = json.loads(json_str)
|
94 |
+
st.write("π JSON parsed successfully!")
|
95 |
+
return result_data
|
96 |
+
else:
|
97 |
+
st.write("β No JSON object found in the response.")
|
98 |
+
return {
|
99 |
+
"summary_text": "Verification failed: The AI did not return a valid JSON object.",
|
100 |
+
"image_url": "",
|
101 |
+
"raw_response": raw_text
|
102 |
+
}
|
103 |
+
|
104 |
+
except Exception as e:
|
105 |
+
st.write(f"β An error occurred: {e}")
|
106 |
+
raw_response_text = response.text if response else "No response from model."
|
107 |
+
return {
|
108 |
+
"summary_text": f"An error occurred during verification. Please check the details below.",
|
109 |
+
"image_url": "",
|
110 |
+
"error_details": str(e),
|
111 |
+
"raw_response": raw_response_text
|
112 |
+
}
|
113 |
+
|
114 |
+
# ----------------------------
|
115 |
+
# Video Processing Class
|
116 |
+
# ----------------------------
|
117 |
+
class QRCodeScanner(VideoProcessorBase):
|
118 |
+
def __init__(self):
|
119 |
+
self.result = None
|
120 |
+
self.lock = threading.Lock()
|
121 |
+
|
122 |
+
def recv(self, frame: av.VideoFrame) -> av.VideoFrame:
|
123 |
+
image = frame.to_ndarray(format="bgr24")
|
124 |
+
decoded_objects = pyzbar.decode(image)
|
125 |
+
|
126 |
+
for obj in decoded_objects:
|
127 |
+
code_data = obj.data.decode("utf-8")
|
128 |
+
code_type = obj.type
|
129 |
+
|
130 |
+
with self.lock:
|
131 |
+
self.result = {"data": code_data, "type": code_type}
|
132 |
+
|
133 |
+
(x, y, w, h) = obj.rect
|
134 |
+
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
|
135 |
+
text = f"Detected: {code_data}"
|
136 |
+
cv2.putText(image, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
|
137 |
+
break
|
138 |
+
|
139 |
+
return av.VideoFrame.from_ndarray(image, format="bgr24")
|
140 |
+
|
141 |
+
# ----------------------------
|
142 |
+
# Streamlit UI
|
143 |
+
# ----------------------------
|
144 |
+
st.title("π Real-Time Drug Code Verifier with AI Search")
|
145 |
+
st.markdown("Point a product's barcode or QR code at the camera. The app will use Gemini AI with Google Search to verify its authenticity.")
|
146 |
+
|
147 |
+
if "code_found" not in st.session_state:
|
148 |
+
st.session_state.update({"code_found": False, "last_code_data": "", "last_code_type": ""})
|
149 |
+
|
150 |
+
if not st.session_state["code_found"]:
|
151 |
+
st.subheader("π· Camera Feed")
|
152 |
+
webrtc_ctx = webrtc_streamer(
|
153 |
+
key="scanner",
|
154 |
+
video_processor_factory=QRCodeScanner,
|
155 |
+
rtc_configuration=RTCConfiguration({"iceServers": [{"urls": ["stun:stun.l.google.com:19302"]}]}),
|
156 |
+
media_stream_constraints={"video": True, "audio": False},
|
157 |
+
async_processing=True,
|
158 |
+
)
|
159 |
+
st.info("The camera will stop automatically once a code is detected.")
|
160 |
+
|
161 |
+
if webrtc_ctx.video_processor:
|
162 |
+
while True:
|
163 |
+
with webrtc_ctx.video_processor.lock:
|
164 |
+
result = webrtc_ctx.video_processor.result
|
165 |
+
if result:
|
166 |
+
st.session_state.update(code_found=True, last_code_data=result["data"], last_code_type=result["type"])
|
167 |
+
st.rerun()
|
168 |
+
time.sleep(0.2)
|
169 |
+
else:
|
170 |
+
st.success("β
Code captured successfully! The camera has been turned off.")
|
171 |
+
|
172 |
+
col1, col2 = st.columns([1, 2])
|
173 |
+
|
174 |
+
with col1:
|
175 |
+
st.subheader("π¦ Detected Code")
|
176 |
+
st.code(f"Data: {st.session_state['last_code_data']}\nType: {st.session_state['last_code_type']}", language="text")
|
177 |
+
|
178 |
+
if st.button("π Scan Another Product"):
|
179 |
+
st.session_state.update({"code_found": False, "last_code_data": "", "last_code_type": ""})
|
180 |
+
st.rerun()
|
181 |
+
|
182 |
+
with col2:
|
183 |
+
st.subheader("π€ Gemini AI Verification")
|
184 |
+
with st.spinner("π Using Gemini with Google Search to verify..."):
|
185 |
+
verification_result = verify_code_with_gemini(
|
186 |
+
st.session_state['last_code_data'],
|
187 |
+
st.session_state['last_code_type']
|
188 |
+
)
|
189 |
+
|
190 |
+
# Display the summary text
|
191 |
+
st.info(verification_result.get("summary_text", "No summary available."))
|
192 |
+
|
193 |
+
# Display the image if a URL was found
|
194 |
+
image_url = verification_result.get("image_url")
|
195 |
+
if image_url:
|
196 |
+
st.image(image_url, caption="Product Image (from web search)")
|
197 |
+
|
198 |
+
# Display error details if they exist
|
199 |
+
if "error_details" in verification_result:
|
200 |
+
st.error("An error occurred during verification:")
|
201 |
+
st.code(f"Error: {verification_result['error_details']}\n\nRaw AI Response:\n{verification_result['raw_response']}", language="text")
|